Python-Selenium教程

安装python

打开 Python官网,找到“Download”, 在其下拉菜单中选择自己的平台(Windows/Mac),一般的Linux平台已经自带的Python,所以不需要安装,通过打开“终端” ,输入“python”命令来验证。
如果你是第一次接触Python,一定会迷惑Python为什么会提供Python2.x 和 Python3.x两个版本?那么,直接使用Python3.x的最新版本就好了。
x86 只支持32位的系统; x86-64 支持64位的系统。 web-based 在安装的过程中需要联网;executable 可执行文件(.exe)方式安装;embeddable zip file 嵌入式版本,可以集成到其它应用中。
注意:在安装的过程中需要勾选:“Add Python 3.x to PATH” , 如果没有勾选,需要在安装完成之后,将Python的安装目录(如:C:Python38)添加到环境变量PATH下面。

安装selenium


首先,在Windows命令提示符(cmd)/ Linux终端输入:

C:Usersname>pip
Usage:
  pip <command> [options]
Commands:
  install                     Install packages.
  download                    Download packages.
  uninstall                   Uninstall packages.
  freeze                      Output installed packages in requirements format.
  list                        List installed packages.
  show                        Show information about installed packages.
  check                       Verify installed packages have compatible dependencies.
……

确保pip命令可用,如果提示“pip不是内部或外部命令”,需要将将pip的安装目录(如:C:Python36Scripts)添加到环境变量PATH下面。
接下来通过pip命令安装Selenium:

pip3 install selenium

测试

打开一款Python编辑器,默认Python自带的IDLE也行。创建 baidu.py文件,输入以下内容:

from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
print(driver.title)
driver.quit()

下载浏览器驱动

当selenium升级到3.0之后,对不同的浏览器驱动进行了规范。如果想使用selenium驱动不同的浏览器,必须单独下载并设置不同的浏览器驱动。
各浏览器下载地址:
Firefox浏览器驱动:geckodriver
Chrome浏览器驱动:chromedriver , taobao备用地址
IE浏览器驱动:IEDriverServer
Edge浏览器驱动:MicrosoftWebDriver
Opera浏览器驱动:operadriver
PhantomJS浏览器驱动:phantomjs
注:部分浏览器驱动地址需要科学上网。

设置浏览器驱动

设置浏览器的地址非常简单。 我们可以手动创建一个存放浏览器驱动的目录,如: C:driver , 将下载的浏览器驱动文件(例如:chromedriver、geckodriver)丢到该目录下。
我的电脑–>属性–>系统设置–>高级–>环境变量–>系统变量–>Path,将“C:driver”目录添加到Path的值中。

  • Path
  • C:driver

检测浏览器驱动

验证不同的浏览器驱动是否正常使用。

from selenium import webdriver
driver = webdriver.Firefox()   # Firefox浏览器
driver = webdriver.Chrome()    # Chrome浏览器
driver = webdriver.Ie()        # Internet Explorer浏览器
driver = webdriver.Edge()      # Edge浏览器
driver = webdriver.Opera()     # Opera浏览器
driver = webdriver.PhantomJS()   # PhantomJS

selenium定位方法

Selenium提供了8种定位方式。

  • id
  • name
  • class name
  • tag name
  • link text
  • partial link text
  • xpath
  • css selector

这8种定位方式在Python selenium中所对应的方法为:

  • find_element_by_id()
  • find_element_by_name()
  • find_element_by_class_name()
  • find_element_by_tag_name()
  • find_element_by_link_text()
  • find_element_by_partial_link_text()
  • find_element_by_xpath()
  • find_element_by_css_selector()

定位方法的用法

假如我们有一个Web页面,通过前端工具(如,Firebug)查看到一个元素的属性是这样的。

<html>
  <head>
  <body link="#0000cc">
    <a id="result_logo" href="/" onmousedown="return c({'fm':'tab','tab':'logo'})">
    <form id="form" class="fm" name="f" action="/s">
      <span class="soutu-btn"></span>
        <input id="kw" class="s_ipt" name="wd" value="" maxlength="255" autocomplete="off">

我们的目的是要定位input标签的输入框。

  • 通过id定位:
dr.find_element_by_id("kw")
  • 通过name定位:
dr.find_element_by_name("wd")
  • 通过class name定位:
dr.find_element_by_class_name("s_ipt")
  • 通过tag name定位:
dr.find_element_by_tag_name("input")
  • 通过xpath定位,xpath定位有N种写法,这里列几个常用写法:
dr.find_element_by_xpath("//*[@id='kw']")
dr.find_element_by_xpath("//*[@name='wd']")
dr.find_element_by_xpath("//input[@class='s_ipt']")
dr.find_element_by_xpath("/html/body/form/span/input")
dr.find_element_by_xpath("//span[@class='soutu-btn']/input")
dr.find_element_by_xpath("//form[@id='form']/span/input")
dr.find_element_by_xpath("//input[@id='kw' and @name='wd']")

