等待概述
使用Selenium
操控浏览器时,等待非常重要,因为浏览器进行渲染、执行交互都是非常耗费时间的,另外由于网络等因素,经常出现我们需要操纵的DOM元素未就绪的情况,此时经常抛出NoSuchElementException
异常。
为了解决这一问题有两种处理策略:
- 被动等待。执行完一个比较耗时的操作后,使用
time.sleep()
等待一段时间,然后再继续执行。这种策略的问题在于不够灵活,等待时间无法预测,时间过长浪费时间,时间过短仍然可能导致异常。 - 主动检测。不断轮询元素是否就绪。这样程序稍微复杂一些。
Selenium
中的等待机制
Selenium
内置了等待机制,一种是显示等待,一种是隐式等待。
- 显式等待:采用主动检测策略,根据条件进行等待。
- 隐式等待:采用被动等待策略,全局性固定等待时间。
显式等待
该功能由selenium\webdriver\support\wait.py
中的WebDriverWait
类实现。
WebDriverWait
类签名:class selenium.webdriver.support.wait.WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
构造函数参数:
driver
:WebDriver
实例。必备参数。timeout
:超时秒数。浮点数。单位为秒。必备参数。poll_frequency
:轮询频率,即每次轮询的间隔秒数。浮点数。单位为秒。默认值为0.5秒。可选参数。ignored_exceptions
:轮询时忽略的异常类。可迭代结构。默认只包含一个元素,即NoSuchElementException
。可选参数。
until(method, message='')
方法是WebDriverWait
类中最关键的方法。
作用:当返回值不为False
时,调用method
。
参数:
method
:可调用对象,参数为当前WebDriver
实例。message
:超时抛出异常时的提示。
返回值:最后一次调用method
的结果。
until_not(method, message='')
方法与until(method, message='')
方法非常相似,只是作用条件相反。
作用:当返回值为False
时,调用method
。
参数:
method
:可调用对象,参数为当前WebDriver
实例。message
:超时抛出异常时的提示。
返回值:True或者超时异常。
案例:演示显式等待
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
# 当lambda d: d.find_element_by_id("kw")返回值为True,即定位到id为kw的元素时,停止轮询,返回d.find_element_by_id("kw")
# 当函数返回值False时,持续轮询,要么返回值为True,要么超时
el = WebDriverWait(driver, 5).until(lambda d: d.find_element_by_id("kw"))
print(type(el))
# 当lambda d: d.find_element_by_id("kw1")抛出被忽略的异常(默认即NoSuchElementException),停止轮询,返回True
is_disappeared1 = WebDriverWait(driver, 5, 1,).until_not(
lambda d: d.find_element_by_id("kw1"))
print(is_disappeared1)
# 当lambda d: d.find_element_by_id("kw")未抛出异常时,持续轮询,要么抛出被忽略的异常返回值为True,要么超时
is_disappeared2 = WebDriverWait(driver, 5, 1,).until_not(
lambda d: d.find_element_by_id("kw"), "超时")
print(is_disappeared2)
结果
<class 'selenium.webdriver.firefox.webelement.FirefoxWebElement'>
True
Traceback (most recent call last):
File "c:/Users/Administrator/Desktop/selenium_learning/8 wait.py", line 16, in <module>
lambda d: d.find_element_by_id("kw"),"超时")
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\selenium\webdriver\support\wait.py", line 96, in until_not
raise TimeoutException(message)
selenium.common.exceptions.TimeoutException: Message: 超时
演示使用常规函数作为检测条件
until(method, message='')
方法和until_not(method, message='')
方法中的method参数,既可以使用匿名函数也可以使用常规函数。
需要注意的是,返回值至关重要,因为这两个方法的行为与返回值息息相关。
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
def check_element1(d):
return d.find_element_by_id("kw")
def check_element2(d):
return d.find_element_by_id("kw").get_attribute("value")
# check_element1函数返回值为百度搜索框元素,即True,因此会返回该元素
el1 = WebDriverWait(driver, 5).until(check_element1)
print(type(el1))
# check_element2函数返回值为百度搜索框的值,默认为空即False,因此会不断轮询,直到超时抛出异常
el2 = WebDriverWait(driver, 5).until(check_element2)
print(type(el2))
driver.quit()
结果为:
<class 'selenium.webdriver.firefox.webelement.FirefoxWebElement'>
Traceback (most recent call last):
File "c:/Users/Administrator/Desktop/selenium_learning/8 wait.py", line 15, in <module>
el2 = WebDriverWait(driver, 5).until(check_element2)
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\selenium\webdriver\support\wait.py", line 80, in until
raise TimeoutException(message, screen, stacktrace)
selenium.common.exceptions.TimeoutException: Message:
期望异常条件
在WebDriverWait
类的构造函数中ignored_exceptions
默认值为None
,即默认包含NoSuchElementException
异常类。
为了便于使用,Selenium
内置了一系列异常类,这些类定义在selenium\webdriver\support\expected_conditions.py
中。
可以通过selenium.webdriver.support.expected_conditions
包导入相关类。
隐式等待
该功能由selenium\webdriver\remote\webdriver.py
中的WebDriver
类实现。
功能:在当前会话生命周期中全局性为所有操作设置统一的超时时间,如果超时时间内操作完成,则继续其他操作,如果操作未完成则继续轮询,直到超时。例如:在设置隐式等待10秒后,如果需要定位某元素,该元素3秒后被定位到,那么继续向下执行,如果该元素未定位到,那么一直轮询,等超过10秒后抛出超时异常。
签名: driver.implicitly_wait(time_to_wait)
参数:time_to_wait
:等待时间。浮点数。单位为秒。必备参数。
注意事项:隐式等待不推荐与显式等待同时使用,将会导致不可预测的等待时间。
案例:演示隐式等待
本案例中虽然设置了隐式等待10秒钟,但是定位id为kw的元素0.01秒就完成了,所以继续向下执行。定位页面上不存在的id为kw1的元素时,由于该元素一直未成功定位,因此一直轮询,10秒后超时。
import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Firefox()
driver.implicitly_wait(10)
driver.get("http://www.baidu.com")
start = time.time()
el1 = driver.find_element_by_id("kw")
end1 = time.time()
print(end1-start)
# 定位不存在的元素,验证等待时长
try:
el2 = driver.find_element_by_id("kw1")
except:
end2 = time.time()
print(end2-end1)
driver.quit()
结果为:
0.008975505828857422
10.01714825630188
超时
页面加载超时
该功能由selenium\webdriver\remote\webdriver.py
中的WebDriver
类实现。
功能:设置页面加载超时时间。
签名: driver.set_page_load_timeout( time_to_wait)
参数:time_to_wait
:等待时间。浮点数。单位为秒。必备参数。
异步JavaScript脚本调用超时
该功能由selenium\webdriver\remote\webdriver.py
中的WebDriver
类实现。
功能:设置execute_async_script
方法异步调用JavaScript脚本超时时间。
签名: driver.set_script_timeout(time_to_wait)
参数:time_to_wait
:等待时间。浮点数。单位为秒。必备参数。