关于显示等待WebDriverWait是什么,可以看selenium-wait源码解析
话不多说,先上实现代码:
第一种实现方式(官方提供):
from selenium.webdriver.support.wait import WebDriverWait
element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId"))
第二种实现方式(民间高手提供):
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'kw')))
一.第一种实现方式分析:
首先介绍下需要用到的lambda表达式:
f= lambda x:x+1
print(f(1))
输出:
2
可理解为:
def f(x):
return x+1
了解了这个之后,我们来看下until是如何执行的:
def until(self, method, message=''):
"""Calls the method provided with the driver as an argument until the \
return value is not False."""
screen = None
stacktrace = None
end_time = time.time() + self._timeout
while True:
try:
value = method(self._driver)
if value:
return value
except self._ignored_exceptions as exc:
screen = getattr(exc, 'screen', None)
stacktrace = getattr(exc, 'stacktrace', None)
time.sleep(self._poll)
if time.time() > end_time:
break
raise TimeoutException(message, screen, stacktrace)
通过value = method(self._driver)这行,我们可以理解为,我们先通过lambda表达式,定义了一个匿名函数,而入口的传参,正是driver
等价于:
driver.find_element_by_id("someId")
执行后返回一个webelement对象,供我们后续操作
二.第二种实现方式分析:
这里讲两个会引入的文件,expected_conditions.py和by.py
expected_conditions:封装了一些预期结果,通常用来测试
By:支持定位的策略集
我们看下我们引入的EC文件中的presence_of_element_located类:
class presence_of_element_located(object):
""" An expectation for checking that an element is present on the DOM
of a page. This does not necessarily mean that the element is visible.
locator - used to find the element
returns the WebElement once it is located
"""
def __init__(self, locator):
self.locator = locator
def __call__(self, driver):
return _find_element(driver, self.locator)
这俩简单介绍下两种魔法方法的特性:
__init__:初始化时自动载入
__call__:允许类的实例像函数一样被调用
我们debug一下这种方式的执行步骤可以发现:
初始化完成后,until会借助value = method(self.driver),利用我们前面所讲的魔法方法__call_,把presence_of_element_located类当作实例,传递driver对象,调用 _find_element
再看_find_element:
def _find_element(driver, by):
"""Looks up an element. Logs and re-raises ``WebDriverException``
if thrown."""
try:
return driver.find_element(*by)
except NoSuchElementException as e:
raise e
except WebDriverException as e:
raise e
会继续调用driver.find_element,
看下此时_find_element的入参:
driver:method给的driver,也就是WebDriverWait初始化时,我们自己传入的
by:这里可以看到,下面find_element(*by)是*by,所以他可以接收多个参数放到一个数组中,本质是一个tuple
接着看下by入参的是什么:在最开始EC.presence_of_element_located初始化时,locator入参为:(By.ID, ‘kw’),因为是通过__call__执行的,所以现在by的入参是通过locator传入的也是(By.ID, ‘kw’)(到这里发现调用了By类,所以也需要引入此文件)
再往下运行,就到了调用driver.find_element(*by),后续就是正常执行流程了,他会进入到webdriver.py的执行逻辑中selenium-webdriver源码解析
执行后也返回一个webelement对象,供我们后续操作
写在最后:
两种执行方式比较之后,如何选择还请各位小伙伴自行判断~