显式等待与隐式等待——元素等待

2020-06-02  本文已影响0人  天花板上老蜥蜴

首先,了解元素等待需要先知道浏览器的工作方式。浏览器在解析HTML文件时,需要从第一行按顺序往下解析。不仅仅解析HTML文件如此,解析JS文件也是这样,但是浏览器解析JS文件就比解析HTML文件要复杂地多的多。对于许多页面来说,页面需要加载数据来驱动视图,数据没加载完成,页面就不会显示对应的节点,又或者监听用户的行为,根据用户的行为添加、显示和删除页面相应的节点。页面加载数据的方式有很多种:加载本地文件、自定义AJAX、使用JQuery、或者是客户端请求数据库返回数据。在上述任何一种情况下当想要操纵的节点没有加载完成时,python脚本又已经开始执行,那么结果只能是报错。顺着这个问题往下想,还有许许多多地情况都会阻碍自动化脚本的执行。为了解决这样类似的问题,有人提出了元素等待的概念,简单的来说就是让脚本等待页面元素的出现

元素等待的三种方式

强制等待sleep()

强制等待是指让脚本等待一定的固定时间再执行,需要说明的是,强制等待sleep()方法由Python的time模块提供

from time import sleep
sleep(10)
print('等待10秒之后执行')

当脚本执行到第三行时,会等待10秒的事件,然后再往下执行,参数就是要等待的时间。
比如下面的例子,在很多地方都需要使用到强制等待sleep()方法

from time import sleep
browser = webdriver.Chrome()
browser.get('https://weibo.com/')
sleep(10)

微博的首页繁冗且杂长,需要等待一定的时间再去获取相应的元素,否则直接去查找还未加载出来或者需要特定操作才可以出现的元素,会抛出NoSuchElementException的异常。

image-20200515151649311.png

显式等待

显式等待和隐式等待都是由WebDriver提供的,显式等待是在一段固定时间内,每隔一定的时间检测一次相应的元素是否存在,如果在这段固定时间内想要检测的元素依然不存在的话,会抛出TimeoutException的异常。

image-20200515152510190.png

显式等待WebDriverWait的使用

WebDriverWait类是由WebDriver提供的方法,具体格式如下:

WebDriverWait(driver,timeout,interval,err)
from selenium import webdriver
#从selenium导入webdriver包
from selenium.webdriver.common.by import By
#从selenium.webdriver.common.by 导入By包进行元素定位
from selenium.webdriver.support.wait import WebDriverWait
#从selenium.webdriver.common.wait 导入WebDriverWait包进行显式等待
from selenium.webdriver.support import expected_conditions as Ec
#从selenium.webdriver.common.wait 导入expected_conditions类
#并通过关键字as将expected_conditions重命名为Ec
WebDriverWait(driver,timeout,interval,err).until(
    method,message=""
)
#或者
WebDriverWait(driver,timeout,interval,err).until_not(
    method,message=""
)
#message=""可以忽略,但是忽略时method外是两层括号,下面会继续提到message
#from selenium.webdriver.support.wait import WebDriverWait有的文档并不是这个写的
#而是from selenium.webdriver.support.ui import WebDriverWait,其实ui和wait并没有什么差别,随心写就好

上述代码中,untiluntil_not都是以method作为参数,直到until的返回值为true或者直到until_not的返回值为falsemethod方法是由expected_conditions类提供的内置方法,具体方法如下:

内置方法示例前提条件:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as Ec
browser = webdriver.Chrome()
browser.get('https://weibo.com/')
sleep(10)
element = WebDriverWait(browser,10,1).until(
    Ec.title_is(('微博随时随地发现新鲜事')),message='没有找到字符串'
)

值得注意的是,如果超时(until的返回值为false),脚本会抛出TimeoutException,同时将message传入异常,下面的很多种方法都是如此,下图就是将message传入异常并抛出的例子

image-20200515213035913.png
element = WebDriverWait(browser,10,1).until(
    Ec.title_contains(('京东'))
)
element = WebDriverWait(browser,10,1).until(
    Ec.presence_of_element_located((By.ID,'loginname'))
)
element = WebDriverWait(browser,10,1).until(
    Ec.visibility_of_element_located((By.ID,'loginname'))
)
#可见返回true,不可见抛出TimeoutException异常
element = WebDriverWait(browser,10,1).until(
    Ec.invisibility_of_element_located((By.ID,'loginname'))
)
#和上一个方法相反不可见返回true,可见抛出TimeoutException异常
loginModel = browser.find_element_by_id('loginname')
element = WebDriverWait(browser,10,1).until(
    Ec.visibility_of((loginModel))
)
#值得注意的是,visibility_of的参数必须为一个经过定位之后找到的元素,而不是元素的定位
element = WebDriverWait(browser,10,1).until(
    Ec.presence_of_all_elements_located((By.ID,'loginname'))
)
element = WebDriverWait(browser,10,1).until(
    Ec.text_to_be_present_in_element((By.CLASS_NAME,'enter_psw'),'密码')
)
#这个方法的使用格式和其他的方法有点不一样
element = WebDriverWait(browser,10,1).until(
    Ec.text_to_be_present_in_element_value((By.CLASS_NAME,'enter_psw'),'密码')
)
element = WebDriverWait(browser,10,1).until(
    Ec.element_to_be_clickable((By.ID,'login_form_savestate'))
)
node = browser.find_element_by_id('loginname')
element = WebDriverWait(browser,50,1).until(
    Ec.staleness_of((node))
)
#因为微博首页没有下拉列表,所以此测试在12306的注册页面上
browser.get('https://kyfw.12306.cn/otn/regist/init')
sleep(10)
node = browser.find_element_by_css_selector("[value='0']")
element = WebDriverWait(browser,20,1).until(
    Ec.element_to_be_selected((node))
)
#因为微博首页没有下拉列表,所以此测试在12306的注册页面上
browser.get('https://kyfw.12306.cn/otn/regist/init')
sleep(10)
node = browser.find_element_by_id("idTypeCode_wgjzz")
element = WebDriverWait(browser,20,1).until(
    Ec.element_selection_state_to_be(node,True)
)
# True表示预期选中,false表示预期不选中
#因为微博首页没有下拉列表,所以此测试在12306的注册页面上
browser.get('https://kyfw.12306.cn/otn/regist/init')
sleep(10)
element = WebDriverWait(browser,20,1).until(
    Ec.element_located_selection_state_to_be((By.ID,"idTypeCode_wgjzz"),True)
)
element = WebDriverWait(browser,10,1).until(
    Ec.alert_is_present()
)

至此,expected_conditions类提供的所有的方法的实例都已经举例完毕了

隐式等待

如果 WebDriver 没有在 DOM 中找到元素,将继续等待,超出设定时间后则抛出找不到元素的异常,
换句话说,当查找元素或元素并没有立即出现的时候,隐式等待将等待一段时间再查找 DOM,默认的时间是 0

implictly_wait()方法具体格式

browser.implicitly_wait(10)
#参数为秒数,默认为0

实例:

from selenium import webdriver
browser = webdriver.Chrome()
browser.implicitly_wait(10)
browser.get('https://baidu.com/')
browser.find_element_by_id("kw").send_keys('selenium')

上述代码表示在脚本查找元素时,即使当前没有找到相应的元素也不会立即报错,而是等待10秒的时间继续查找相应的元素,如果依然未找到,则超时TimeoutException报错

总结:显式等待和隐式等待的区别

上一篇下一篇

猜你喜欢

热点阅读