5. selenium - 三种等待方式原理剖析和场景应用
前言
UI 自动化一大困难点就是元素定位,而且最不稳定的就是页面,定位方法没问题,但是有时候网络不好,页面加载慢,也会导致找不到元素。
找不到元素的时候selenium 会抛出类似的异常,NoSuchElementException
为了解决页面等待问题,selenium 也专门设计了一些方法来进行页面等待,等待页面加载完成才开始定位元素,这样的话就会大大降低找不到元素的异常。
等待方式有三种:
- Sleep 固定等待,等待时间到了,不管页面有没有加载完成,都会进行下一步。
- 隐式等待,implicitly_wait()。等待页面元素全部加载完成,进行下一步操作。
- 显式等待,WebDriverWait()。等待特定某个元素加载完成,进行下一步。
一、sleep 固定等待
在自动化测试过程中,不要用 sleep 方法进行等待,这样会导致整个项目的自动化测试无线延长,不建议使用。但是在调试脚本的时候可以使用,方便快捷。
from selenium import webdriver
from time import sleep # 导入sleep
driver = webdriver.Chrome()
url = 'https://movie.douban.com/'
driver.get(url)
sleep(5) # 等待 5 s
driver.find_element_by_name('search_text').send_keys('蜘蛛侠')
sleep(2)
driver.quit() # 关闭浏览器
二、implicitly_wait
隐式等待是通过一定的时长等页面元素全部加载完成(就是等待页面左上角转圈结束)。如果超出了设置的时长,元素还没有加载出来,那么就会抛出异常 NoSuchElementException。
否则一直等到时间结束,这里有不好的地方,因为 JavaScript 一般是放在 body 的最后进行加载,需要的元素加载完成了,我们却在等待页面全部加载完成,也是浪费时间。
隐式等待对整个 driver 周期都起作用,在最开始设置一次就可以了。
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
driver = webdriver.Chrome()
# 设置隐式等待,最多等待 10 s,如果 10s内什么时候加载完毕就什么时候进行操作
driver.implicitly_wait(10)
try:
url = 'https://movie.douban.com/'
driver.get(url)
driver.find_element_by_name('search_text').send_keys('蜘蛛侠')
except NoSuchElementException as msg:
print(msg)
# 关闭浏览器
driver.quit()
三、 显式等待 - WebDriverWait()
显式等待是针对某一个元素进行等待判定,隐式等待是等待页面上全部加载完成,所以显式等待更加好,也是最常用的一种等待方式。
WebDriverWait 是 selenium 提供显式等待的模块。
class WebDriverWait(object):
def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
"""Constructor, takes a WebDriver instance and timeout in seconds.
:Args:
- driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
- 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.ui 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 : 调用 until 或until_not 中的方法的间隔时间,默认是 0.5 s
-ignored_exceptions: 忽略的异常
这个模块中,有两种方法 until 和 until_not。
-method : 在等待期间,每隔一段时间调用这个传入的方法,直到返回值不是 False
-message : 如果超时,抛出 TimeoutException,讲 message 传入异常。
例子1:
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
url = 'https://movie.douban.com/'
driver.get(url)
# 一共等待 10s,每 0.5 查找一次,直到找到name 为 search_text 的元素。
element = WebDriverWait(driver, 10, 0.5).until(lambda x: x.find_element_by_name("search_text"))
element.send_keys('蜘蛛侠')
例子2
方式二、
看看 expected_conditions 的源码,按住 ctrl 键,鼠标点击就可以过去源码这里。
有判断 title 的方法,有url 的,也有 alert 弹窗的,根据实际情况选择即可。
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
url = 'https://movie.douban.com/'
driver.get(url)
# 最多等待 5 s,每 0.5 秒查找一次。
wait = WebDriverWait(driver, 5, 0.5)
# 直到在这个页面出现 title 为 豆瓣电影
wait.until(EC.title_is('豆瓣电影'))
driver.find_element_by_name('search_text').send_keys('蜘蛛侠')