本文章最初发布在 XJHui’s Blog,未经允许,任何人禁止转载!
注意:最新修改版本已发布在 这里,点击前往查看!
关于笔记
- 视频地址:bilibili-白月黑羽教编程(34p)
- 课件、笔记:http://www.python3.vip/tut/auto/selenium/01/
- 学前技能:
- python基础
- python3环境、python解释器
- 前端基础(html、css)
- 学后技能:
- web自动化
- python小脚本
- 爬虫(不推荐)
selenium库
概述
定义:是一个web的自动化测试工具
官方文档:Selenium with Python
功能:python中导入selenium库后通过调用函数实现在浏览器中模拟鼠标点击、移动等操作
使用范围:
- 模拟登陆
- 刷网课
原理图
注意:
- 安装:selenium库、浏览器(chrome为例)以及浏览器对应版本的驱动
- 使用:想实现
点击
操作,只需要调用selenium库中函数 - 兼容性:支持Java,Python等多种语言
安装库
执行命令:
1
pip install selenium -i https://mirror.baidu.com/pypi/simple # 为了保证安装速度,使用了百度镜像
运行结果:
验证安装:
1
2import selenium # 导入selenium库
print(selenium.__version__) # 查看版本号运行结果:
浏览器及驱动
安装浏览器:点这里下载
安装驱动:
查看chrome版本:
点击 这里 查找对应驱动下载并解压:
注意:
- 驱动和浏览器的版本号越接近越好,略有差别(比如72和73)也行
- 解压路径不要出现中文符号、空格等
- 解压后为一个.exe文件,不需要安装
简单示例
开启浏览器
代码:
1
2
3from selenium import webdriver # 从selenium库中导入web自动化常用的函数
wd = webdriver.Chrome(r'd:\tool\chromedriver.exe') # 实例化一个WebDriver类型的对象,参数浏览器驱动路径注意:
- python默认会将
浏览器驱动路径
中的\转义,在其前面添加r
表示不转义 - 通过创建的wd(webdriver对象)来操控浏览器
- python默认会将
运行结果:
跳转网址
代码:
1
wd.get('https://plushine.cn') # 使用get方法,打开一个新网址
运行结果:
选择元素
根据id
语法:
1
kw = wd.find_element_by_id('kw') # 根据id选择元素,返回该元素对应的WebElement对象,下文以wb代指
案例:在百度搜索页面输入plushine并回车
查看文本框的id值(kw):
注意:控制台查看网页源码的教程 在这里,感谢:百度经验。
查看提交按钮的id值(su):
编写代码:
1
2
3
4
5kw = wd.find_element_by_id('kw') # 根据id选择元素
kw.send_keys('plushine') # .send_keys():文本框中输入文字
su = wd.find_element_by_id('su')
su.click() # .click():点击运行结果:
根据class
语法:
1
element = wd.find_elements_by_class_name('plant') # 返回wb类型的对象
注意区分:
- element:返回第一个符合条件的元素的wb对象,找不到抛出异常【NoSuchElementException】
- elements:返回所有符合条件元素的wb对象列表【适合元素的统一处理】,找不到返回空列表
案例:选择所有class属性为plant的元素
访问网址,选择元素:
1
2
3wd.get('http://cdn1.python3.vip/files/selenium/sample1.html') # 打开网址
elements = wd.find_elements_by_class_name('plant') # 查找class等于plant的元素,elements返回列表查看对象内容(debug):
1
# 最后一行添加pass,并断点调试,详见下图
运行结果:
注意:如果使用print是无法查看对象内容的:
1
print(elements)
运行结果:
输出元素text属性值:
1
2for element in elements:
print(element.text) # .text:查看内部文字(下文会详解)运行结果:
根据tag
代码:
1
2
3
4spanList = wd.find_elements_by_tag_name('span') # 返回所有标签为span元素的WebElement对象
for span in spanList:
print(span.text) # 输出标签内部文字运行结果:
根据wb
wb(WebElement):通过find函数选择元素返回值类型为wb
案例:分别从全局和局部选择span标签,判断返回值是否相同
1
2
3
4
5
6
7
8
9
10
11wd.get('http://cdn1.python3.vip/files/selenium/sample1.html')
wb = wd.find_element_by_id('container') # find返回值类型为wb
wdElement = wd.find_elements_by_tag_name('span') # webdriver中查找元素
weElement = wb.find_elements_by_tag_name('span') # webelement中查找元素
print("-------全局范围wd-----")
print(len(wdElement))
print("-------局部范围wb-----")
print(len(weElement))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
运行结果:
<img src="https://cdn.jsdelivr.net/gh/xingjiahui/CDN@latest/2020/10/20/5d9843f5f36fc578427ab8ae660cbf95.png" width="70%"/>
## 隐式等待
1. 案例:百度搜索 `plushine` ,获取第一条搜索结果:
<img src="https://cdn.jsdelivr.net/gh/xingjiahui/CDN@latest/2020/10/20/d7ca6f337c1cc11a0b79b56dfae17dfc.png" width="70%"/>
- 程序代码:
```python
wd.get('https://baidu.com') # 访问百度搜素首页
wd.find_element_by_id('kw').send_keys('plushine\n') # 通过id选择元素,通过send_keys()填充文本,\n:回车
element = wd.find_element_by_id('1') # 通过id选择元素
print(element.text) # 输出返回元素报错截图:
原因分析:
1
2
3element.send_keys('plushine\n') # 回车后,开始从百度服务器接收数据
element = wd.find_element_by_id('1') # 查找id为1的元素,但此时数据还未完全获取导致查找失败解决方法(使用隐式等待):
语法:
1
2wd = webdriver.Chrome(r'd:\tool\chromedriver.exe') # 创建Chrome浏览器的WebDriver对象
wd.implicitly_wait(max) # 其中max表示最大等待时间(s)原理:当元素查找失败时,会每隔0.5s检查一下能否找到元素,直至最大等待时间报错
案例:百度搜索
plushine
,获取第一条搜索结果:1
2
3
4
5
6
7
8
9
10
11from selenium import webdriver
wd = webdriver.Chrome(r'd:\tool\chromedriver.exe')
wd.implicitly_wait(10) # 设置最大等待时间为10s
wd.get('https://baidu.com')
wd.find_element_by_id('kw').send_keys('plushine\n')
element = wd.find_element_by_id('1')
print(element)运行结果:
操控元素
常用方式
前面案例中已经多次使用了,这里不再赘述
- 点击:.click()【a标签、按钮等均可点击】
- 输入字符串:.send_keys()
- 获取元素内容:.text
获取标签属性
语法:
1
element.get_attribute('name') # 注意:element是一个wb对象,get_attribute:获取name属性值
案例:通过id选择元素并获取该元素的srcid属性值
程序代码:
1
2
3element = wd.find_element_by_id('1')
print(element.get_attribute('srcid'))运行结果:
获取html
使用.get_attribute()可以获取某元素的html代码,当参数分别为text、outerHTML、innerHTML时的结果如右图:
案例:分别输出text、get_attribute(‘outerHTML’)),get_attribute(‘innerHTML’))的结果:
1
2
3
4
5
6
7element = wd.find_element_by_id('h1') # 查找id为h1的元素
print("-------text-------")
print(element.text) # 元素内部文字
print("-------outerHTML-------")
print(element.get_attribute('outerHTML')) # 整个元素对应的html
print("-------innerHTML-------")
print(element.get_attribute('innerHTML')) # 元素内部html运行结果:
注意:有些内容使用text可能无法获取,可以尝试使用:
element.get_attribute('innerText')
element.get_attribute('textContent')
获取输入框内容
注意:对于输入(搜索)框,不可以使用id或class来获取里面的内容
案例:百度搜索框中输入
plushine
,返回刚刚输入的内容1
2
3
4
5wd.find_element_by_id('kw').send_keys('plushine') # 文本框中输入plushine
element = wd.find_element_by_id('kw') # 通过id选择文本框
print(element.get_attribute('value')) # 通过get_attribute('value')获取文本框中的内容运行结果:
输入
wd.find_element_by_id('kw').send_keys('plushine')
的类型:1
print(type(wd.find_element_by_id('kw').send_keys('plushine')))
运行结果:
因为
element.get_attribute('value')
中要求element的类型是wb对象,所以下面的代码是错误的:1
2
3element = wd.find_element_by_id('kw').send_keys('plushine') # 此时element的类型为NoneType
print(element.get_attribute('value'))
CSS选择器
根据标签选择
案例:选择所有标签为div的元素并输出其内部文字
1
2
3
4
5
6wd.get('http://cdn1.python3.vip/files/selenium/sample1.html')
elements = wd.find_elements_by_css_selector('div')
for element in elements:
print(element.text)运行结果:
注意:
- 其它标签也具有相同的用法,不再列举
- 也都可以使用
wd.find_element_by_css_selector()
来选择元素
根据属性选择
案例:选择所有class属性为animal的元素,并输出整个元素内容(class、id)
1
2
3
4elements = wd.find_elements_by_css_selector('.animal') # class用.表示,id用#表示
for element in elements:
print(element.get_attribute('outerHTML'))运行结果:
案例:选择下面代码中的外部元素(其它属性)
1
<a href="http://www.miitbeian.gov.cn">苏ICP备88885574号</a>
注意:href不像class、id可以使用特定的符号表示,但依然可以通过css选择器选择元素
1
2
3# 当没有class、id时可以使用[属性=""]来选择元素
element = wd.find_element_by_css_selector('[href="http://www.miitbeian.gov.cn"]')
print(element.get_attribute('outerHTML'))运行结果:
技巧:每次都需要运行代码才能判断css选择器是否正确?有没有简便方法?有,Chrome控制台的搜索功能
打开chrome控制台,点击下图位置的
元素
:按下快捷键:ctrl+f,出现搜索框:
输入css选择器可筛选出符合条件的元素,大大节省了调试时间:
子元素和后代元素
代码块:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<div id='container'>
<div id='layer1'>
<div id='inner11'>
<span>内层11</span>
</div>
<div id='inner12'>
<span>内层12</span>
</div>
</div>
<div id='layer2'>
<div id='inner21'>
<span>内层21</span>
</div>
</div>
</div>- 子元素:layer1、layer2都是container的子元素,同理 inner11是layer1的子元素
- 后代元素:inner11、inner12、inner21都是container的后代元素,但不是其子代元素
语法:
1
2
3wd.find_element_by_css_selector('#container > #layer1 > #inner11') # 子代元素之间用 >
wd.find_element_by_css_selector('#container #inner11') # 后代元素之间用空格
print(element.get_attribute('outerHTML')) # 打印外部元素运行结果:
联合使用
为了便于表示,下面代码中省去了
wd.find_element_by_css_selector()
标签和属性联用:
1
2'#container > div' # id为container的子元素中标签为div的元素(之间有空格或>代表描述的是不同元素)
'span.copyright' # class为copyright且标签为span的元素(之间无空格或>代表描述的是同一个元素)两个选择器联用(逗号分隔,or关系):
1
2
3elements = wd.find_elements_by_css_selector('.plant ,.animal') # 选取class属性为plant或animal的元素
for element in elements:
print(element.get_attribute('outerHTML'))运行结果:
案例:获取.vsc-initialized 下 #t1 和 #t2 元素的css选择器
1
2'.vsc-initialized (#t1 , #t2)' # 错误,因为括号不能用在css选择器中
'.vsc-initialized #t1 , .vsc-initialized #t2' # 正确方法(略显笨重,不过后面会将其它简单方法)运行结果:
按次序选择子节点
父元素的第n个子节点:
1
2element = wd.find_element_by_css_selector('span:nth-child(2)') # 查询第二个span标签
print(element.get_attribute('outerHTML'))运行结果:
注意:
- 使用
:nth-last-child(2)
表示选择倒数第二个子元素 - 使用
span:nth-child(2)
(标签属性联用),表示选择第二个且标签为span的子元素
- 使用
父元素的第几个某类型的子节点:
1
2element = wd.find_element_by_css_selector('span:nth-of-type(2)')
print(element.get_attribute('outerHTML'))运行结果:
奇数、偶数节点:
案例:选择#t1的偶数子元素:
1
2element = wd.find_element_by_css_selector('span:nth-child(even)')
print(element.get_attribute('outerHTML'))运行结果:
注意:选取偶数子元素只需将
even
更换为odd
即可
选择弟弟节点
为什么是“弟弟”节点?
案例:使用css选择器中弟弟节点选择方法,选择下图中的span标签
代码:
1
2
3
4wd.get("http://cdn1.python3.vip/files/selenium/sample1a.html")
element = wd.find_element_by_css_selector('#t2>h3+span') # css选择器中使用'+'表示兄弟节点的选择
print(element.text)运行结果:
切换frame/窗口
切换frame
frame、iframe的作用:在frame或iframe标签之间存放完整的html代码,实现网页嵌套
注意:selenium默认不会获取iframe内部元素
案例:输出iframe内部满足class=”.plant”的元素
1
2
3
4
5
6
7
8wd.get('http://cdn1.python3.vip/files/selenium/sample2.html')
wd.switch_to.frame('innerFrame') # 使用switch_to将操作范围切换到iframe内部,参数为iframe的name属性或一个wb对象
elements = wd.find_elements_by_class_name('plant')
for element in elements:
print(element.get_attribute('outerHTML'))运行结果:
案例:执行完案例2后点击iframe外部按钮
1
2
3wd.switch_to.default_content() # 先将操作范围切换到iframe外部
wd.find_element_by_css_selector('#outerbutton').click()运行结果:
切换窗口
使用js或点击超链接打开新标签页后,窗口并不会自动切换,需要手动操作
区分标签页和窗口:
语法:
1
wd.switch_to.window(handle) # 使用switch_to.window()将窗口切换为目标窗口,参数为目标窗口的handle值
注意:目标窗口的handle值是未知的,但window_handles中存放了该网页的所有handle值,可依次尝试
案例:点击超链接访问必应首页,文本框中输入 “plushine”
1
2
3
4
5
6
7
8
9
10wd.get('http://cdn1.python3.vip/files/selenium/sample3.html')
wd.find_element_by_css_selector('a[href="http://www.bing.com"]').click()
for handle in wd.window_handles: # 遍历所有handle,依次切换,直至满足条件(handle对应的title与目标窗口一致)
wd.switch_to.window(handle)
if '必应' in wd.title:
break
wd.find_element_by_css_selector('input.b_searchbox').send_keys('plushine')运行结果:
案例:将操作范围切换回原来的标签,并点击按钮
方式一:for循环寻找正确的handle
方式二:记录最开始打开的标签handle值,然后直接跳转
1
2
3
4
5
6
7
8
9
10
11
12
13
14wd.find_element_by_css_selector('a[href="http://www.bing.com"]').click()
mainHandle = wd.current_window_handle # 记录当前窗口handle值
for handle in wd.window_handles:
wd.switch_to.window(handle)
if '必应' in wd.title:
break
wd.find_element_by_css_selector('input.b_searchbox').send_keys('plushine')
wd.switch_to.window(mainHandle) # 跳转到最开始打开的标签
wd.find_element_by_css_selector('#outerbutton').click()运行结果:
选择框
radio
介绍:radio是单选框,最多只能选择一项
案例:先打印当前选中的老师,再选中小雷老师
1
2
3
4
5
6
7wd.get('http://cdn1.python3.vip/files/selenium/test2.html')
element = wd.find_element_by_css_selector('input[type="radio"][checked="checked"]')
print(element.get_attribute('value'))
wd.find_element_by_css_selector('input[type="radio"][value="小雷老师"]').click()运行结果:
注意:使用
element.text
是获取不到其内部文字的,因为input是单标签
checkbox
介绍:checkbox是复选框,可选择多项
案例:只选中小雷老师
1
2
3
4
5
6
7
8wd.get('http://cdn1.python3.vip/files/selenium/test2.html')
elements = wd.find_elements_by_css_selector('input[type="checkbox"][checked="checked"]')
for element in elements:
element.click()
wd.find_element_by_css_selector('input[type="checkbox"][value="小雷老师"]').click()运行结果:
select
定义:select是下拉框,每个option标签是一个选项,有单选下拉框也有多选下拉框
select类:selenium中一个专门操作select下拉框的类
案例:单选下拉框中使用select类选择小雷老师
1
2
3
4
5
6
7
8from selenium.webdriver.support.ui import Select # 导入Select类
wd.get('http://cdn1.python3.vip/files/selenium/test2.html')
select = Select(wd.find_element_by_css_selector('#ss_single')) # 创建select类时的参数为wb对象
select.select_by_index(1) # 根据序号选择(从0开始),还有下面两种选择方法(结果相同)
# select.select_by_value('小雷老师') # 根据option对应的属性选择
# select.select_by_visible_text('小雷老师') # 根据网页可见文字选择运行结果:
案例:多选下拉框中使用selenium的select类选择小江老师和小凯老师
1
2
3
4
5
6
7
8
9from selenium.webdriver.support.ui import Select # 导入Select类
wd.get('http://cdn1.python3.vip/files/selenium/test2.html')
select = Select(wd.find_element_by_css_selector('#ss_multi')) # 创建select类时的参数为WebElement对象
select.deselect_all() # 取消全部选择
select.select_by_index(0) # 使用下标选取(从0开始)
select.select_by_index(2)运行结果:
注意:
select_by_value()
和select_by_visible_text()
的方法依旧适用于多选框
弹出框
alert
定义:alert弹框,可以显示一段text文本和一个accept(确定)按钮
案例:获取alert弹出框显示的text内容后点击accept(确定)按钮
1
2
3
4
5
6wd.get('http://cdn1.python3.vip/files/selenium/test4.html')
wd.find_element_by_css_selector('#b1').click() # 点击页面的按钮后显示alert弹窗
print(wd.switch_to.alert.text) # 获取alert弹出框显示的text内容
wd.switch_to.alert.accept() # 点击accept按钮运行结果:
confirm
定义:confirm弹框,可以显示一段text文本、一个accept(确定)按钮和一个dismiss(取消)按钮
案例:获取confirm弹出框显示的text内容后点击dismiss(取消)按钮
1
2
3
4
5
6wd.get('http://cdn1.python3.vip/files/selenium/test4.html')
wd.find_element_by_css_selector('#b2').click() # 点击页面的按钮后显示confirm弹窗
print(wd.switch_to.alert.text) # 获取confirm弹出框显示的text内容
wd.switch_to.alert.dismiss() # 点击dismiss按钮运行结果:
注意:
switch_to.alert.accept()
函数依然适用于confirm 弹出框
prompt
定义:prompt弹框,可以显示一个input文本框、一段text文本、一个accept(确定)按钮和一个dismiss(取消)按钮
案例:文本框中输入
plushine.cn
后点击确定1
2
3
4
5
6
7wd.get('http://cdn1.python3.vip/files/selenium/test4.html')
wd.find_element_by_css_selector('#b3').click() # 点击页面的按钮后显示prompt弹窗
print(wd.switch_to.alert.text) # 获取prompt弹出框显示的text内容
wd.switch_to.alert.send_keys('plushine.cn') # 向文本框中输入文本
wd.switch_to.alert.accept() # 点击accept按钮运行结果:
更多操控方法
鼠标悬停
引入:在 魅族官网 选择产品时需要将鼠标悬停在导航栏上,才能看到内部元素
ActionChains类:实现右键点击、双击、移动到某个元素、拖拽等鼠标操作
案例:将鼠标【移动并悬停】在导航栏中
配件
位置上1
2
3
4
5
6
7
8from selenium.webdriver.common.action_chains import ActionChains # 导入ActionChains类
wd.get('https://www.meizu.com/')
ac = ActionChains(wd) # 创建ActionChains对象,参数为wd对象
ac.move_to_element(
wd.find_element_by_css_selector('#meizu-header-link > :nth-child(3)')
).perform()运行结果:
注意:更多ActionChains类的使用方法请自行查找
冻结界面
问题:通过控制台无法展开商品
解决:先冻结界面,再通过控制台选择
控制台输入如下JavaScript代码:
1
setTimeout(function(){debugger}, 5000) # 设置定时器,5秒后冻结界面
运行结果:
快速将鼠标悬停在网站导航栏中使商品展示:
等待窗口冻结:
选择商品元素:
执行js
语法:
1
2
3
4js = """
console.log("{}");
""".format('hello world!')
wd.execute_script(js) # 执行上面的js代码注意:
- js代码可以使用放在三个双引号之间(如上面格式)
- js中注释符号为//,注意和python注释的符号区分
案例:使用js打开一个新的标签页,并将窗口切换到该标签,关闭新标签页并将窗口切换回最开始的标签页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20from selenium import webdriver
wd = webdriver.Chrome('D:\environment\chromedriver.exe')
wd.get("https://plushine.cn")
print(wd.title) # 通过打印窗口title,判断所在的标签页
js = """ //js代码
window.open('https://www.baidu.com')
"""
wd.execute_script(js) # 运行js代码
hanldeList = wd.window_handles
wd.switch_to.window(hanldeList[-1]) # 打开最新的标签页
print(wd.title) # 打印title,判断窗口是否切换成功
wd.close() # 关闭当前标签页
wd.switch_to.window(hanldeList[0]) # 将窗口切换切换到最开始的标签页
print(wd.title)运行过程:
运行结果:
注意:常用的窗口切换
handles = wd.window_handles #获取当前浏览器的所有窗口句柄
wd.switch_to.window(handles[-1]) #切换到最新打开的窗口
wd.switch_to.window(handles[-2]) #切换到倒数第二个打开的窗口
…
wd.switch_to.window(handles[0]) #切换到最开始打开的窗口
Xpath
我一般都是用css选择器,当css不能选择时再使用xpath,所以总结的不全面
定义:堂兄节点,下图橙色框中两个节点互为堂兄节点
语法:
- 选择子节点:/
- 选择父节点:/..
- 选择div标签:div
- 选择第二个子元素且标签为div的节点:div[2]
案例:使用css选择器选择上图中的span标签,再使用xpath选择div标签
1
2
3
4element = wd.find_element_by_css_selector('interaction-status evaluation') # 先使用css选择器,选择span标签
str = element.find_element_by_xpath('./../div[2]') # 再使用xpath语句选择div
print(str.text)注意:
- wb的基础上选择要在xpath语句最前面加上’.’(类似css的局部选择时)
- wd的基础上选择不需要添加
写在后面
- 课程知识很全,老师讲解很细
- b站还有老师的app自动化和Python Qt的课程,有时间就看
- 明天开始写云班课互评作业的脚本,有可能就写刷课的脚本,代码到时候会开源
不足之处,欢迎留言,会及时回复,及时更正!
创作不易,感谢支持!