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

首先,了解元素等待需要先知道浏览器的工作方式。浏览器在解析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)
  • driver:浏览器的实例
  • timeout:设置等待的总时间
  • interval:设置间隔的时间
  • err:超时后的异常信息,上面已经提到了,默认抛出TimeoutException的异常。
    WebDriverWait()一般由until()或者until_not配合使用,具体格式如下:
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)
  • title_is 判断当前页面的标题是否等于预期
element = WebDriverWait(browser,10,1).until(
    Ec.title_is(('微博随时随地发现新鲜事')),message='没有找到字符串'
)

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

image-20200515213035913.png

  • title_contains判断当前页面的标题是否包含预期字符串
element = WebDriverWait(browser,10,1).until(
    Ec.title_contains(('京东'))
)
  • presence_of_element_located 判断元素是否被加在DOM树里,不代表该元素一定可见
element = WebDriverWait(browser,10,1).until(
    Ec.presence_of_element_located((By.ID,'loginname'))
)
  • visibility_of_element_located 判断元素是否可见(可见代表没有隐藏,并且宽高都不为0)
element = WebDriverWait(browser,10,1).until(
    Ec.visibility_of_element_located((By.ID,'loginname'))
)
#可见返回true,不可见抛出TimeoutException异常
  • invisibility_of_element_located 判断某个元素是否不存在与DOM树中或者不可见
element = WebDriverWait(browser,10,1).until(
    Ec.invisibility_of_element_located((By.ID,'loginname'))
)
#和上一个方法相反不可见返回true,可见抛出TimeoutException异常
  • visibility_of 与上一个方法相同,但是上一个方法的参数为定位,该方法的参数为定位后的元素
loginModel = browser.find_element_by_id('loginname')
element = WebDriverWait(browser,10,1).until(
    Ec.visibility_of((loginModel))
)
#值得注意的是,visibility_of的参数必须为一个经过定位之后找到的元素,而不是元素的定位
  • presence_of_all_elements_located 判断DOM树中是否存在这个元素,可以存在一个或多个,只要存在一个就返回true
element = WebDriverWait(browser,10,1).until(
    Ec.presence_of_all_elements_located((By.ID,'loginname'))
)
  • text_to_be_present_in_element 判断某个元素中的text是否包含了预期的字符串
element = WebDriverWait(browser,10,1).until(
    Ec.text_to_be_present_in_element((By.CLASS_NAME,'enter_psw'),'密码')
)
#这个方法的使用格式和其他的方法有点不一样
  • text_to_be_present_in_element_value 判断某个元素中的value是否包含了预期的字符串
element = WebDriverWait(browser,10,1).until(
    Ec.text_to_be_present_in_element_value((By.CLASS_NAME,'enter_psw'),'密码')
)
  • element_to_be_clickableo 判断某个元素是否可见并且是可以点击的
element = WebDriverWait(browser,10,1).until(
    Ec.element_to_be_clickable((By.ID,'login_form_savestate'))
)
  • staleness_of 等待一个元素从DOM树中移除
node = browser.find_element_by_id('loginname')
element = WebDriverWait(browser,50,1).until(
    Ec.staleness_of((node))
)
  • element_to_be_selected 判断某个元素是否被选中,一般用于下拉列表
#因为微博首页没有下拉列表,所以此测试在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))
)
  • element_selection_state_to_be 判断某个元素的选中状态是否符合预期
#因为微博首页没有下拉列表,所以此测试在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表示预期不选中
  • element_located_selection_state_to_be 和上面一样判断元素的选中状态是否和预期一样,只不过仔细看就会发现,此方法的参数为定位,而上面的方法的参数是定位后的元素
#因为微博首页没有下拉列表,所以此测试在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)
)
  • alert_is_present() 判断页面上是否存在alert
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报错

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

  • 显式等待更节省时间
  • 显式等待有等待条件,而隐式等待没有
  • 隐式等待是一个全局等待,前面设置了隐式等待时间,那么后面的元素找不到的话也都会进行隐式等待
  • 显式等待可以针对特定的元素等待,而隐式等待不是,当隐式等待执行测试时,如果没有找到元素将继续等待,超出时间会抛出异常
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352