强大的库selenium
官网:Selenium
文档:
Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium 可以直接运行在浏览器上,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器)。
Selenium 可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。
注意:本笔记默认读者会CSS,HTML等基础的Web语言
安装
pip install selenium
快速入门
# IPython2 测试代码
# 导入 webdriver
from selenium import webdriver
# 要想调用键盘按键操作需要引入keys包
from selenium.webdriver.common.keys import Keys
# 调用环境变量指定的PhantomJS浏览器创建浏览器对象
driver = webdriver.PhantomJS()
# 如果没有在环境变量指定PhantomJS位置
# driver = webdriver.PhantomJS(executable_path="./phantomjs"))
# get方法会一直等到页面被完全加载,然后才会继续程序,通常测试会在这里选择 time.sleep(2)
driver.get("http://www.baidu.com/")
# 获取页面名为 wrapper的id标签的文本内容
data = driver.find_element_by_id("wrapper").text
# 打印数据内容
print data
# 打印页面标题 "百度一下,你就知道"
print driver.title
# 生成当前页面快照并保存
driver.save_screenshot("baidu.png")
# id="kw"是百度搜索输入框,输入字符串"长城"
driver.find_element_by_id("kw").send_keys(u"马云")
# id="su"是百度搜索按钮,click() 是模拟点击
driver.find_element_by_id("su").click()
# 获取新的页面快照
driver.save_screenshot("马云.png")
# 打印网页渲染后的源代码
print driver.page_source
# 获取当前页面Cookie
print driver.get_cookies()
# 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')
# 输入框重新输入内容
driver.find_element_by_id("kw").send_keys(u"王健林")
# 模拟Enter回车键
driver.find_element_by_id("su").send_keys(Keys.RETURN)
# 清除输入框内容
driver.find_element_by_id("kw").clear()
# 生成新的页面快照
driver.save_screenshot("王健林.png")
# 获取当前url
print driver.current_url
# 关闭当前页面,如果只有一个页面,会关闭浏览器
# driver.close()
# 关闭浏览器
driver.quit()
引用Selenium
使用from selenium import webdriver
和from selenium.webdriver.common.keys import Keys
引用Selenium和Selenium keys(用于使用特定的按键,比如说F2等等)包
API
初始化Selenium
首先安装浏览器驱动,笔者用的是Edge和Chrome(这个用的最多),这两个驱动分别在Microsoft Edge Driver - Microsoft Edge Developer和chromedriver存储目录(由于114以上版本不更新,需要进入最新的chromedriver地址https://chromedriver.com/download下载driver)
下载,下载方法如下:
Edge
进入网页,找到Stable Channel,根据自己的系统位数选择下载即可。
Chrome
首先查看自己Chrome的版本号,在Chrome输入chrome://version
即可查看Chrome的版本号
进入网页,找到相应的版本号后,点击下载即可
保存好浏览器驱动后,使用如下代码,让Selenium找到相应的驱动后即可开启浏览器
driver = webdriver.Chrome("xxx/browserDriver/chromedriver.exe")# Chrome 浏览器驱动存放路径
driver = webdriver.Edge("xxx/browserDriver/msedgedriver.exe")# Edge 浏览器驱动存放路径
也可以将浏览器驱动存放在python的根目录下,不用输入存放路径
driver = webdriver.Chrome()
driver = webdriver.Edge()
这样,Selenium初始化完毕。
可以使用headless无UI进行selenium,通常运用在爬虫中,特别的,headless模式通常运用在Chrome浏览器中
# Chrome后台静默运行
__options = webdriver.ChromeOptions()
__options.add_argument('--no-sandbox') # 解决DevToolsActivePort文件不存在的报错
__options.add_argument('--start-maximized') # 最大化运行(全屏窗口),不设置,取元素会报错
__options.add_argument('--disable-infobars') # 禁用浏览器正在被自动化程序控制的提示
__options.add_argument('window-size=1920x1080') # 设置浏览器分辨率
__options.add_argument('--disable-gpu') # 谷歌文档提到需要加上这个属性来规避bug
__options.add_argument('--hide-scrollbars') # 隐藏滚动条,应对一些特殊页面
__options.add_argument('blink-settings=imagesEnabled=false') # 不加载图片,提升运行速度
__options.add_argument('--headless') # 浏览器不提供可视化界面。Linux下如果系统不支持可视化不加这条会启动失败
__options.add_argument("--ignore-certificate-errors") # 忽视掉证书认证的不安全连接的错误
# 传入user-agent,欺骗网站,使得它认为是在实际的浏览器上运行,这样的话运行的网络速度是和UI运行的时候是一样的
__options.add_argument('user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"')
__options.add_argument("--ignore-ssl-errors")
__driver = webdriver.Chrome(executable_path = "./browserDriver/chromedriver.exe",options=__options)# Chrome 浏览器驱动存放路径
user-agent可以通过在Chrome浏览器中输入chrome://version
的用户代理查找到,这里笔者使用的是最新的chrome浏览器(作为自动化工具)
打开网址
driver.get("https://www.baidu.com/")
定位UI元素
引用selenium教程 - 简书 (jianshu.com),有少量修改
通过如下选择器可以精准找到网页的HTML元素和标签。
find_elements_by_id
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
示例:
driver.find_element_by_id("txtMobileTexter").send_keys("12345")# 通过id获取元素,发送字符12345
driver.find_element_by_xpath("//*[@id='mobileSelSend").click()# 通过xpath获取元素,并点击
driver.find_element_by_link_text("发送").text# 通过元素中的字符获取元素获取文本
# 获取指定元素,并判断是否存在
try:
driver.find_element_by_xpath("/html/body/div[1]/div[1]/div[2]/div[2]/span")
print("成功")
except:
print("失败")
其中,find_element_by_xxxx
和find_elements_by_xxxx
的区别是,find_element_by_xxxx
是适用于某一个页面中的某个元素,而find_elements_by_xxxx
中可能包含多个元素,返回的是list,例如:
driver.find_element_by_id("txtMobileTexter").click() # 点击id为txtMobileTexter的这个元素
driver.find_elements_by_id("txtMobileTexter")[0].click() # 点击id为txtMobileTexter的第一个元素
find_element_by_xxxx
或者find_elements_by_xxxx
新版本的selenium已不适用,取而代之的是find_element(by=By.xxx, value=value)
或find_elements(by=By.xxx, value=value)
,其作用相当于find_element_by_xxxx
或者find_elements_by_xxxx
。需要导入By
这个包,具体为:from selenium.webdriver.common.by import By
xpath说明:
符号 | 说明 |
---|---|
/ | 从根节点选取,使用绝对路径,路径必须完全匹配 |
// | 从整个文档中选取,使用相对路径 |
. | 从当前节点开始选取 |
… | 从当前节点父节点开始选取 |
@ | 选取属性 |
text() | 获取文本 |
案例
路径表达式 | 结果 |
---|---|
body | 选取 body 元素的所有子节点。 |
/head | 选取根元素下head。假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
div/a | 选取属于 div 的子元素的所有 a 元素。 |
//a | 选取所有 a 子元素,而不管它们在文档中的位置。 |
div//a | 选择属于 div 元素的后代的所有 a 元素,而不管它们位于 bookstore 之下的什么位置。 |
//@class | 选取名为 class 的所有属性。 |
./a | 选取当前元素下的a |
…/a | 选取父元素下的a |
a/@href | 选取a标签的href属性 |
a/text() | 选取a标签下的文本 |
鼠标动作链
以下代码和文字(包括本小章节到页面等待章节前)引用了网址selenium教程 - 简书 (jianshu.com),有少量修改
引入鼠标动作库from selenium.webdriver import ActionChains
这样就可以使用复杂的鼠标动作了。
# 鼠标移动到 ac 位置
ac = driver.find_element_by_xpath('element')
ActionChains(driver).move_to_element(ac).perform()
# 在 ac 位置单击
ac = driver.find_element_by_xpath("elementA")
ActionChains(driver).move_to_element(ac).click(ac).perform()
# 在 ac 位置双击
ac = driver.find_element_by_xpath("elementB")
ActionChains(driver).move_to_element(ac).double_click(ac).perform()
# 在 ac 位置右击
ac = driver.find_element_by_xpath("elementC")
ActionChains(driver).move_to_element(ac).context_click(ac).perform()
# 在 ac 位置左键单击按住
ac = driver.find_element_by_xpath('elementF')
ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform()
# 将 elementD 拖拽到 elementE') 位置
ac1 = driver.find_element_by_xpath('elementD')
ac2 = driver.find_element_by_xpath('elementE')
ActionChains(driver).drag_and_drop(ac1, ac2).perform()
如果是普通的点击,也可以直接使用如下代码点击,不用ActionChains类
driver.find_element_by_xpath("//button[@type='button']").click()
选择表单
若碰到<select> </select>标签的下拉框,需要使用引用from selenium.webdriver.support.ui import Select
。代码如下:
# 找到 name 的选项卡
select = Select(driver.find_element_by_name('status'))
#
select.select_by_index(1)
select.select_by_value("0")
select.select_by_visible_text(u"未审核")
以上是三种选择下拉框的方式,它可以根据索引来选择,可以根据值来选择,可以根据文字来选择。
index 索引从 0 开始
value是option标签的一个属性值,并不是显示在下拉框中的值
visible_text是在option标签文本的值,是显示在下拉框的值
全部取消选择的代码如下:
select.deselect_all()
上传文件
首先区分出上传按钮的种类,大体上可以分为两种
一种是input框
另外一种就比较复杂,通过js、flash等实现,标签非input
input上传
该方法大概针对该类型的html:
<input type="file" name="file" id="file" />
遇到该类型的html,selenium可以十分简单粗暴得上传图片:
upload = driver.find_element_by_id('file')
upload.send_keys('d:\\all_money.wmv') # send_keys
非input上传
这种上传千奇百怪,有用a标签的,有用div的,有用button的,有用object的,selenium没有办法通过直接在网页上处理掉这些上传,唯一的办法就是打开OS弹框,去处理弹框。
但是OS弹框涉及的层面已经不是selenium能解决的了,大体上有以下几种解决方案:
autoIT:借助外力,我们去调用其生成的au3或exe文件。
pywin32库:识别对话框句柄,进而操作
SendKeys库
keybd_event:跟pywin32库类似,但是是模拟按键,ctrl+a,ctrl+c, ctrl+v…
页面前进和后退
driver.forward() # 前进
driver.back() # 后退
页面切换
切换窗口
浏览器会有很多窗口,所以要有方法来实现窗口的切换
driver.switch_to.window("this is window name")
也可以使用 window_handles 方法来获取每个窗口的操作对象
for handle in driver.window_handles:
driver.switch_to.window(handle)
切换到iframe(通过页面的UI)
页面中可能会存在iframe元素,需要切换到iframe里面才可以让Selenium操作里面的内容
driver.switch_to.frame(driver.find_element_by_xpath("/html/body/div[3]/div[5]/div[1]/div[7]/iframe"))
弹窗处理
当触发了某个事件之后,页面出现了弹窗提示,处理如下代码提示或者获取提示信息方法
alert = driver.switch_to.alert()
alert.accept()# 点击弹窗的确认
alert.text# 获取弹窗文本内容
Cookies
获取页面每个Cookies值
for cookie in driver.get_cookies():
print "%s -> %s" % (cookie['name'], cookie['value'])
删除Cookies
# By name
driver.delete_cookie("CookieName")
# all
driver.delete_all_cookies()
页面等待
现在的网页越来越多采用了 Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。如果实际页面等待时间过长导致某个dom元素还没出来,但是代码直接使用了这个WebElement,那么就会抛出NullPointer的异常。
为了避免这种元素定位困难而且会提高产生 ElementNotVisibleException 的概率。所以 Selenium 提供了两种等待方式,一种是隐式等待,一种是显式等待。
隐式等待是等待特定的时间,显式等待指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常了。
显式等待
引入WebDriverWait
from selenium.webdriver.support.wait import WebDriverWait
WebDriverWait的API
WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None)
说明:
driver:浏览器驱动
timeout:最长超时时间,默认以秒为单位
poll_frequency:检测的间隔步长,默认为0.5s
ignored_exceptions:超时后的抛出的异常信息,默认抛出NoSuchElementExeception异常。
WebDriverWait通常与until()或者until_not()方法结合使用:
# 调用该方法提供的驱动程序作为参数,直到返回值为True
WebDriverWait(driver,10).until(method,message="")
# 调用该方法提供的驱动程序作为参数,直到返回值为False
WebDriverWait(driver,10).until_not(method,message="")
可以自定义等待条件:
#设置等待
wait = WebDriverWait(driver,10,0.5)
#使用匿名函数
wait.until(lambda diver:driver.find_element_by_id('kw'))
WebDriverWait与expected_conditions方法结合使用:
from selenium.webdriver.common.by import By
# WebDriverWait 库,负责循环等待
from selenium.webdriver.support.wait import WebDriverWait
# expected_conditions 类,负责条件出发
from selenium.webdriver.support import expected_conditions as EC
# 页面一直循环,直到 id="myDynamicElement" 出现
wait = WebDriverWait(driver,10)
element = wait.until(EC.presence_of_element_located((By.ID,"myDynamicElement")))
如果不写步长参数,程序默认会 0.5s 调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回。
expected_conditions
类提供的预期条件判断的方法如下表:
方法 | 说明 |
---|---|
title_is | 判断当前页面的 title 是否完全等于(==)预期字符串,返回布尔值 |
title_contains | 判断当前页面的 title 是否包含预期字符串,返回布尔值 |
presence_of_element_located | 判断某个元素是否被加到了 dom 树里,并不代表该元素一定可见 |
visibility_of_element_located | 判断元素是否可见(可见代表元素非隐藏,并且元素宽和高都不等于 0) |
visibility_of | 同上一方法,只是上一方法参数为locator,这个方法参数是 定位后的元素 |
presence_of_all_elements_located | 判断是否至少有 1 个元素存在于 dom 树中。举例:如果页面上有 n 个元素的 class 都是’wp’,那么只要有 1 个元素存在,这个方法就返回 True |
text_to_be_present_in_element | 判断某个元素中的 text 是否 包含 了预期的字符串 |
text_to_be_present_in_element_value | 判断某个元素中的 value 属性是否包含 了预期的字符串 |
frame_to_be_available_and_switch_to_it | 判断该 frame 是否可以 switch进去,如果可以的话,返回 True 并且 switch 进去,否则返回 False |
invisibility_of_element_located | 判断某个元素中是否不存在于dom树或不可见 |
element_to_be_clickable | 判断某个元素中是否可见并且可点击 |
staleness_of | 等某个元素从 dom 树中移除,注意,这个方法也是返回 True或 False |
element_to_be_selected | 判断某个元素是否被选中了,一般用在下拉列表 |
element_selection_state_to_be | 判断某个元素的选中状态是否符合预期 |
element_located_selection_state_to_be | 跟上面的方法作用一样,只是上面的方法传入定位到的 element,而这个方法传入 locator |
alert_is_present | 判断页面上是否存在 alert |
隐式等待
隐式等待比较简单,就是简单地设置一个等待时间,单位为秒。
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://www.xxxxx.com/loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")
图像验证码识别
很多网站上都有图像验证码,这是自动化测试的一大难题,这里用ddddorc
来识别网站的比较简单的图像验证码文字,但是一旦碰到识别准确率不高的情况下需要手动处理。
安装
pip install ddddocr
引用
import ddddocr
使用
import ddddocr
import time
begin=time.time()
ocr = ddddocr.DdddOcr()
with open('test.png', 'rb') as f:
img_bytes = f.read()
res = ocr.classification(img_bytes)
finish=time.time()
print("结果:")
print(res)
print("用时:%s 秒" % str(finish-begin))
Airtest-Selenium
简介
airtest-selenium库是基于selenium库的进一步封装,有如下几个特点:
对切换标签的界面进行了友好封装
支持图像识别功能
自动进行log记录(参考selenium-java的监听模式)
兼容selenium的原生api
注意:Airtest-Selenium暂时不兼容selenium4,需要将selenium版本更改为3.0的版本后才能使用,否则会报WebElement' object has no attribute '_w3c'
的错误,可以在pycharm中将selenium修改为3.x的版本
安装
pip install airtest-selenium
pip install pynput
引用
# 引入selenium的webdriver模块
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from airtest.core.api import *
from airtest_selenium.proxy import WebChrome
初始化
需要把Chrome浏览器的驱动放在python的根目录上,具体下载在Selenium中说明。注意,要引用from airtest_selenium.proxy import WebChrome
的接口
#创建一个实例,代码运行到这里,会打开一个chrome浏览器
driver = WebChrome()
api
driver.airtest_touch:airtest-selenium封装的图像点击
driver.airtest_touch(Template(......))
driver.assert_template:airtest-selenium封装的图像存在断言
driver.assert_template(Template(......), "请填写测试点")
snapshot:airtest-selenium封装的页面截图操作
driver.snapshot()
previous_tab:airtest-selnium封装的切换到上一个标签页的操作
driver.switch_to_previous_tab()
new_tab:airtest-selenium封装的切换到新打开标签页的操作
driver.switch_to_new_tab()
运行
python -m airtest run
或者与selenium相同,在main方法中运行
Ubuntu运行selenium
selenium不仅仅能在windows上运行,也能在linux上运行,这里用Ubuntu去运行。
安装chrome
sudo apt-get install libxss1 libappindicator1 libindicator7 # 安装依赖库
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb # 获取chrome安装包
sudo dpkg -i google-chrome*.deb # 开始安装
google-chrome --version # 查看版本
下载chromedriver
#执行如下命令(第一条用于获取最新版本号,第二条用于下载):
LATEST=$(wget -q -O - http://chromedriver.storage.googleapis.com/LATEST_RELEASE)
wget http://chromedriver.storage.googleapis.com/$LATEST/chromedriver_linux64.zip
# 解压
unzip chromedriver_linux64.zip
# 修改权限
chmod +x chromedriver
# 可以移动driver 省的指定位置
sudo mv chromedriver /usr/bin/
# 查看版本
./chromedriver --version
测试
from selenium import webdriver
# 如果没有将driver移动到 sudo mv chromedriver /usr/bin/ 要指定 driverpath
driverpath = ''
driver = webdriver.Chrome(executable_path=driverpath)
driver.get("https://www.baidu.com/")
print(driver.title)