通过css定位,css定位有N种写法,这里列几个常用写法:

dr.find_element_by_css_selector("#kw")
dr.find_element_by_css_selector("[name=wd]")
dr.find_element_by_css_selector(".s_ipt")
dr.find_element_by_css_selector("html > body > form > span > input")
dr.find_element_by_css_selector("span.soutu-btn> input#kw")
dr.find_element_by_css_selector("form#form > span > input")

接下来,我们的页面上有一组文本链接。

<a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a>
<a class="mnav" href="http://www.hao123.com" name="tj_trhao123">hao123</a>

通过link text定位:

dr.find_element_by_link_text("新闻")
dr.find_element_by_link_text("hao123")

通过link text定位:

dr.find_element_by_partial_link_text("新")
dr.find_element_by_partial_link_text("hao")
dr.find_element_by_partial_link_text("123")

关于xpaht和css的定位比较复杂,请参考:

xpath语法

XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。

XML 实例文档

我们将在下面的例子中使用这个 XML 文档。

<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>
<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>
</bookstore>

选取节点

XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。

下面列出了最有用的路径表达式:

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

实例

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式 结果
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。
注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。

谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。

实例

在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:

路径表达式 结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。

实例

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
/bookstore/* 选取 bookstore 元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的 title 元素。

选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

实例

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

css选择器

CSS3 选择器

在 CSS 中,选择器是一种模式,用于选择需要添加样式的元素。
“CSS” 列指示该属性是在哪个 CSS 版本中定义的。(CSS1、CSS2 还是 CSS3。)

选择器 例子 例子描述 CSS
.class .intro 选择 class=”intro” 的所有元素。 1
#id #firstname 选择 id=”firstname” 的所有元素。 1
* * 选择所有元素。 2
element p 选择所有 <p> 元素。 1
element,element div,p 选择所有 <div> 元素和所有 <p> 元素。 1
element element div p 选择 <div> 元素内部的所有 <p> 元素。 1
element>element div>p 选择父元素为 <div> 元素的所有 <p> 元素。 2
element+element div+p 选择紧接在 <div> 元素之后的所有 <p> 元素。 2
[attribute] [target] 选择带有 target 属性所有元素。 2
[attribute=value] [target=_blank] 选择 target=”_blank” 的所有元素。 2
[attribute~=value] [title~=flower] 选择 title 属性包含单词 “flower” 的所有元素。 2
[attribute|=value] [lang|=en] 选择 lang 属性值以 “en” 开头的所有元素。 2
:link a:link 选择所有未被访问的链接。 1
:visited a:visited 选择所有已被访问的链接。 1
:active a:active 选择活动链接。 1
:hover a:hover 选择鼠标指针位于其上的链接。 1
:focus input:focus 选择获得焦点的 input 元素。 2
:first-letter p:first-letter 选择每个 <p> 元素的首字母。 1
:first-line p:first-line 选择每个 <p> 元素的首行。 1
:first-child p:first-child 选择属于父元素的第一个子元素的每个 <p> 元素。 2
:before p:before 在每个 <p> 元素的内容之前插入内容。 2
:after p:after 在每个 <p> 元素的内容之后插入内容。 2
:lang(language) p:lang(it) 选择带有以 “it” 开头的 lang 属性值的每个 <p> 元素。 2
element1~element2 p~ul 选择前面有 <p> 元素的每个 <ul> 元素。 3
[attribute^=value] a[src^=”https”] 选择其 src 属性值以 “https” 开头的每个 <a> 元素。 3
[attribute$=value] a[src$=”.pdf”] 选择其 src 属性以 “.pdf” 结尾的所有 <a> 元素。 3
[attribute*=value] a[src*=”abc”] 选择其 src 属性中包含 “abc” 子串的每个 <a> 元素。 3
:first-of-type p:first-of-type 选择属于其父元素的首个 <p> 元素的每个 <p> 元素。 3
:last-of-type p:last-of-type 选择属于其父元素的最后 <p> 元素的每个 <p> 元素。 3
:only-of-type p:only-of-type 选择属于其父元素唯一的 <p> 元素的每个 <p> 元素。 3
:only-child p:only-child 选择属于其父元素的唯一子元素的每个 <p> 元素。 3
:nth-child(n) p:nth-child(2) 选择属于其父元素的第二个子元素的每个 <p> 元素。 3
:nth-last-child(n) p:nth-last-child(2) 同上,从最后一个子元素开始计数。 3
:nth-of-type(n) p:nth-of-type(2) 选择属于其父元素第二个 <p> 元素的每个 <p> 元素。 3
:nth-last-of-type(n) p:nth-last-of-type(2) 同上,但是从最后一个子元素开始计数。 3
:last-child p:last-child 选择属于其父元素最后一个子元素每个 <p> 元素。 3
:root :root 选择文档的根元素。 3
:empty p:empty 选择没有子元素的每个 <p> 元素(包括文本节点)。 3
:target #news:target 选择当前活动的 #news 元素。 3
:enabled input:enabled 选择每个启用的 <input> 元素。 3
:disabled input:disabled 选择每个禁用的 <input> 元素 3
:checked input:checked 选择每个被选中的 <input> 元素。 3
:not(selector) :not(p) 选择非 <p> 元素的每个元素。 3
::selection ::selection 选择被用户选取的元素部分。 3
 

控制浏览器操作

控制浏览器窗口大小

有时候我们希望能以某种浏览器尺寸打开,让访问的页面在这种尺寸下运行。例如可以将浏览器设置成移动端大小(480* 800),然后访问移动站点,对其样式进行评估;WebDriver提供了set_window_size()方法来设置浏览器的大小。

from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://m.baidu.com")
# 参数数字为像素点
print("设置浏览器宽480、高800显示")
driver.set_window_size(480, 800)
driver.quit()

在PC端执行自动化测试脚本大多的情况下是希望浏览器在全屏幕模式下执行,那么可以使用maximize_window()方法使打开的浏览器全屏显示,其用法与set_window_size() 相同,但它不需要参数。

控制浏览器后退、前进


在使用浏览器浏览网页时,浏览器提供了后退和前进按钮,可以方便地在浏览过的网页之间切换,WebDriver也提供了对应的back()和forward()方法来模拟后退和前进按钮。下面通过例子来演示这两个方法的使用。

from selenium import webdriver
driver = webdriver.Firefox()
#访问百度首页
first_url= 'http://www.baidu.com'
print("now access %s" %(first_url))
driver.get(first_url)
#访问新闻页面
second_url='http://news.baidu.com'
print("now access %s" %(second_url))
driver.get(second_url)
#返回(后退)到百度首页
print("back to  %s "%(first_url))
driver.back()
#前进到新闻页
print("forward to  %s"%(second_url))
driver.forward()
driver.quit()

为了看清脚本的执行过程,下面每操作一步都通过print()来打印当前的URL地址。

刷新页面


有时候需要手动刷新(F5) 页面。

driver.refresh() #刷新当前页面

WebDriver常用方法

点击和输入


前面我们已经学习了定位元素, 定位只是第一步, 定位之后需要对这个元素进行操作, 或单击(按钮) 或输入(输入框) , 下面就来认识 WebDriver 中最常用的几个方法:

  • clear(): 清除文本。
  • send_keys (value): 模拟按键输入。
  • click(): 单击元素。
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
driver.find_element_by_id("kw").clear()
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
driver.quit()

提交


  • submit()

submit()方法用于提交表单。 例如, 在搜索框输入关键字之后的“回车” 操作, 就可以通过该方法模拟。

from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
search_text = driver.find_element_by_id('kw')
search_text.send_keys('selenium')
search_text.submit()
driver.quit()

有时候 submit()可以与 click()方法互换来使用, submit()同样可以提交一个按钮, 但 submit()的应用范围远不及 click()广泛。

其他常用方法


  • size: 返回元素的尺寸。
  • text: 获取元素的文本。
  • get_attribute(name): 获得属性值。
  • is_displayed(): 设置该元素是否用户可见。
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
# 获得输入框的尺寸
size = driver.find_element_by_id('kw').size
print(size)
# 返回百度页面底部备案信息
text = driver.find_element_by_id("cp").text
print(text)
# 返回元素的属性值, 可以是 id、 name、 type 或其他任意属性
attribute = driver.find_element_by_id("kw").get_attribute('type')
print(attribute)
# 返回元素的结果是否可见, 返回结果为 True 或 False
result = driver.find_element_by_id("kw").is_displayed()
print(result)
driver.quit()

输出结果:

{'width': 500, 'height': 22}
©2015 Baidu 使用百度前必读 意见反馈 京 ICP 证 030173 号
text
True

执行上面的程序并查看结果: size 方法用于获取百度输入框的宽、 高, text 方法用于获得百度底部的备案信息, get_attribute()用于获得百度输入的 type 属性的值, is_displayed()用于返回一个元素是否可见, 如果可见则返回 True, 否则返回 False。

鼠标事件

在 WebDriver 中, 将这些关于鼠标操作的方法封装在 ActionChains 类提供。
ActionChains 类提供了鼠标操作的常用方法:

  • perform(): 执行所有 ActionChains 中存储的行为;
  • context_click(): 右击;
  • double_click(): 双击;
  • drag_and_drop(): 拖动;
  • move_to_element(): 鼠标悬停。

鼠标悬停操作


from selenium import webdriver
# 引入 ActionChains 类
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Chrome()
driver.get("https://www.baidu.cn")
# 定位到要悬停的元素
above = driver.find_element_by_link_text("设置")
# 对定位到的元素执行鼠标悬停操作
ActionChains(driver).move_to_element(above).perform()
……
  • from selenium.webdriver import ActionChains

导入提供鼠标操作的 ActionChains 类。

  • ActionChains(driver)

调用 ActionChains()类, 将浏览器驱动 driver 作为参数传入。

  • move_to_element(above)

context_click()方法用于模拟鼠标右键操作, 在调用时需要指定元素定位。

  • perform()

执行所有 ActionChains 中存储的行为, 可以理解成是对整个操作的提交动作。

键盘事件

Keys()类提供了键盘上几乎所有按键的方法。 前面了解到, send_keys()方法可以用来模拟键盘输入, 除此 之外, 我们还可以用它来输入键盘上的按键, 甚至是组合键, 如 Ctrl+A、 Ctrl+C 等。

from selenium import webdriver
# 引入 Keys 模块
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
# 输入框输入内容
driver.find_element_by_id("kw").send_keys("seleniumm")
# 删除多输入的一个 m
driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE)
# 输入空格键+“教程”
driver.find_element_by_id("kw").send_keys(Keys.SPACE)
driver.find_element_by_id("kw").send_keys("教程")
# ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'a')
# ctrl+x 剪切输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'x')
# ctrl+v 粘贴内容到输入框
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'v')
# 通过回车键来代替单击操作
driver.find_element_by_id("su").send_keys(Keys.ENTER)
driver.quit()

需要说明的是, 上面的脚本没有什么实际意义, 仅向我们展示模拟键盘各种按键与组合键的用法。

  • from selenium.webdriver.common.keys import Keys

在使用键盘按键方法前需要先导入 keys 类。
以下为常用的键盘操作:

  • send_keys(Keys.BACK_SPACE) 删除键(BackSpace)
  • send_keys(Keys.SPACE) 空格键(Space)
  • send_keys(Keys.TAB) 制表键(Tab)
  • send_keys(Keys.ESCAPE) 回退键(Esc)
  • send_keys(Keys.ENTER) 回车键(Enter)
  • send_keys(Keys.CONTROL,’a’) 全选(Ctrl+A)
  • send_keys(Keys.CONTROL,’c’) 复制(Ctrl+C)
  • send_keys(Keys.CONTROL,’x’) 剪切(Ctrl+X)
  • send_keys(Keys.CONTROL,’v’) 粘贴(Ctrl+V)
  • send_keys(Keys.F1) 键盘 F1
  • ……
  • send_keys(Keys.F12) 键盘 F12

获取断言信息

不管是在做功能测试还是自动化测试,最后一步需要拿实际结果与预期进行比较。这个比较的称之为断言
我们通常可以通过获取title 、URL和text等信息进行断言。text方法在前面已经讲过,它用于获取标签对之间的文本信息。 下面同样以百度为例,介绍如何获取这些信息。

from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get("https://www.baidu.com")
print('Before search================')
# 打印当前页面title
title = driver.title
print(title)
# 打印当前页面URL
now_url = driver.current_url
print(now_url)
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
sleep(1)
print('After search================')
# 再次打印当前页面title
title = driver.title
print(title)
# 打印当前页面URL
now_url = driver.current_url
print(now_url)
# 获取结果数目
user = driver.find_element_by_class_name('nums').text
print(user)
driver.quit()

脚本运行结果如下:

Before search================
百度一下,你就知道
https://www.baidu.com/
After search================
selenium_百度搜索
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx...
搜索工具
百度为您找到相关结果约5,380,000个
  • title:用于获得当前页面的标题。
  • current_url:用户获得当前页面的URL。
  • text:获取搜索条目的文本信息。

设置元素等待

WebDriver提供了两种类型的等待:显式等待隐式等待

显式等待


显式等待使WebdDriver等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException)。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
element = WebDriverWait(driver, 5, 0.5).until(
                      EC.presence_of_element_located((By.ID, "kw"))
                      )
element.send_keys('selenium')
driver.quit()

WebDriverWait类是由WebDirver 提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。具体格式如下:

WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
  • driver :浏览器驱动。
  • timeout :最长超时时间,默认以秒为单位。
  • poll_frequency :检测的间隔(步长)时间,默认为0.5S。
  • ignored_exceptions :超时后的异常信息,默认情况下抛NoSuchElementException异常。

WebDriverWait()一般由until()或until_not()方法配合使用,下面是until()和until_not()方法的说明。

  • until(method, message=‘’)

调用该方法提供的驱动程序作为一个参数,直到返回值为True。

  • until_not(method, message=‘’)

调用该方法提供的驱动程序作为一个参数,直到返回值为False。
在本例中,通过as关键字将expected_conditions 重命名为EC,并调用presence_of_element_located()方法判断元素是否存在。

隐式等待


WebDriver提供了implicitly_wait()方法来实现隐式等待,默认设置为0。它的用法相对来说要简单得多。

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from time import ctime
driver = webdriver.Firefox()
# 设置隐式等待为10秒
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")
try:
    print(ctime())
    driver.find_element_by_id("kw22").send_keys('selenium')
except NoSuchElementException as e:
    print(e)
finally:
    print(ctime())
    driver.quit()

implicitly_wait() 默认参数的单位为秒,本例中设置等待时长为10秒。首先这10秒并非一个固定的等待时间,它并不影响脚本的执行速度。其次,它并不针对页面上的某一元素进行等待。当脚本执行到某个元素定位时,如果元素可以定位,则继续执行;如果元素定位不到,则它将以轮询的方式不断地判断元素是否被定位到。假设在第6秒定位到了元素则继续执行,若直到超出设置时长(10秒)还没有定位到元素,则抛出异常。

定位一组元素

WebDriver还提供了8种用于定位一组元素的方法。

find_elements_by_id()
find_elements_by_name()
find_elements_by_class_name()
find_elements_by_tag_name()
find_elements_by_link_text()
find_elements_by_partial_link_text()
find_elements_by_xpath()
find_elements_by_css_selector()

定位一组元素的方法与定位单个元素的方法类似,唯一的区别是在单词element后面多了一个s表示复数。
接下来通过例子演示定位一组元素的使用:

from selenium import webdriver
from time import sleep
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
sleep(1)
# 定位一组元素
texts = driver.find_elements_by_xpath('//div/h3/a')
# 循环遍历出每一条搜索结果的标题
for t in texts:
    print(t.text)
driver.quit()

程序运行结果:

Selenium - Web Browser Automation
官网
功能自动化测试工具——Selenium篇
selenium + python自动化测试环境搭建
selenium是什么?_百度知道
怎样开始用selenium进行自动化测试(个人总结)_百度经验
Selenium_百度百科
selenium_百度翻译
Selenium(浏览器自动化测试框架)_百度百科
自动化基础普及之selenium是啥?
python十大主流开源框架 「菜鸟必看」

多表单切换

在Web应用中经常会遇到frame/iframe表单嵌套页面的应用,WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌页面上的元素无法直接定位。这时就需要通过switch_to.frame()方法将当前定位的主体切换为frame/iframe表单的内嵌页面中。

<html>
  <body>
    ...
    <iframe id="x-URS-iframe" ...>
      <html>
         <body>
           ...
           <input name="email" >

126邮箱登录框的结构大概是这样子的,想要操作登录框必须要先切换到iframe表单。

from selenium import webdriver
driver = webdriver.Chrome()
driver.get("http://www.126.com")
driver.switch_to.frame('x-URS-iframe')
driver.find_element_by_name("email").clear()
driver.find_element_by_name("email").send_keys("username")
driver.find_element_by_name("password").clear()
driver.find_element_by_name("password").send_keys("password")
driver.find_element_by_id("dologin").click()
driver.switch_to.default_content()
driver.quit()

switch_to.frame() 默认可以直接取表单的id 或name属性。如果iframe没有可用的id和name属性,则可以通过下面的方式进行定位。

……
#先通过xpth定位到iframe
xf = driver.find_element_by_xpath('//*[@id="x-URS-iframe"]')
#再将定位对象传给switch_to.frame()方法
driver.switch_to.frame(xf)
……
driver.switch_to.parent_frame()

除此之外,在进入多级表单的情况下,还可以通过switch_to.default_content()跳回最外层的页面。

多窗口切换

在页面操作过程中有时候点击某个链接会弹出新的窗口,这时就需要主机切换到新打开的窗口上进行操作。WebDriver提供了switch_to.window()方法,可以实现在不同的窗口之间切换。 以百度首页和百度注册页为例,在两个窗口之间的切换如下图。

from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")
# 获得百度搜索窗口句柄
sreach_windows = driver.current_window_handle
driver.find_element_by_link_text('登录').click()
driver.find_element_by_link_text("立即注册").click()
# 获得当前所有打开的窗口的句柄
all_handles = driver.window_handles
# 进入注册窗口
for handle in all_handles:
    if handle != sreach_windows:
        driver.switch_to.window(handle)
        print('now register window!')
        driver.find_element_by_name("account").send_keys('username')
        driver.find_element_by_name('password').send_keys('password')
        time.sleep(2)
        # ……
driver.quit()

在本例中所涉及的新方法如下:

  • current_window_handle:获得当前窗口句柄。
  • window_handles:返回所有窗口的句柄到当前会话。
  • switch_to.window():用于切换到相应的窗口,与上一节的switch_to.frame()类似,前者用于不同窗口的切换,后者用于不同表单之间的切换。

警告框处理

在WebDriver中处理JavaScript所生成的alert、confirm以及prompt十分简单,具体做法是使用 switch_to.alert 方法定位到 alert/confirm/prompt,然后使用text/accept/dismiss/ send_keys等方法进行操作。

  • text:返回 alert/confirm/prompt 中的文字信息。
  • accept():接受现有警告框。
  • dismiss():解散现有警告框。
  • send_keys(keysToSend):发送文本至警告框。keysToSend:将文本发送至警告框。

如下图,百度搜索设置弹出的窗口是不能通过前端工具对其进行定位的,这个时候就可以通过switch_to_alert()方法接受这个弹窗。

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.get('http://www.baidu.com')
# 鼠标悬停至“设置”链接
link = driver.find_element_by_link_text('设置')
ActionChains(driver).move_to_element(link).perform()
# 打开搜索设置
driver.find_element_by_link_text("搜索设置").click()
# 保存设置
driver.find_element_by_class_name("prefpanelgo").click()
time.sleep(2)
# 接受警告框
driver.switch_to.alert.accept()
driver.quit()

通过switch_to_alert()方法获取当前页面上的警告框,并使用accept()方法接受警告框。

下拉框选择

有时我们会碰到下拉框,WebDriver提供了Select类来处理下拉框。 如百度搜索设置的下拉框,如下图:

from selenium import webdriver
from selenium.webdriver.support.select import Select
from time import sleep
driver = webdriver.Chrome()
driver.implicitly_wait(10)
driver.get('http://www.baidu.com')
# 鼠标悬停至“设置”链接
driver.find_element_by_link_text('设置').click()
sleep(1)
# 打开搜索设置
driver.find_element_by_link_text("搜索设置").click()
sleep(2)
# 搜索结果显示条数
sel = driver.find_element_by_xpath("//select[@id='nr']")
Select(sel).select_by_value('50')  # 显示50条
# ……
driver.quit()

Select类用于定位select标签。
select_by_value() 方法用于定位下接选项中的value值。

文件上传

对于通过input标签实现的上传功能,可以将其看作是一个输入框,即通过send_keys()指定本地文件路径的方式实现文件上传。
创建upfile.html文件,代码如下:

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<title>upload_file</title>
<link href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
  <div class="row-fluid">
    <div class="span6 well">
    <h3>upload_file</h3>
      <input type="file" name="file" />
    </div>
  </div>
</body>
<script src="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.js"></scrip>
</html>

通过浏览器打开upfile.html文件,功能如下图。
接下来通过send_keys()方法来实现文件上传。

from selenium import webdriver
import os
driver = webdriver.Firefox()
file_path = 'file:///' + os.path.abspath('upfile.html')
driver.get(file_path)
# 定位上传按钮,添加本地文件
driver.find_element_by_name("file").send_keys('D:\upload_file.txt')
driver.quit()

cookie操作

有时候我们需要验证浏览器中cookie是否正确,因为基于真实cookie的测试是无法通过白盒和集成测试进行的。WebDriver提供了操作Cookie的相关方法,可以读取、添加和删除cookie信息。
WebDriver操作cookie的方法:

  • get_cookies(): 获得所有cookie信息。
  • get_cookie(name): 返回字典的key为“name”的cookie信息。
  • add_cookie(cookie_dict) : 添加cookie。“cookie_dict”指字典对象,必须有name 和value 值。
  • delete_cookie(name,optionsString):删除cookie信息。“name”是要删除的cookie的名称,“optionsString”是该cookie的选项,目前支持的选项包括“路径”,“域”。
  • delete_all_cookies(): 删除所有cookie信息。

下面通过get_cookies()来获取当前浏览器的cookie信息。

from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://www.youdao.com")
# 获得cookie信息
cookie= driver.get_cookies()
# 将获得cookie的信息打印
print(cookie)
driver.quit()

从执行结果可以看出,cookie数据是以字典的形式进行存放的。知道了cookie的存放形式,接下来我们就可以按照这种形式向浏览器中写入cookie信息。


from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://www.youdao.com")
# 向cookie的name 和value中添加会话信息
driver.add_cookie({'name': 'key-aaaaaaa', 'value': 'value-bbbbbb'})
# 遍历cookies中的name 和value信息并打印,当然还有上面添加的信息
for cookie in driver.get_cookies():
    print("%s -> %s" % (cookie['name'], cookie['value']))
driver.quit()

输出结果:
======================== RESTART: =========================

YOUDAO_MOBILE_ACCESS_TYPE -> 1
_PREF_ANONYUSER__MYTH -> aGFzbG9nZ2VkPXRydWU=
OUTFOX_SEARCH_USER_ID -> [email protected]
JSESSIONID -> abc7qSE_SBGsVgnVLBvcu
key-aaaaaaa -> value-bbbbbb

从执行结果可以看到,最后一条cookie信息是在脚本执行过程中通过add_cookie()方法添加的。通过遍历得到所有的cookie信息,从而找到key为“name”和“value”的特定cookie的value。

调用JavaScript代码

虽然WebDriver提供了操作浏览器的前进和后退方法,但对于浏览器滚动条并没有提供相应的操作方法。在这种情况下,就可以借助JavaScript来控制浏览器的滚动条。WebDriver提供了execute_script()方法来执行JavaScript代码。
用于调整浏览器滚动条位置的JavaScript代码如下:

<!-- window.scrollTo(左边距,上边距); -->
window.scrollTo(0,450);

window.scrollTo()方法用于设置浏览器窗口滚动条的水平和垂直位置。方法的第一个参数表示水平的左间距,第二个参数表示垂直的上边距。其代码如下:

from selenium import webdriver
from time import sleep
# 访问百度
driver=webdriver.Firefox()
driver.get("http://www.baidu.com")
# 设置浏览器窗口大小
driver.set_window_size(500, 500)
# 搜索
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
sleep(2)
# 通过javascript设置浏览器窗口的滚动条位置
js="window.scrollTo(100,450);"
driver.execute_script(js)
sleep(3)
driver.quit()

通过浏览器打开百度进行搜索,并且提前通过set_window_size()方法将浏览器窗口设置为固定宽高显示,目的是让窗口出现水平和垂直滚动条。然后通过execute_script()方法执行JavaScripts代码来移动滚动条的位置。

窗口截图

自动化用例是由程序去执行的,因此有时候打印的错误信息并不十分明确。如果在脚本执行出错的时候能对当前窗口截图保存,那么通过图片就可以非常直观地看出出错的原因。WebDriver提供了截图函数get_screenshot_as_file()来截取当前窗口。

from selenium import webdriver
from time import sleep
driver = webdriver.Firefox()
driver.get('http://www.baidu.com')
driver.find_element_by_id('kw').send_keys('selenium')
driver.find_element_by_id('su').click()
sleep(2)
# 截取当前窗口,并指定截图图片的保存位置
driver.get_screenshot_as_file("D:\baidu_img.jpg")
driver.quit()

脚本运行完成后打开D盘,就可以找到baidu_img.jpg图片文件了。

关闭浏览器

在前面的例子中我们一直使用quit()方法,其含义为退出相关的驱动程序和关闭所有窗口。除此之外,WebDriver还提供了close()方法,用来关闭当前窗口。例多窗口的处理,在用例执行的过程中打开了多个窗口,我们想要关闭其中的某个窗口,这时就要用到close()方法进行关闭了。

  • close() 关闭单个窗口
  • quit() 关闭所有窗口

 

常见问题

什么是firefox的profile,profile有什么用?


官方解释
简单来说,profile保存了

  • 书签
  • 记住的password
  • 其他信息,比如cookie

webidrver启动浏览器的时候会产生临时的profile,这也是为什么每次浏览器启动以后我们都要重新登录系统的原因,profile重置了,登录态也就没保存下来了。
我们在使用selenium做测试的时候可以指定使用1个已经存在的profile,从而实现一段时间的免登录功能。
如何找到firefox的profile文件

如何在selenium中指定使用已存在的profile

# 传入profile所在的绝对路径
profile = webdriver.FirefoxProfile('/home/jmunsch/.mozilla/firefox/yxjwk1py.default')
driver = webdriver.Firefox(profile)

Chrome headless 模式


我们在通过Selenium运行自动化测试时,必须要启动浏览器,浏览器的启动与关闭必然会影响执行效率,而且还会干扰你做其它事情(本机运行的话)。
那能不能把自动化测试的运行放在后台?当然可以!

  • htmlunit 项目可以模拟浏览器运行,是一个没有界面的浏览器,运行速度快。
  • PhantomJS 是一个基于webkit的JavaScript API。它使用QtWebKit作为它核心浏览器的功能,使用webkit来编译解释执行JavaScript代码。任何你可以在基于webkit浏览器做的事情,它都能做到。
  • Chrome-headless 模式,Google 自己出的无头浏览器模式。

htmlunit 在Selenium 下面,我都没跑通过一个 baidu 的 demo, 因为它不打开 UI 界面,所以,你也不知道它后台是如何渲染页面的。 弃之!
PhantomJS 非常不错,因为是使用的 QtWebKit 浏览器内核渲染页面,基本可以和真正浏览器保持一致。
Chrome-headless 模式, Google 针对 Chrome 浏览器新增加的一种模式,可以让你不打开UI界面的情况下使用 Chrome 浏览器,所以运行效果与 Chrome 保持完美一致。PhantomJS作者说,你这么搞我失业了啊!

用法

Python Selenium 用法:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver.Chrome(chrome_options=chrome_options)
...

Java Selenium用法:


import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class WebTest {
    public static void main(String[] args) throws InterruptedException {
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("--headless");
        WebDriver driver = new ChromeDriver(chromeOptions);
        ...
    }
}

Selenium的发展史

最后更新时间 2018-12-12

selenium logo
Jason Huggins在2004年发起了Selenium项目,当时身处ThoughtWorks的他,为了不想让自己的时间浪费在无聊的重复性工作中,幸运的是,所有被测试的浏览器都支持Javascript。Jason和他所在的团队采用Javascript编写一种测试工具来验证浏览器页面的行为;这个JavaScript类库就是Selenium core,同时也是seleniumRC、Selenium IDE的核心组件。Selenium由此诞生。
关于Selenium的命名比较有意思,当时QTP mercury是主流的商业自化工具,是化学元素汞(俗称水银),而Selenium是开源自动化工具,是化学元素硒,硒可以对抗汞。

Selenium 1.0

selenium 1
用简单的公式:

Selenium 1.0 = Selenium IDE + Selenium Grid + Selenium RC

Selenium IDE
Selenium IDE是嵌入到Firefox浏览器中的一个插件,实现简单的浏览器操作的录制与回放功能。
Selenium Grid
Selenium Grid是一种自动化的测试辅助工具,Grid通过利用现有的计算机基础设施,能加快Web-App的功能测试。利用Grid可以很方便地实现在多台机器上和异构环境中运行测试用例。
Selenium RC
Selenium RC(Remote Control)是Selenium家族的核心部分。Selenium RC 支持多种不同语言编写的自动化测试脚本,通过Selenium RC的服务器作为代理服务器去访问应用,从而达到测试的目的。
Selenium RC分为Client Libraries和Selenium Server。Client Libraries库主要用于编写测试脚本,用来控制Selenium Server的库。Selenium Server负责控制浏览器行为。
所以,我们在学习Selenium1.0的时候,核心应该是学习RC,它的工作原理是这样的:
selenium RC
在2006年的时候,Google的工程师Simon Stewart发起了WebDriver的项目;因为长期以来Google一直是Selenium的重度用户,但却被限制在有限的操作范围内。
Selenium RC 是在浏览器中运行JavaScript应用,使用浏览器内置的JavaScript翻译器来翻译和执行selenese命令(selenese是Selenium命令集合)。
WebDriver是通过原生浏览器支持或者浏览器扩展来直接控制浏览器。WebDriver针对各个浏览器而开发,取代了嵌入到被测Web应用中的JavaScript,与浏览器紧密集成,因此支持创建更高级的测试,避免了JavaScript安全模型导致的限制。除了来自浏览器厂商的支持之外,WebDriver还利用操作系统级的调用,模拟用户输入。
Selenium与WebDriver原是属于两个不同的项目,WebDriver的创建者Simon Stewart早在2009年8月的一份邮件中解释了项目合并的原因。
Selenium与WebDriver合并原因:为何把两个项目合并?部分原因是WebDriver解决了Selenium存在的缺点(例如能够绕过JavaScript沙箱,我们有出色的API),部分原因是Selenium解决了WebDriver存在的问题(例如支持广泛的浏览器),部分原因是因为Selenium的主要贡献者和我都觉得合并项目是为用户提供最优秀框架的最佳途径。

Selenium 2.0

因为Selenium和Webdriver的合并,所以,Selenium 2.0由此诞生。简单用公式表示为:

Selenium 2.0 = Selenium 1.0 + WebDriver

需要强调的是,在Selenium 2.0中主推的是WebDriver,可以将其看作Selenium RC的替代品。因为Selenium为了保持向下的兼容性,所以在Selenium 2.0中并没有彻底地抛弃Selenium RC。
所以,我们在学习Selenium2.0的时候,核心是学习WebDriver。它的工作原理是这样的:
selenium wd
大概是在2013年的时候,那一年我刚开始深入的学习和使用Selenium,我通过Selenium官方博客上了解到,Selenium团队将会在圣诞节发布Selenium3.0,然后,我开始期待即将到来的3.0版,后来就没有了后来,很多年过去了,依然没等到Selenium3.0。
直到2016年7月,Selenium3.0悄悄发布第一个beta版。惊不惊喜,意不意外?他们是这么解释的:
“在seleniumconf 2013,我们宣布,Selenium的一个新的主要版本将在‘圣诞节’发布。幸运的是,我们从来没有说过哪个圣诞节,因为我们已经花了一段时间来做我们想做的所有改变!我们很兴奋地宣布第一个bate版–Selenium 3.0 – beta1的发布。”

Selenium 3.0

Selenium 3.0做了一些不大不小的更新:
1、终于去掉了RC,简单用公式表示为:

Selenium 3.0 = Selenium 2.0 - Selenium RC(Remote Control)

2、Selenium3.0只支持Java8版本以上。
3、Selenium3.0中的Firefox浏览器驱动独立了,以前装完selenium2就可以驱动Firefox浏览器了,现在和Chrome一样,必须下载和设置浏览器驱动。
4、MAC OS 集成Safari的浏览器驱动。默认在/usr/bin/safaridriver 目录下。
5、只支持IE 9.0版本以上。

发表评论