一、强制等待/固定等待
- 优点:用法简单,一般用于调试项目。
- 缺点:等待时间固定,脚本中大量使用会导致运行效率低。
import time
time.sleep(5)
二、隐式等待/全局等待
设置一次后,全局有效。元素对象没有在页面出现时,最多等待指定时间;但如果在等待时间内,元素对象出现就停止等待。
- 缺点:隐式等待只能在元素对象没有出现时才会等待,否则不会产生任何等待效果。
- 举例:在某个页面中点击了A按钮后,有个<span id='msg'></span>标签的文本内容会变成"XXX",然后需要获取到这个"XXX"文本内容。
- 正常来说,点击了A按钮后,应该等待span标签文本内容变成XXX,才能获取到这个文本值。
driver.find_element('id', 'A').click() # 等待span标签文本内容改变成:XXX print(driver.find_element('id', 'msg').text)
- 而使用隐式等待来实现时,因为span这个元素对象已经出现在页面了,那么会直接去打印span标签的文本内容,而不会进行等待,所以打印值为空。
driver.implicitly_wait(10) driver.find_element('id', 'A').click() print(driver.find_element('id', 'msg').text)
三、显示等待(WebDriverWait)
1)参数解释
from selenium.webdriver.support.ui import WebDriverWait
class WebDriverWait(Generic[D]):
def __init__(
self,
driver: D,
timeout: float,
poll_frequency: float = POLL_FREQUENCY,
ignored_exceptions: typing.Optional[WaitExcTypes] = None,
):
"""Constructor, takes a WebDriver instance and timeout in seconds.
:Args:
- driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote) or a WebElement
- timeout - Number of seconds before timing out
- poll_frequency - sleep interval between calls
By default, it is 0.5 second.
- ignored_exceptions - iterable structure of exception classes ignored during calls.
By default, it contains NoSuchElementException only.
Example::
from selenium.webdriver.support.wait import WebDriverWait \n
element = WebDriverWait(driver, 10).until(lambda x: x.find_element(By.ID, "someId")) \n
is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\\ \n
until_not(lambda x: x.find_element(By.ID, "someId").is_displayed())
"""
- driver:(必选)是webDriver的实例。
- timeout:(必选)是设置等待超时时间,单位:秒。
- poll_frequency:(非必选)是循环检查时间,默认是0.5秒。可以实现每0.5秒检查一次要定位的元素是否已存在,一直到超时;如果元素存在的话,则返回该元素。
- ignored_exceptions:(非必选)调用期间忽略异常类的可迭代结构。
2)简单示例
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
driver=webdriver.Chrome()
driver.get('https://www.baidu.com')
# 定位百度搜索框,并输入内容
# 等待时间为10秒,循环检查时间为0.5秒
WebDriverWait(driver, 10).until(lambda x: x.find_element('id', 'kw')).send_keys('python')
3)until()中的lambda函数
查看until()方法的源码:
由上述源码可知,lambda函数中的参数x就是driver对象,整个lambda函数类似于下面这样:
def lambda_method(x):
return x.find_element('id', 'kw')
WebDriverWait(driver, 10).until(lambda_method)
四、智能等待
每一个要定位的元素可能都要进行等待,如果每个元素定位代码都写成如下结构,会显得代码不够简洁。
WebDriverWait(self.driver, 30).until(lambda x: x.find_element(...))
因此,可以考虑对程序进行函数封装,简化代码,使得等待功能更加智能。这里以点击 “百度热搜 -> 置顶热搜” 为例。
import unittest
from selenium import webdriver
from selenium.common import NoSuchElementException
from selenium.webdriver.support.ui import WebDriverWait
class MyTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.get('https://www.baidu.com')
def tearDown(self):
self.driver.quit()
# 自定义find_element方法
def find_element(self, locator):
try:
element = WebDriverWait(self.driver, 30).until(lambda x: x.find_element(*locator))
return element
except NoSuchElementException as e:
print('报错:{}'.format(e.args[0]))
def test_01(self):
# 点击百度热搜
self.find_element(('css selector', '#ss-hotsearch-wrapper > div > a.hot-title > div > i:nth-child(1)')).click()
# 切换窗口
all_handle = self.driver.window_handles
self.driver.switch_to.window(all_handle[1])
# 点击置顶热搜
self.find_element(('xpath', '//*[@id="sanRoot"]/main/div[1]/div[1]/div[2]/a[1]/div[1]/img')).click()
五、显示等待与EC模块的结合使用
可针对每一个元素进行单独设置,等待条件更加灵活。如何判断一个元素是否存在,如何判断alert弹窗出来了,如何判断动态的元素等等,在selenium的expected_conditions模块中提供了一系列的场景判断方法。
1)导入相关模块
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
2)常见的EC模块方法
# 判断当前页面的title是否精确等于预期
title_is(strtitle)
# 判断当前页面的title是否包含指定字符串
title_contains(strtitle)
# 判断某个元素是否被加到了DOM树里,并不代表该元素一定可见
presence_of_element_located(locator)
# 判断某个元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于0
visibility_of_element_located(locator)
# 判断某个元素是否不可见
invisibility_of_element_located(locator)
# 判断某个元素是否可见并且是enable的,满足这些条件的称为clickable
element_to_be_clickable(locator)
# 判断某个元素中的text是否包含了预期的字符串
text_to_be_present_in_element(locator)
# 判断某个元素中的value是否包含了预期的字符串
text_to_be_present_in_element_value(locator)
# 判断弹出框是否存在
alert_is_present()
3)具体使用
在某个页面中点击了A按钮后,有个<span id='msg'></span>标签的文本内容会变成"XXX",然后需要获取到这个"XXX"文本内容。
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("...")
driver.find_element('id', 'A').click()
WebDriverWait(driver, 5).until(EC.text_to_be_present_in_element(('id', 'msg'), 'XXX'))
print(driver.find_element('id', 'msg').text)
4)其他方法使用
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("A.html")
def func1():
WebDriverWait(driver, 5).until(EC.element_to_be_clickable(('id', 'btn3')))
driver.find_element("id", 'btn3').click()
def func2():
WebDriverWait(driver, 5).until(EC.visibility_of_element_located(('id', 'btn2')))
def func3():
WebDriverWait(driver, 5).until(EC.presence_of_element_located(('id', 'btn2')))
if __name__ == '__main__':
pass
# btn3按钮在3秒后满足EC.element_to_be_clickable条件,因此等待3秒后会点击btn3按钮
# func1()
# btn2按钮不满足EC.visibility_of_element_located条件,5秒后会报超时错误
# func2()
# btn2按钮满足EC.presence_of_element_located条件,无需等待
# func3()