学习网站:白月黑羽
原理与安装
原理
使用Selenium,我们可以通过写出自动化程序,像人一样在浏览器里操作web界面。 比如点击界面按钮,在文本框中输入文字等。而且还能从web界面获取信息,比如获取火车、汽车票务信息,招聘网站职位信息,财经网站股票价格信息等等,然后用程序进行分析处理。Selenium的自动化原理是这样的:
从上图可以看出:我们写的程序的自动化请求通过客户端库里面的编程接口发送给浏览器。比如,我们要模拟用户点击界面按钮, 自动化程序里面就应该调用客户端库相应的函数, 就会发送点击元素的请求给下方的浏览器驱动。 然后,浏览器驱动再转发这个请求给浏览器。这个自动化程序发送给浏览器驱动的请求是HTTP请求。
- 客户端库是Selenium组织提供的。Selenium组织提供了多种编程语言的Selenium客户端库, 包括java,python,js, ruby等,方便不同编程语言的开发者使用。我们只需要安装好客户端库,调用这些库,就可以发出自动化请求给浏览器咯。
- 浏览器驱动也是一个独立的程序,是由浏览器厂商提供的,不同的浏览器需要不同的浏览器驱动。 比如 Chrome浏览器和火狐浏览器有 各自不同的驱动程序。
浏览器驱动接收到我们的自动化程序发送的界面操作请求后,会转发请求给浏览器,让浏览器去执行对应的自动化操作。浏览器执行完操作后,会将自动化的结果返回给浏览器驱动,浏览器驱动再通过HTTP响应的消息返回给我们的自动化程序的客户端库。自动化程序的客户端库 接收到响应后,将结果转化为数据对象返回给我们的代码。我们的程序就可以知道这次自动化操作的结果如何了。
selenium 自动化流程如下:
- 自动化程序调用Selenium客户端库函数(比如点击按钮元素)
- 客户端库会发送Selenium命令给浏览器的驱动程序
- 浏览器驱动程序接收到命令后,驱动浏览器去执行命令
- 浏览器执行命令
- 浏览器驱动程序获取命令执行的结果,返回给我们自动化程序
- 自动化程序对返回结果进行处理
安装
Selenium环境的安装主要安装两样东西: 客户端库和浏览器驱动 。
安装客户端库
不同的编程语言选择不同的Selenium客户端库。本文我们使用python,在命令行程序输入:pip install selenium
。或使用国内的豆瓣源:pip install selenium -i https://pypi.douban.com/simple/
安装浏览器驱动
简单示例
下面的代码可以自动的打开Chrome浏览器,并且自动化打开百度网站:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# 创建一个控制浏览器的对象,即WebDriver对象(可以把它理解为控制了浏览器的遥控器),指明使用chrome浏览器驱动,此时会运行浏览器驱动,并且运行Chrome浏览器。
# 等号右边返回的是WebDriver类型的对象,我们可以通过这个对象来操控浏览器,比如打开网址、选择界面元素等。
wd = webdriver.Chrome(service=Service(r'd:\tools\chromedriver.exe'))# 在python中,r表示\不代表转义字符
# 调用WebDriver对象的get方法 可以让浏览器打开指定网址
wd.get('https://www.baidu.com')
# 程序运行完会自动关闭浏览器,就是很多人说的闪退
# 这里加入等待用户输入,防止闪退
input('等待回车键结束程序')
省略浏览器驱动路径
前面,我们的代码创建WebDriver对象时,需要指定浏览器驱动路径,比如
from selenium.webdriver.chrome.service import Service
wd = webdriver.Chrome(service=Service(r'd:\tools\chromedriver.exe'))
这样写存在几个问题:
- 比较麻烦, 每次写自动化代码都 要指定路径。
- 如果你的代码给别人运行,他的电脑上存放浏览器驱动的路径不一定和你一样(比如他的电脑是苹果Mac电脑),得改脚本。
解决办法:把浏览器驱动所在目录(比如d:\tools)加入环境变量Path,写代码时,就可以无需指定浏览器驱动路径了,像下面这样:wd = webdriver.Chrome()
。因为,Selenium会自动在环境变量Path指定的那些目录里查找名为chromedriver.exe 的文件。设置完环境变量后,重启IDE(比如 PyCharm) 新的环境变量才会生效。
选择元素的基本方法
基本思想:告诉浏览器,你要操作的这个web元素的特征 。
方法:用chrome浏览器访问百度,按F12后,点击下图箭头处的Elements标签,即可查看页面对应的HTML 元素
然后,再点击最左边的图标,如下所示
之后,鼠标在界面上点击哪个元素,就可以查看该元素对应的html标签 了。
根据id属性选择元素
下面的代码,可以自动化在浏览器中访问我们的股票搜索网站,并且在输入框中搜索通讯 。
from selenium import webdriver
from selenium.webdriver.common.by import By
# 创建 WebDriver 对象
wd = webdriver.Chrome()
# 调用WebDriver 对象的get方法 可以让浏览器打开指定网址
wd.get('https://www.byhy.net/_files/stock1.html')
# 根据id选择元素,返回的就是该元素对应的WebElement对象,WebElement对象可以看成是对应页面元素的遥控器。
try:
element = wd.find_element(By.ID, 'kw')
element1 = wd.find_element(By.ID, 'kw1')
except NoSuchElementException:
print("元素不存在")
# 通过该 WebElement对象,就可以对页面元素进行操作了
element.send_keys('通讯\n')# 比如输入字符串到这个输入框里
element1.click()# 点击按钮
wd.quit()# 关闭浏览器
根据class属性、tag名选择元素
页面代码:
<body>
<div class="plant"><span>土豆</span></div>
<div class="plant"><span>洋葱</span></div>
<div class="plant"><span>白菜</span></div>
<div class="animal"><span>狮子</span></div>
<div class="animal"><span>老虎</span></div>
<div class="animal"><span>山羊</span></div>
</body>
所有的植物元素都有个class属性 值为 plant。所有的动物元素都有个class属性 值为 animal。如果我们要选择 所有的 动物, 就像下面可以这样写:wd.find_elements(By.CLASS_NAME, 'animal')
find_element和find_elements的区别:
- 使用find_elements选择的是符合条件的所有元素,如果没有符合条件的元素,返回空列表
- 使用find_element选择的是符合条件的第一个元素,如果没有符合条件的元素,抛出NoSuchElementException异常
from selenium import webdriver
from selenium.webdriver.common.by import By
# 创建 WebDriver 实例对象,指明使用chrome浏览器驱动
wd = webdriver.Chrome()
# WebDriver 实例对象的get方法 可以让浏览器打开指定网址
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# 根据 class name 选择元素,返回的是 一个列表,里面都是class属性值为animal的元素对应的WebElement对象
elements = wd.find_elements(By.CLASS_NAME, 'animal')
# 取出列表中的每个WebElement对象,打印出其text属性的值,text属性就是该WebElement对象对应的元素在网页中的文本内容
for element in elements:
print(element.text)# 获取该元素 在网页中的文本内容
通过指定参数为By.TAG_NAME
,选择所有的tag名为div的元素:
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# 根据 tag name 选择元素,返回的是 一个列表
# 里面 都是 tag 名为 div 的元素对应的 WebElement对象
elements = wd.find_elements(By.TAG_NAME, 'div')
# 取出列表中的每个 WebElement对象,打印出其text属性的值
# text属性就是该 WebElement对象对应的元素在网页中的文本内容
for element in elements:
print(element.text)
通过WebElement对象选择元素
WebDriver对象选择元素的范围是整个web页面, 而WebElement对象选择元素的范围是 该元素的内部。
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
element = wd.find_element(By.ID,'container')
# 限制 选择元素的范围是 id 为 container 元素的内部。
spans = element.find_elements(By.TAG_NAME, 'span')
for span in spans:
print(span.text)
输出结果就只有:
内层11
内层12
内层21
等待界面元素出现
在我们进行网页操作的时候, 有的元素内容不是可以立即出现的,可能会等待一段时间。比如我们的股票搜索示例页面,搜索一个股票名称,我们点击搜索后,浏览器需要把这个搜索请求发送给服务器,服务器进行处理后,再把搜索结果返回给我们。有以下两种解决方案:
- 我们可以用
sleep
来等待几秒钟, 等百度服务器返回结果后,再去选择 id 为1 的元素
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://www.byhy.net/_files/stock1.html')
element = wd.find_element(By.ID, 'kw')
element.send_keys('通讯\n')
# 等待 1 秒
from time import sleep
sleep(1)
element = wd.find_element(By.ID,'1')
print(element.text)
但是等待时间的长短不好确定
- 当发现元素没有找到的时候, 并不立即返回找不到元素的错误。而是周期性(每隔半秒钟)重新寻找该元素,直到该元素找到,或者超出指定最大等待时长,这时才 抛出异常(如果是 find_elements 之类的方法, 则是返回空列表)。Selenium 的 Webdriver 对象 有个方法叫 implicitly_wait ,可以称之为 隐式等待 ,或者 全局等待。该方法接受一个参数, 用来指定 最大等待时长。
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
# 后续所有的find_element或者find_elements之类的方法调用都会采用上面的策略:如果找不到元素,每隔半秒钟再去界面上查看一次,直到找到该元素,或者过了10秒最大时长。
wd.implicitly_wait(10)
wd.get('https://www.byhy.net/_files/stock1.html')
element = wd.find_element(By.ID, 'kw')
element.send_keys('通讯\n')
# 返回页面 ID为1 的元素
element = wd.find_element(By.ID,'1')
print(element.text)
操控元素的基本方法
操控元素通常包括
- 点击元素
- 在元素中输入字符串,通常是对输入框这样的元素
- 获取元素包含的信息,比如文本内容,元素的属性
点击元素
当我们调用 WebElement 对象的 click 方法去点击 元素的时候, 浏览器接收到自动化命令,点击的是该元素的 中心点 位置 。
输入框
调用元素WebElement对象的 click方法,如果我们要 把输入框中已经有的内容清除掉,可以使用WebElement对象的clear方法
element = wd.find_element(By.ID, "input1")
element.clear() # 清除输入框已有的字符串
element.send_keys('白月黑羽') # 输入新字符串
获取元素信息
获取元素的文本内容
通过WebElement对象的 text 属性,可以获取元素 展示在界面上的 文本内容。
element = wd.find_element(By.ID, 'animal')
print(element.text)
获取元素属性
通过WebElement对象的 get_attribute 方法来获取元素的属性值,比如要获取元素属性class的值,就可以使用element.get_attribute('class')
element = wd.find_element(By.ID, 'input_name')
print(element.get_attribute('class'))
执行完自动化代码,如果想关闭浏览器窗口可以调用WebDriver对象的quit方法:wd.quit()
获取整个元素对应的HTML
要获取整个元素对应的HTML文本内容,可以使用element.get_attribute('outerHTML')
,如果,只是想获取某个元素内部的HTML文本内容,可以使用element.get_attribute('innerHTML')
获取输入框里面的文字
对于input输入框的元素,要获取里面的输入文本,这时可以使用 element.get_attribute('value')
element = wd.find_element(By.ID, "input1")
print(element.get_attribute('value')) # 获取输入框中的文本
通过WebElement对象的text属性,可以获取元素展示在界面上的文本内容。但是,有时候,元素的文本内容没有展示在界面上,或者没有完全完全展示在界面上。 这时,用WebElement对象的text属性,获取文本内容,就会有问题,我们可以尝试使用element.get_attribute('innerText')
,或者 element.get_attribute('textContent')
使用innerText和textContent的区别是,前者只显示元素可见文本内容,后者显示所有内容(包括display属性为none的部分)
css表达式
通过 CSS Selector 选择单个元素的方法是:find_element(By.CSS_SELECTOR, CSS Selector参数)
,选择所有元素的方法是:find_elements(By.CSS_SELECTOR, CSS Selector参数)
根据tag名、id、class选择元素
一、根据tag名选择元素直接写上tag名即可,比如要选择所有的tag名为div的元素:elements = wd.find_elements(By.CSS_SELECTOR, 'div')
,等价于elements = wd.find_elements(By.TAG_NAME, 'div')
二、根据id属性选择元素的语法是在id号前面加上一个井号: #id值
,假如页面为:<input type="text" id='searchtext' />
,我们想在id为searchtext的输入框中输入文本【你好】
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
element = wd.find_element(By.CSS_SELECTOR, '#searchtext')
element.send_keys('你好')
三、根据class属性选择元素的语法是在class值前面加上一个点: .class值
,假如页面代码为:
<body>
<div class="plant"><span>土豆</span></div>
<div class="plant"><span>洋葱</span></div>
<div class="plant"><span>白菜</span></div>
<div class="animal"><span>狮子</span></div>
<div class="animal"><span>老虎</span></div>
<div class="animal"><span>山羊</span></div>
</body>
要选择所有class属性值为animal的元素动物除了这样写:elements = wd.find_elements(By.CLASS_NAME, 'animal')
,还可以这样写:elements = wd.find_elements(By.CSS_SELECTOR, '.animal')
选择子元素和后代元素
页面代码:
<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>
说明:
- id为container的div元素包含了id为layer1和layer2的两个div元素,这种包含是直接包含, 中间没有其他的层次的元素了,所以id为layer1和layer2的两个div元素是id为container的div元素的直接子元素。同理,id为inner11和inner12的两个div元素是id为layer1的div元素的直接子元素;id为inner21这个div元素是id为layer2的div元素的直接子元素
- 对于id为container的div元素来说,id为inner11、inner12、inner22的元素和两个span类型的元素都不是它的直接子元素, 因为中间隔了几层,但是它们还是在container的内部, 可以称之为它的后代元素
后代元素也包括了直接子元素,比如id为layer1和layer2的两个div元素,也可以说是id为container的div元素的直接子元素,同时也是后代子元素
如果元素2是元素1的直接子元素,CSS Selector选择直接子元素的语法是这样的:元素1 > 元素2
,也支持更多层级的选择:元素1 > 元素2 > 元素3 > 元素4
,就是选择元素1里面的子元素元素2里面的子元素元素3里面的子元素元素4,最终选择的元素是元素4
如果元素2是元素1的后代元素,CSS Selector选择后代元素的语法是这样的:元素1 元素2
,中间是一个或者多个空格隔开,也支持更多层级的选择, 比如元素1 元素2 元素3 元素4
,最终选择的元素是 元素4
根据属性选择
css 选择器支持通过任何属性来选择元素,语法是用一个方括号[]
,假如想根据href选择<a href="http://www.miitbeian.gov.cn">苏ICP备88885574号</a>
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://cdn2.byhy.net/files/selenium/sample1.html')
# 根据属性选择元素
element = wd.find_element(By.CSS_SELECTOR, '[href="http://www.miitbeian.gov.cn"]')# 属性值用单引号,双引号都可以。
# 打印出元素对应的html
print(element.get_attribute('outerHTML'))
说明:
- 前面可以加上标签名的限制,比如
div[class='SKnet']
表示选择所有标签名为div,且class属性值为SKnet的元素。 - 根据属性选择,还可以不指定属性值,比如
[href]
, 表示选择所有具有属性名为href
的元素,不管它们的值是什么。 - CSS还支持正则表达式
(1)选择属性值包含某个字符串的元素,比如要选择a节点,里面的href属性包含了miitbeian字符串:a[href*="miitbeian"]
(2)选择属性值以某个字符串开头的元素,比如要选择a节点,里面的href属性以http开头:a[href^="http"]
(3)选择属性值以某个字符串结尾的元素,比如要选择a节点,里面的href属性以gov.cn结尾:a[href$="gov.cn"]
- 如果一个元素具有多个属性,如
<div class="misc" ctype="gun">沙漠之鹰</div>
,CSS选择器 可以指定选择的元素要同时具有多个属性的限制:div[class=misc][ctype=gun]
验证CSS Selector
假如我们要验证#bottom > .footer2 a
是否选中<a href="http://www.miitbeian.gov.cn">苏ICP备88885574号</a>
,使用开发者工具,点击Elements标签后, 同时按Ctrl+F,就会出现下图箭头处的搜索框
我们可以在里面输入任何CSS Selector表达式,如果能选择到元素, 右边的的红色方框里面就会显示出类似2of3这样的内容。
选择语法联合使用
如果我们要同时选择所有class为plant和class为animal的元素,可以使用逗号,这称之为组选择:.plant , .animal
。再比如,我们要同时选择所有tag名为div的元素和id为BYHY的元素,就可以像这样写:div,#BYHY
,对应的selenium代码如下
elements = wd.find_elements(By.CSS_SELECTOR, 'div,#BYHY')
for element in elements:
print(element.text)
如果我们要选择所有id为t1里面的span和p元素:#t1 > span , #t1 > p
按次序选择子节点
html代码如下:
<body>
<div id='t1'>
<h3> 唐诗 </h3>
<span>李白</span>
<p>静夜思</p>
<span>杜甫</span>
<p>春夜喜雨</p>
</div>
<div id='t2'>
<h3> 宋词 </h3>
<span>苏轼</span>
<p>赤壁怀古</p>
<p>明月几时有</p>
<p>江城子·乙卯正月二十日夜记梦</p>
<p>蝶恋花·春景</p>
<span>辛弃疾</span>
<p>京口北固亭怀古</p>
<p>青玉案·元夕</p>
<p>西江月·夜行黄沙道中</p>
</div>
</body>
父元素的第n个子节点
frame切换/窗口切换
切换到frame
如果我们要选择上图所有class="plant"
的标签,这些元素是在一个叫iframe的元素中的。在html语法中,frame元素或者iframe元素的内部会包含一个被嵌入的另一份html文档。在我们使用selenium打开一个网页时,我们的操作范围是当前的html, 并不包含被嵌入的html文档里面的内容,如果我们要操作被嵌入的html文档中的元素,就必须切换操作范围到被嵌入的文档中:使用WebDriver对象的switch_to属性:wd.switch_to.frame(frame_reference)
,其中,frame_reference可以是frame元素的属性、name或者ID,比如这里,就可以填写iframe元素的id值‘frame1’或者name属性值‘innerFrame’:wd.switch_to.frame('frame1')
或者wd.switch_to.frame('innerFrame')
,也可以填写frame所对应的WebElement对象。我们可以根据frame的元素位置或者属性特性,使用find系列的方法,选择到该元素,得到对应的WebElement对象:wd.switch_to.frame(wd.find_element(By.TAG_NAME, "iframe"))
,然后,就可以进行后续操作frame里面的元素了。如果我们已经切换到某个iframe里面进行操作了,那么后续选择和操作界面元素 就都是在这个frame里面进行的。这时候,如果我们又需要操作 主html(我们把最外部的html称之为主html) 里面的元素了呢?怎么切换回原来的主html呢?使用:wd.switch_to.default_content()
。操作完 frame里面的元素后, 需要 点击 主html 里面的按钮,完整代码就可以这样写:
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Chrome()
wd.get('https://cdn2.byhy.net/files/selenium/sample2.html')
# 先根据name属性值 'innerFrame',切换到iframe中
wd.switch_to.frame('innerFrame')
# 根据 class name 选择元素,返回的是 一个列表
elements = wd.find_elements(By.CLASS_NAME, 'plant')
for element in elements:
print(element.text)
# 切换回 最外部的 HTML 中
wd.switch_to.default_content()
# 然后再 选择操作 外部的 HTML 中 的元素
wd.find_element_by_id('outerbutton').click()
wd.quit()
切换到新的窗口
点击一个链接 或者 按钮,就会打开一个 新窗口 。在打开的网页中,点击 链接 “访问bing网站” , 就会弹出一个新窗口,即使新窗口打开了, 我们的 WebDriver对象对应的还是老窗口,自动化操作也还是在老窗口进行,如果我们要到新的窗口里面操作,可以使用Webdriver对象的switch_to属性的window方法:wd.switch_to.window(handle)
,handle需要传入什么呢?
WebDriver对象有window_handles属性,是一个列表对象, 里面包括了当前浏览器里面所有的窗口句柄(可以把句柄想象成对应网页窗口的一个ID),那么我们就可以通过 类似下面的代码,
for handle in wd.window_handles:
# 先切换到该窗口
wd.switch_to.window(handle)
# 得到该窗口的标题栏字符串,判断是不是我们要操作的那个窗口
if 'Bing' in wd.title:
# 如果是,那么这时候WebDriver对象就是对应的该该窗口,正好,跳出循环,
break
上述代码依次获取wd.window_handles
里面的所有句柄对象,并且调用 wd.switch_to.window(handle)
方法切入到每个窗口,然后检查里面该窗口对象的属性(可以是标题栏,地址栏),判断是不是我们要操作的那个窗口,如果是,就跳出循环。同样的,如果我们在新窗口 操作结束后,怎么回到原来的窗口?我们可以仍然使用上面的方法,依次切入窗口,然后根据标题栏之类的属性值判断。还有更省事的方法,因为我们一开始就在原来的窗口里面,我们知道进入新窗口操作完后,还要回来,可以事先保存该老窗口的句柄,使用如下方法
# mainWindow变量保存当前窗口的句柄
mainWindow = wd.current_window_handle
切换到新窗口操作完后,就可以直接像下面这样,将driver对应的对象返回到原来的窗口
#通过前面保存的老窗口的句柄,自己切换到老窗口
wd.switch_to.window(mainWindow)
选择框
radio框
直接用WebElement的click方法,模拟用户点击即可。比如,我们要在下面的html中:
<div id="s_radio">
<input type="radio" name="teacher" value="小江老师">小江老师<br>
<input type="radio" name="teacher" value="小雷老师">小雷老师<br>
<input type="radio" name="teacher" value="小凯老师" checked="checked">小凯老师
</div>
先打印当前选中的老师名字,再选择小雷老师,对应的代码如下
# 获取当前选中的元素
element = wd.find_element(By.CSS_SELECTOR,
'#s_radio input[name="teacher"]:checked')# :checked表示选择checked状态的元素,对 radio和checkbox类型的input有效
print('当前选中的是: ' + element.get_attribute('value'))
# 点选 小雷老师
wd.find_element(By.CSS_SELECTOR,
'#s_radio input[value="小雷老师"]').click()
checkbox框
直接用WebElement的click方法,模拟用户点击选择。需要注意的是,要选中checkbox的一个选项,必须先获取当前该复选框的状态,如果该选项已经勾选了,就不能再点击,否则反而会取消选择。比如我们要在下面的html中
<div id="s_checkbox">
<input type="checkbox" name="teacher" value="小江老师">小江老师<br>
<input type="checkbox" name="teacher" value="小雷老师">小雷老师<br>
<input type="checkbox" name="teacher" value="小凯老师" checked="checked">小凯老师
</div>
选中小雷老师:
# 先把 已经选中的选项全部点击一下
elements = wd.find_elements(By.CSS_SELECTOR,
'#s_checkbox input[name="teacher"]:checked')
for element in elements:
element.click()
# 再点击 小雷老师
wd.find_element(By.CSS_SELECTOR,
"#s_checkbox input[value='小雷老师']").click()
select框
对于Select选择框,Selenium专门提供了一个Select类进行操作。Select类提供了如下的方法
select_by_value
:根据选项的value属性值选择元素。比如针对<option value="foo">Bar</option>
,就可以根据foo这个值选择该选项:s.select_by_value('foo')
select_by_index
:根据选项的次序(从0开始)选择元素select_by_visible_text
:根据选项的可见文本选择元素。比如针对<option value="foo">Bar</option>
,就可以根据Bar这个内容,选择该选项s.select_by_visible_text('Bar')
deselect_by_value
:根据选项的value属性值, 去除选中元素deselect_by_index
:根据选项的次序,去除选中元素deselect_by_visible_text
:根据选项的可见文本,去除选中元素deselect_all
:去除选中所有元素
Select单选框
不管原来选的是什么,直接用Select方法选择即可,比如要选择示例里面的小雷老师:
# 导入Select类
from selenium.webdriver.support.ui import Select
# 创建Select对象
select = Select(wd.find_element(By.ID, "ss_single"))
# 通过 Select 对象选中小雷老师
select.select_by_visible_text("小雷老师")
Select多选框
对于select多选框,要选中某几个选项,要注意去掉原来已经选中的选项。例如,我们选择示例多选框中的小雷老师和小凯老师,可以用select类的deselect_all方法,清除所有已经选中的选项。然后再通过select_by_visible_text方法选择小雷老师和小凯老师:
# 导入Select类
from selenium.webdriver.support.ui import Select
# 创建Select对象
select = Select(wd.find_element(By.ID, "ss_multi"))
# 清除所有 已经选中 的选项
select.deselect_all()
# 选择小雷老师 和 小凯老师
select.select_by_visible_text("小雷老师")
select.select_by_visible_text("小凯老师")
实战技巧
更多操作
如果想要进行鼠标右键点击、双击、移动鼠标到某个元素、鼠标拖拽等操作,可以通过Selenium提供的ActionChains类来实现。百度首页的右上角有个更多产品选项:
如果我们把鼠标放在上边,就会弹出下面的糯米、音乐、图片等图标。使用ActionChains 来模拟鼠标移动操作的代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('https://www.baidu.com/')
from selenium.webdriver.common.action_chains import ActionChains
ac = ActionChains(driver)
# 鼠标移动到 元素上
ac.move_to_element(
driver.find_element(By.CSS_SELECTOR, '[name="tj_briicon"]')
).perform()
直接执行javascript
我们可以直接让浏览器运行一段javascript代码,并且得到返回值:
# 直接执行 javascript,里面可以直接用return返回我们需要的数据
nextPageButtonDisabled = driver.execute_script(
'''
ele = document.querySelector('.soupager > button:last-of-type');
return ele.getAttribute('disabled')
''')
# 返回的数据转化为Python中的数据对象进行后续处理
if nextPageButtonDisabled == 'disabled': # 是最后一页
return True
else: # 不是最后一页
return False
有时,自动化的网页内容很长,或者很宽,超过一屏显示,如果我们要点击的元素不在窗口可见区内,新版本的selenium协议,浏览器发现要操作(比如点击操作)的元素,不在可见区内,往往会操作失败,这时可以调用execute_script直接执行js代码,让该元素出现在窗口可见区正中:driver.execute_script("arguments[0].scrollIntoView({block:'center',inline:'center'})", job)
,其中arguments[0]
,就指代了后面的第一个参数job对应的js对象,js对象的scrollIntoView方法,就是让元素滚动到可见部分
block:‘center’ 指定垂直方向居中
inline:‘center’ 指定水平方向居中
冻结界面
比如我们前面讲的百度首页的右上角,有个更多产品选项,如果我们把鼠标放在上边,就会弹出下面的糯米、音乐、图片等图标。如果我们要用selenium自动化点击糯米图标,就需要F12查看这个元素的特征。但是当我们的鼠标从糯米图标移开, 这个栏目就整个消失了, 就没法查看其对应的HTML。我们可以在浏览器的console中输入以下代码:setTimeout(function(){debugger}, 5000)
并回车,执行后, 立即将鼠标放在界面右上角更多产品处。这时候,就会弹出下面的糯米、音乐、图片等图标。然后,我们仔细等待5秒到了以后,界面就会因为执行了debugger命令而被冻住。然后,我们就可以点击开发者工具栏的查看箭头, 再去点击糯米图标,查看其属性了。
弹出对话框
alert
自动化的时候,代码模拟用户点击OK按钮:driver.switch_to.alert.accept()
,程序获取弹出对话框中的信息内容:driver.switch_to.alert.text
,完整示例代码:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('https://cdn2.byhy.net/files/selenium/test4.html')
# --- alert ---
driver.find_element(By.ID, 'b1').click()
# 打印 弹出框 提示信息
print(driver.switch_to.alert.text)
# 点击 OK 按钮
driver.switch_to.alert.accept()
Confirm
selenium提供如下方法模拟用户点击 OK 或者 Cancel 按钮:
- 点击OK按钮,还是用刚才的accept方法:
driver.switch_to.alert.accept()
- 点击Cancel按钮,可以用dismiss方法:
driver.switch_to.alert.dismiss()
完整示例代码:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('https://cdn2.byhy.net/files/selenium/test4.html')
# --- confirm ---
driver.find_element(By.ID, 'b2').click()
# 打印 弹出框 提示信息
print(driver.switch_to.alert.text)
# 点击 OK 按钮
driver.switch_to.alert.accept()
driver.find_element(By.ID, 'b2').click()
# 点击 取消 按钮
driver.switch_to.alert.dismiss()
Prompt
Prompt弹出框是需要用户输入一些信息并提交上去,调用driver.switch_to.alert.send_keys('这里面写要提交的内容')
即可,完整示例代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('https://cdn2.byhy.net/files/selenium/test4.html')
# --- prompt ---
driver.find_element(By.ID, 'b3').click()
# 获取 alert 对象
alert = driver.switch_to.alert
# 打印 弹出框 提示信息
print(alert.text)
# 输入信息,并且点击 OK 按钮 提交
alert.send_keys('web自动化 - selenium')
alert.accept()
# 点击 Cancel 按钮 取消
driver.find_element(By.ID, 'b3').click()
alert = driver.switch_to.alert
alert.dismiss()
有些弹窗并非浏览器的alert窗口,而是html元素,此时只需要通过之前介绍的选择器选中并进行相应的操作就可以了。
其他技巧
窗口
有时间我们需要获取窗口的属性和相应的信息,并对窗口进行控制
窗口大小
获取窗口大小:driver.get_window_size()
改变窗口大小:driver.set_window_size(x, y)
获取当前窗口标题
使用WebDriver的title属性来获取当前窗口的标题栏字符串:driver.title
获取当前窗口URL地址:driver.current_url
例如,访问网易,并获取当前窗口的标题和URL
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(5)
# 打开网站
driver.get('https://www.163.com')
# 获取网站标题栏文本
print(driver.title)
# 获取网站地址栏文本
print(driver.current_url)
截屏
把浏览器屏幕内容保存为图片文件,比如,做自动化测试时,一个测试用例检查点发现错误,我们可以截屏为文件,以便测试结束时进行人工核查。可以使用WebDriver的get_screenshot_as_file
方法来截屏并保存为图片:
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(5)
# 打开网站
driver.get('https://www.baidu.com/')
# 截屏保存为图片文件
driver.get_screenshot_as_file('1.png')
手机模式
我们可以通过desired_capabilities参数,指定以手机模式打开chrome浏览器,参考代码如下:
from selenium import webdriver
mobile_emulation = { "deviceName": "Nexus 5" }
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
driver = webdriver.Chrome( desired_capabilities = chrome_options.to_capabilities())
driver.get('http://www.baidu.com')
input()
driver.quit()
上传文件
网站页面上传文件的功能是通过type属性为file的HTML input元素实现的:<input type="file" multiple="multiple">
。使用selenium自动化上传文件,我们只需要定位到该input元素,然后通过send_keys
方法传入要上传的文件路径即可:
# 先定位到上传文件的 input 元素
ele = wd.find_element(By.CSS_SELECTOR, 'input[type=file]')
# 再调用 WebElement 对象的 send_keys 方法
ele.send_keys(r'h:\g02.png')
如果需要上传多个文件,可以多次调用send_keys:
ele = wd.find_element(By.CSS_SELECTOR, 'input[type=file]')
ele.send_keys(r'h:\g01.png')
ele.send_keys(r'h:\g02.png')
但是,有的网页上传,是没有file类型的input元素的。如果是Windows上的自动化,可以采用 Windows 平台专用的方法:执行pip install pypiwin32
,确保 pywin32 已经安装,然后参考如下示例代码:
# 找到点击上传的元素,点击
driver.find_element(By.CSS_SELECTOR, '.dropzone').click()
sleep(2) # 等待上传选择文件对话框打开
# 直接发送键盘消息给 当前应用程序,
# 前提是浏览器必须是当前应用
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
# 输入文件路径,最后的'\n',表示回车确定,也可能时 '\r' 或者 '\r\n'
shell.Sendkeys(r"h:\a2.png" + '\n')
sleep(1)
Xpath选择器
XPath (XML Path Language) 是由国际标准化组织W3C指定的,用来在 XML 和 HTML 文档中选择节点的语言。