Selenium3 Python WebDriver API源码探析(8):等待(显式等待、隐式等待)、超时(页面加载超时、异步脚本调用超时)

等待概述

使用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)
构造函数参数:

  • driverWebDriver 实例。必备参数。
  • 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包导入相关类。

扫描二维码关注公众号,回复: 12899149 查看本文章

隐式等待

该功能由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:等待时间。浮点数。单位为秒。必备参数。

猜你喜欢

转载自blog.csdn.net/mighty13/article/details/114798536