目录
0. 上节回顾
- WebDriver的角度看,有8种元素定位策略
- 从浏览器的角度来看,有2种元素选择器:
- CSS选择器执行效率高
- XPath选择器使用效率高
- 底层通过JS实现,可以通过F12进行获取、调试
1. 定位失败的原因
元素交互过程中出错:
- 元素不存在
- 元素不可见
- 元素被遮挡
- 元素被禁用
如:元素被延迟渲染
with get_webdriver() as driver:
driver.get("http://118.24.147.95:8086/delay.html")
ele = driver.find_element(By.XPATH, '//*[@id="content"]/input')
print(ele)
selenium.common.exceptions.NoSuchElementException: Message: no such element:
Unable to locate element: {"method":"xpath","selector":"//*[@id="content"]/input"}
DEBUG秘技:
控制台输入 debugger 让网页停下来
#cdp:chrome devtools 协议
driver.execute_cdp_cmd("Debugger.enable", {}) # JS断点 selenium 4 新特性 (执行的慢的话有概率失败)
driver.execute_script("setTimeout('debugger',0.1*1000)") #JS断电 selenium 3
pdb.set_trace()#python 断点
解决了定位失败的问题
with get_webdriver() as driver:
driver.get("http://118.24.147.95:8086/delay.html")
# cdp:chrome devtools 协议
driver.execute_cdp_cmd("Debugger.enable", {}) # JS断点 selenium 4 新特性 (执行的慢的话有概率失败)
driver.execute_script("setTimeout('debugger',0.1*1000)") # JS断电 selenium 3
pdb.set_trace() # python 断点
ele = driver.find_element(By.XPATH, '//*[@id="content"]/input')
print(ele)
> f:\pythonworkspace\ui_p39_selenium4\1.py(18)<module>()
-> ele = driver.find_element(By.XPATH, '//*[@id="content"]/input')
(Pdb)
通过
DEBUG
,发现很多的元素是通过
JS
动态创建、改变、修改的,如果要定位准许,需要
“
等待
”JS
完成
相关的操作:
- 强制等待 sleep
- 隐式等待 implicitly_wait
- 显式等待 WebDriverWait
- 流畅等待 FluentWait
2. 强制等待
sleep
实际上编程语言中的语句:当程序执行到
sleep
指令时,会暂停
x
秒,然后再继续执行
import time
time.sleep(6) # 定位代码,延迟6秒再执行
- 以秒为单位
- 可以是浮点数:(0.1秒)
使用方法简单粗暴,但是相对呆板,应用场景不多
如:验证 【登录成功】
with get_webdriver() as driver:
driver.get("http://101.34.221.219:8010/?s=user/loginInfo.html")
driver.find_element(By.XPATH, "/html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/form/div[1]/input", ).send_keys(
"tiancao")
driver.find_element(By.XPATH,
"/html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/form/div[2]/div/input", ).send_keys(
"ganju123")
driver.find_element(By.XPATH,
"/html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/form/div[3]/button", ).click() # 点击登录
time.sleep(1) # sleep的值不好取,太大 或者 太小,都会失败
msg = driver.find_element(By.XPATH, "//p[@class='prompt-msg']", ).text
assert msg == "登录成功" # 没有任何输出,说明断言成功
对于出现之后会
消失
的元素来讲,要求
sleep
的秒数相当精确
学习
selenium
中提供
等待
来进行解决
3. 隐式等待
原理:让
selenium
在查找元素时,如果失败,就重试
默认:参数为
0
表示禁用
启用:参数大于
1
即可
特点:一旦启用,全局
(driver)
生效
弊端:
- 只会等待元素出现
- 不会等待元素就绪
with get_webdriver() as driver:
# driver.implicitly_wait(0) #禁用
driver.implicitly_wait(10) # 启用隐式等待,每一次等待最长不超过10秒
driver.get("http://118.24.147.95:8086/delay.html")
msg = driver.find_element(By.XPATH, '//*[@id="content"]/p').text
ele = driver.find_element(By.XPATH, '//*[@id="content"]/input')
assert msg == "大家好,我是北凡"
对于复杂的业务场景,不只要求元素存在,还要求元素的状态就绪
http://118.24.147.95:8086/flash.html
如果要对等待条件,进行定制,就需要用到显式等待
4. 显式等待 【重点难点】
显示等待指的是
WebDriverWait
对象
所谓
“
显式
”
,是为了相对于前面的
“
隐式
”
而言的,
在显式等待中:等待的时机、内容,根据清晰、直观、可控
4.1. 实例化 WebDriverWait 对象
- 两个参数 (固定写法) driver: WebDriver实例 (浏览器对象)
- 10 :超时时间,等待最多不超过10秒
WebDriverWait(driver, 10)
4.2. 指定等待条件
灵活写法(难写法)
msg = WebDriverWait(driver, 10).until(
lambda x: driver.find_element(
By.XPATH,
"//p[@class='prompt-msg']",
).text
)
# 等待条件(匿名函数)
lambda x: driver.find_element(
By.XPATH,
"//p[@class='prompt-msg']",
).text
x
是匿名函数的参数,被传递了
driver
driver = webdriver.Chrome() # 启动浏览器 是空白页
driver.get("http://101.34.221.219:8010/?s=user/loginInfo.html")
driver.find_element(By.XPATH,"/html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/form/div[1]/input",).send_keys("tiancao")
driver.find_element(By.XPATH,"/html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/form/div[2]/div/input",).send_keys("ganju123")
driver.find_element(By.XPATH,"/html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/form/div[3]/button",).click() # 点击登录
msg = WebDriverWait(driver, 10).until(lambda x: driver.find_element(
By.XPATH,
"//p[@class='prompt-msg']",
).text)
assert msg == "登录成功",f"{msg}" # 没有任何输出,说明断言成功
#driver.quit()
4.3. 等待条件的具体要求
成功例子:在等待中,定位元素,函数返回了布尔值True 再交互
with get_webdriver() as driver:
driver.get("http://118.24.147.95:8086/delay.html")
ele = WebDriverWait(driver, 10).until(
lambda x: driver.find_element(By.XPATH, '//*[@id="content"]/input')
)
ele.click() # 元素交互
driver.find_element(By.XPATH, '//*[@id="content"]/input')
的返回值, 是一个对象,它布尔值是 True
失败的例子:在等待中,交互元素
WebDriverWait(driver, 10).until( # 定位元素
lambda x: driver.find_element(By.XPATH, '//*[@id ="content"] /input').click()
)
返回值是 driver.find_element(By.XPATH, '//* [@id="content"]/input').click() 是None,它的布尔值是
False
4.3. 1. 等待条件是一个函数
可以是普通函数
也可以是匿名函数
匿名函数 和普通函数有什么区别?
区别
|
普通函数
|
匿名函数
|
关键
|
def
|
lambda
|
名字
|
有名字
|
没有名字
|
长度
|
可以多行
|
只能一行
|
返回值
|
可选,用
return
指定
|
必选,返回表达式结果
|
4.3. 2. 返回值是布尔值
- 必须是布尔值,不是布尔值, 也会按照布尔值进行判断
- 如果判断为Ture,表示等待成功,停止等待
- 如判断为False,表示等待失败,继续等到,直到超时,
4.3. 3. 只有一个参数,参数值是driver
4.3. 4. 出现异常
出现
NoSuchElementException
异常表示等待失败,将继续等待
出现其他异常,表示出错,停止等待
出现
TimeOut
异常,表示超时,停止等待
总结
1.
是个函数
2.
有且只有一个参数,参数值是
driver
3.
返回值应该是布尔值
如果是
Ture
,表示等待成功,停止等待
- 如果是False,表示等待失败,继续等待,直到超时
- 如果是 NoSuchElementException 异常,表示等待失败,继续等待
- 如果是其他异常,表示出错,停止等待
如果,想要以False表示成功怎么办?可以使用 until_not 方法:
- 如果是Ture,表示等待失败,继续等待,直到超时
- 如果是False,表示等待成功,停止等待
# WebDriverWait(driver, 10).until(lambda x: False) # 会超时
WebDriverWait(driver, 10).until_not(lambda x: False) # 会成功
5. 流畅等待 (最底层,最强大,最难)
selenium
核心是用什么语言开发的?
java
在
Java
语言中,流畅等待指的是
FluentWait
对象,是
WebDriverWait
的父类
在
Python
中,没有定义
FluentWait
对象,直接使用
WebDriverWait
对象完成流畅等待
和显式等待相比,流畅等待增加
2
个参数
:
- 重试频率 (等待的原理:失败重试,多久重试一次?)
- 忽略的异常列表
可以实现更加复杂的业务场景
5. 1. 元素闪现太快
with get_webdriver() as driver:
driver.get("http://118.24.147.95:8086/flash_fast.html")
WebDriverWait(driver, 10, 0.01).until( # 调整了重试频率
lambda x: driver.find_element(By.XPATH, "//p")
)
5. 2. 出现特殊异常
with get_webdriver() as driver:
driver.get("http://118.24.147.95:8086/delay_alert.html")
alert = WebDriverWait(
driver,
10,
ignored_exceptions=[
NoAlertPresentException
], # 在等待过程中,如果出现NoAlertPresentException, 就继续等待
).until(lambda x: driver.switch_to.alert)
print(alert.text)
6. 等待策略可以复用
复用前
with get_webdriver() as driver:
driver.implicitly_wait(10) # 隐式等待只要设置一次
driver.get("http://118.24.147.95:8086/flash.html")
p = WebDriverWait(driver, 10).until( # 显式等待要设置1次
lambda x: driver.find_element(By.XPATH, "//p"))
p = WebDriverWait(driver, 10).until( # 显式等待要设置1次
lambda x: driver.find_element(By.XPATH, "//input"))
复用后
with get_webdriver() as driver:
driver.wait = WebDriverWait(driver, 10) # 等待策略 设置1次
driver.get("http://118.24.147.95:8086/flash.html")
p = driver.wait.until(lambda x: driver.find_element(By.XPATH, "//p"))
i = driver.wait.until(lambda x: driver.find_element(By.XPATH, "//input")) # 到处使用
7. Selenium官网中:等待的警告
隐式等待 和显式 等待不要混用
在框架,只用显式等待(或者流畅等待)