一、WebDriver API
一个简单自动化脚本的构成:
脚本解析
-
# coding = utf-8 from selenium import webdriver import time browser = webdriver.Firefox() time.sleep(3) browser.get("http://www.baidu.com") time.sleep(3) browser.find_element_by_id("kw").send_keys("selenium") time.sleep(3) browser.find_element_by_id("su").click() browser.quit()
- coding = utf-8
- from selenium import webdriver
- browser = webdriver.Firefox()
- browser.find_element_by_id("kw").send_keys("selenium")
- browser.find_element_by_id("su").click()
- browser.quit()
browser.close() 也可以关闭窗口。
两者的区别是:close 方法关闭当前的浏览器窗口,quit 方法不仅关闭窗口,还会彻底的退出 WebDriver,释放与 driver server 之间的连接。所以简单来说 quit 是更加彻底的 close,quit 会更好的释放资源。
1、元素的定位
对象的定位应该是自动化测试的核心,要想操作一个对象,首先应该识别这个对象。一个对象就是一个人一样,他会有各种的特征(属性),比如我们可以通过一个人的身份证号,姓名,或者他住在哪个街道、楼层、门牌找到这个人。那么一个对象也有类似的属性,我们可以通过这些属性找到这对象。
注意:不管用那种方式,必须保证页面上该属性的唯一性。
web 自动化测试的操作核心是能够找到页面对应的元素,然后才能对元素进行具体的操作。
WebDriver 提供了一系列的对象定位方法,常用的有以下几种:
- id
- name
- class name
- link text
- partial link text
- tag name
- xpath
- css selector
我们可以看到,一个百度的输入框,可以用这么多种方式去定位。
(1)cssSelector(常用)
CSS(Cascading Style Sheets)是一种语言,它被用来描述 HTML 和 XML 文档的表现。
CSS 使用选择器来为页面元素绑定属性。这些选择器可以被 Selenium 用作另外的定位策略。
CSS 的比较灵活可以选择控件的任意属性,上面的例子中,find_element_by_css_selector("#kw") 通过 find_element_by_css_selector( ) 函数,选择取百度输入框的 id 属性来定义。
CSS 的获取可以用 Chrome 的 F12 开发者模式中 Element-右键-copy-copy selector 来获取。
css 选择语法:
- id 选择器:#id
- 类选择:class
- 标签选择:标签名
- 后代选择器:父级选择器、子级选择器
选择器的功能:选中页面中指定的标签元素。
选择器的种类分为基础选择器和复合选择器,常见的元素定位方式可以通过 id 选择器和子类选择器来进行定位。
定位百度首页的 “百度热搜” 元素,可以使用通过 id 选择器和子类选择器进行定位:#s-hotsearch-wrapper > div
“搜索输入框元素”:#kw
“百度一下按钮”:#su
若要获取页面多个元素时,可以使用 find_elements:
注意:因为自动化打开的页面是未登录状态,而我们手动打开的百度网页可能是有登录态的,二者页面可能不同,可能造成无法正确打印信息。
A. id 定位
id 是页面元素的属性,我们最常用元素定位方式,但是不是所有的元素都有 id 的。如果一个元素有 id 属性,那么一般在整个页面是唯一的。所以我们一般可以用 id 来唯一的定位到这个元素。
通过前端工具,例如 Edge 浏览器的 F12,找到了百度输入框的属性信息,如下:
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
属性 id=”kw” 通过 find_element_by_id("kw") 函数就可以定位到百度输入框。
(2)name 定位
如果这个元素有 name,并且元素的 name 命名在整个页面是唯一的,那么我们可以用 name 来定位这个元素。
用上面百度输入框的例子,其中元素的属性 name=”wd” 通过 find_element_by_name("wd") 函数同样也可以定位到百度输入框。
(3)tag name 定位和 class name 定位
从上面的百度输入框的属性信息中,我们看到,不单单只有 id 和 name 两个属性, 比如 class 和 tag name(标签名)。
input 就是一个标签的名字,可以通过 find_element_by_tag_name("input") 函数来定位,class="s_ipt" 通过 find_element_by_class_name("s_ipt") 函数定位百度输入框。
注意 :不是所有的元素用 tag name 或者 class name 来定位元素,首先要保证该元素的这两种属性在页面上是唯一的,才能够准确的定位。
(4)XPath 定位(重点)(常用)
A. 什么是 XPath
Cover page | xpath | W3C standards and drafts | W3C
XML 路径语言,不仅可以在 XML 文件中查找信息,还可以在 HTML 中选取节点。
B. XPath 基础教程
W3Schools Online Web Tutorials
相对路径:/html/head/title(不常用)
绝对路径:
- 相对路径+索引://form/span[1]/input
- 相对路径+属性值://input[@class="s_ipt"]
- 相对路径+通配符://*[@*="su"]
- 相对路径+文本匹配://a[text()="新闻"]
XPath 是一种在 XML 文档中定位元素的语言。因为 HTML 可以看做 XML 的一种实现,所以 Selenium 用户可是使用这种强大语言在 Web 应用中定位元素。
XPath 扩展了上面 id 和 name 定位方式,提供了很多种可能性。
XPATH 的获取可以用 Chrome 的 F12 开发者模式中 Element-右键-copy-copy xp。
//*[@id="kw"]
- 获取 HTML 页面所有的节点://*
-
获取 HTML 页面指定的节点://[指定节点]
//ul:获取 HTML 页面所有的 ul 节点//input:获取 HTML 页面所有的 input 节点
-
获取⼀个节点中的直接子节点:/
//span/input
-
获取⼀个节点的父节点:..
//input/..:获取 input 节点的父节点
-
实现节点属性的匹配:[@...]
-
使用指定索引的方式获取对应的节点内容
注意:xpath 的索引是从 1 开始的。
百度首页通过://div/ul/li[3] 定位到第三个百度热搜标签
更便捷的生成 selector/xpath 的方式:右键选择复制 "Copy selector/xpath"
注意 :元素的定位方法必须唯⼀。
既然可以手动复制 selector/xpath 的方式,为什么还有了解语法?
手动复制的 selector / xpath 表达式并不一定可以满足上面的唯一性的要求,有时候也需要手动的进行修改表达式。
css 选择器和 xpath 选择器哪个更好?
css 选择器定位元素效率更高。
(5)link text 定位
有时候不是一个输入框也不是一个按钮,而是一个文字链接,我们可以通过链接内容,也就是 link text 来定位。
注意:链接内容必须这个页面唯一,否则会报错。
(6)Partial link text 定位
通过部分链接定位,这个有时候也会用到,我还没有想到很好的用处。拿上面的例子,我可以只用链接的一部分文字进行匹配:
二、操作测试对象
前面讲到了不少知识都是定位元素,定位只是第一步,定位之后需要对这个元素进行操作。是鼠标点击还是键盘输入,或者清除元素的内容,或者提交表单等。这个取决于定位元素需要进行的下一步操作。
Webdriver 中比较常用的操作对象的方法有下面几个:
- click 点击对象。
- send_keys 在对象上模拟按键输入。
- clear 清除对象输入的文本内容。
- submit 提交。
- text 用于获取元素的文本信息。
- get_attribute 获得属性值。
1、click 点击 / 提交对象
click() # 用于点击一个按钮
页面上任意元素都可以进行点击操作。
2、sendKeys 模拟按键输入
键盘上可以输入的内容都能填入。
sendKeys("")
# send_keys("xx") 用于在一个输入框里输入 xx 内容
3、clear 清除文本内容
输入文本后又想换一个新的关键词,这里就需要用到 clear()。
clear() 用于清除输入框的内容,比如百度输入框里默认有个 “请输入关键字” 的信息,再比如我们的登陆框一般默认会有 “账号”、“密码” 这样的默认信息。clear 可以帮助我们清除这些信息。
连续的 sendKeys 会将多次输入的内容拼接在一起,如果想要重新输入,需要使用清除方法:
4、submit 提交表单
打开百度搜索页面,按钮 “百度一下” 元素的类型 type=“submit”,所以把“百度一下”的操作从 click 换成 submit 可以达到相同的效果:
driver.find_element_by_id("su").submit()
- 如果点击的元素放在 form 标签中,此时使用 submit 实现的效果和 click 是一样的。
- 如果点击的元素放在非 form 标签中,此时使用 submit 报错。
5、text 获取元素文本
如何判断获取到的元素对应的文本是否符合预期呢?
获取元素对应的⽂本并打印⼀下。
getText()
“百度一下” 在这里是作为元素属性值,而不是文本信息。
不应该是获取文本信息,而是获取属性值:
注意 :文本和属性值不要混淆了,获取属性值需要使用方法: getAttribute(" 属性名称 ")。
6、title 获取当前页面标题 & current_url 获取当前页面 URL
getTitle()
getCurrentUrl()
适用场景:页面元素可点击跳转的情况下,用来检测跳转的结果是否为正确的。
三、等待(重点)
通常代码执行的速度比页面渲染的速度要快,如果避免因为渲染过慢出现的自动化误报的问题呢?
可以使用 selenium 中提供的三种等待方法。
1、强制等待
添加休眠非常简单,我们需要引入 time 包,就可以在脚本中自由的添加休眠时间了,这里的休眠指固定休眠。
import time
time.sleep() # 单位:秒
# 当调用该方法时,程序会直接阻塞,等待指定秒数后继续执行后面的代码
- 优点:使用简单,调试的时候比较有效。
- 缺点:影响运行效率,浪费大量的时间。
2、隐式等待
隐式等待是⼀种智能等待,它可以规定在查找元素时,在指定时间内不断查找元素。如果找到则代码继续执行,直到超时没找到元素才会报错。
通过添加 implicitly_wait() 方法就可以方便的实现智能等待。implicitly_wait(30) 的用法比 time.sleep() 更智能,后者只能选择一个固定的时间的等待,而前者可以在一个时间范围内智能的等待。
隐式等待的生命周期:隐式等待作用域是整个脚本的所有元素。即只要 driver 对象没有被释放掉(driver.quit()),隐式等待就一直生效。
- 优点:智能等待,作用于全局。
隐式地等待并非一个固定的等待时间,当脚本执行到某个元素定位时,如果元素可以定位,则继续执行;如果元素定位不到,则它以轮询的方式不断的判断元素是否被定位到。直到超出设置的时长。若等待指定秒数后还是查找不到元素,则报错。
隐式等待扫描的是整个页面的元素,页面中所有的元素都被加载到之后才去执行后续的代码。
3、显示等待
显示等待也是一种智能等待,在指定超时时间范围内只要满足操作的条件就会继续执行后续代码。
# new WebDriverWait(driver, Duration.ofSeconds(3)).until($express)
# $press:涉及到selenium.support.ui.ExpectedConditions包下的ExpectedConditions类
# 返回值:boolean
# 示例:
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 2)
wait.until(EC.invisibility_of_element((By.XPATH, '//*[@id="2"]/div/div/div[3]/div[1]/div[1]/div')))
ExpectedConditions 预定义方法的一些示例:
- title_is(title):检查页面标题的期望值。
- title_contains(title):检查标题是否包含区分大小写的子字符串的期望值。
- visibility_of_element(locator):用于检查元素的期望是可见的并已启用,以便我们可以单击它。
- invisibility_of_element(locator):用于检查元素的不可见性。
- visibility_of_element_located(locator, str]):用于检查元素是否存在于页面的 DOM 上的期望值。
- visibility_of(element):检查已知存在于页面 DOM 上的元素是否可见的期望。
- alert_present():检查是否出现弹窗。(可以等待隐式等待(无法等待弹窗,因为弹窗不是页面的元素,无法通过页面元素来定位到弹窗)无法处理的问题)
- 优点:显示等待是智能等待,可以自定义显示等待的条件,操作灵活。
- 缺点:写法复杂。
显示等待相较于隐式等待的好处:
隐式等待和显示等待一起使用效果如何呢?
不建议这么做。混合隐式和显示等待可能会导致不可预测的等待时间。
但是强制等待可以任意配合隐式等待或显示等待来使用。
重复多次,设置 10 秒的隐式等待和 15 秒的显示等待导致 20 秒后发生超时:
四、窗口
打开一个新的页面之后获取到的 title 和 URL 仍然还是前一个页面的。当我们手工测试的时候,我们可以通过眼睛来判断当前的窗口是什么,但对于程序来说它是不知道当前最新的窗口应该是哪一个的。
对于程序来说,它如何来识别每一个窗口呢?
每个浏览器窗口都有一个唯一的属性句柄(handle)来表示,我们就可以通过句柄来切换。
1、切换窗口
- 获取当前页面句柄:driver.current_window_handle
- 获取所有页面句柄:driver.windowhandles
- 通常情况下会打开两个标签页来实现标签页的切换测试,多个标签页切换到某一个标签页的场景在测试中不常见。
- 更多时候是输入对应页面的链接在当前标签页下进行测试。
2、窗口设置大小
仅作了解即可,在自动化脚本执行过程中通常测试人员不关注页面的变化。
3、屏幕切换
我们的自动化脚本⼀般部署在机器上自动的去运行,当自动化运行出现了报错,仅仅通过终端的错误提示给到的有用信息是一定的,若能将当时的页面变化截图抓拍下来,记录当时的错误场景,能更好的定位问题并解决问题。
指定图片存储路径(需要提前创建好):
如果多次运行,由于图片给定的名称是固定的,那么历史的图片会被覆盖。
如何让我们每次生成的图片都是唯一的呢,如何将我们的历史图片都保存下来?
需要让我们每次生成的图片对应的名称都不一样,最好通过保存当前图片的时间来作为其名称。
4、关闭窗口
driver.close(); # 退出一个窗口
driver.quit(); # 退出浏览器
注意:窗口关闭后 driver 要重新定义。
五、弹窗
弹窗是在页面是找不到任何元素的,这种情况怎么处理?
使用 Selenium 提供的 alert 接口。
1、警告弹窗 + 确认弹窗
(1)警告弹窗
出现了弹窗,页面其他元素也无法定位:
页面出现了弹窗,必须先处理弹窗之后才能定位到页面的元素。
- 切换到弹窗。
- 关闭弹窗(点击确认 / 取消)
Alert alert = driver.switchTo.alert();
# 确认
alert.accept()
# 取消
alert.dismiss()
# 注意:switch_to.alert() 只能处理原生的 alert
(2)确认弹窗
- 确定:
- 取消:
2、提示弹窗
Alert alert = driver.switchTo.alert();
alert.sendKeys("xxxx");
alert.accept();
alert.dismiss();
- 输入文本 + 确定:
肉眼看不到在弹窗上输入的文本信息:
- 输入文本 + 取消:
六、浏览器的操作
1、浏览器最大化
我们知道调用启动的浏览器不是全屏的,这样不会影响脚本的执行,但是有时候会影响我们 “观看” 脚本的执行。
browser.maximize_window()
2、设置浏览器宽、高
最大化还是不够灵活,能不能随意的设置浏览的宽、高显示?
当然是可以的。browser.set_window_size(width, high)
3、浏览器导航
(1)打开网站
// 更⻓的⽅法
driver.navigate().to("https://selenium.dev");
// 简洁的⽅法
driver.get("https://selenium.dev");
(2)浏览器的前进、后退、刷新
非页面元素,所以需要 selenium 提供的现成的方法:
driver.navigate().back();
driver.navigate().forward();
driver.navigate().refresh();
4、控制浏览器滚动条(重点)
浏览器滚动条的控制需要依靠 js 脚本。
5、浏览器参数设置
(1)设置无头模式
程序在后端运行,界面看不到页面的表现。
自动化打开浏览器默认情况下为有头模式。
(2)设置浏览器加载策略
options.page_load_strategy = '加载方式';
driver.get 默认情况下等待所有的资源加载完成之后才能继续往下执行,但是实际上主页面加载完成之后就可以继续执行自动化。若一直等待的话可能会造成页面超时、元素找不到等问题。
页面加载方式主要有三种类型:
- normal:默认值,等待所有资源下载完成(主体框架、图片、视频等资源)
- eager:DOM 访问已准备就绪,但诸如图像的其它资源可能仍在加载(推荐)
- none:完全不会阻塞 WebDriver,直接继续往下执行脚本(不推荐)
七、上传文件操作
点击文件上传的场景下会弹窗系统窗口,进行文件的选择。
Selenium 无法识别非 Web 的控件,上传文件窗口为系统自带,无法识别窗口元素。但是可以使用 Sendkeys 来上传指定路径的文件,达到的效果是一样的。
文件上传操作也是比较常见功能之一,上传功能没有用到新有方法或函数,关键是思路。
上传过程一般要打开一个本地窗口,从窗口选择本地文件添加。所以,一般会卡在如何操作本地窗口添加上传文件。
当通过页面元素调起上传的窗口后,文件窗口不能被作为页面元素被选中,此时要解决的问题是:如何定位到弹窗并选中文件呢?
只要定位上传按钮,通过 send_keys() 添加本地文件路径就可以了,可以实现将本地文件夹中的文件上传上来。完整绝对路径和相对路径都可以(注意需要添加转义字符),关键是上传的文件存在。
八、键盘事件
1、键盘按键用法
- from selenium.webdriver.common.keys import Keys
- send_keys(Keys.TAB) # TAB
- send_keys(Keys.ENTER) # 回车
- send_keys(Keys.SPACE) #空格键
- send_keys(Keys.ESCAPE) #回退键(Esc)
- .....
2、键盘组合键用法
- 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)
九、鼠标事件
- from selenium.webdriver.common.action_chains import ActionChains
语法示例如下:
#鼠标拖动事件
ActionChains(driver).move_to_element(element).perform()
ActionChains(driver) 生成用户的行为。所有的行动都存储在actionchains 对象。通过perform()存储的行为。
move_to_element(element) 移动鼠标到一个元素中,menu 上面已经定义了他所指向的哪一个元素。
perform() 执行所有存储的行为。
- context_click() 右击
- double_click() 双击
- drag_and_drop() 拖动
- move_to_element() 移动
十、定位一组元素
WebDriver 可以很方便的使用 findElement 方法来定位某个特定的对象,不过有时候我们却需要定位一组对象,这时候就需要使用 findElements 方法。
- 批量操作对象,比如将页面上所有的checkbox 都勾上。
- 先获取一组对象,再在这组对象中过滤出需要具体定位的一些对象。比如定位出页面上所有的 checkbox,然后选择最后一个。
十一、多层框架 / 窗口定位
- 定位一个 frame :switch_to.frame(name_or_id_or_frame_element)
- 定位一个窗口 window:switch_to.window(name_or_id_or_frame_element)
1、多层框架的定位
switch_to.frame(name_or_id_or_frame_element):通过 frame 的 id 或者 name 或者 frame 自带的其它属性来定位框架,这里 switch_to.frame() 把当前定位的主体切换了 frame 里。
switch_to.default_content:从 frame 中嵌入的页面里跳出,跳回到最外面的默认页面中。
2、多层窗口定位
有可能嵌套的不是框架,而是窗口,还有真对窗口的方法:switch_to.window 用法与 switch_to.frame 相同:driver.switch_to.window("windowName")