首先绰疤,了解元素等待需要先知道瀏覽器的工作方式铜犬。瀏覽器在解析HTML文件時(shí)轻庆,需要從第一行按順序往下解析。不僅僅解析HTML文件如此纷宇,解析JS文件也是這樣蛾方,但是瀏覽器解析JS文件就比解析HTML文件要復(fù)雜地多的多像捶。對(duì)于許多頁(yè)面來(lái)說(shuō)桩砰,頁(yè)面需要加載數(shù)據(jù)來(lái)驅(qū)動(dòng)視圖,數(shù)據(jù)沒(méi)加載完成硼莽,頁(yè)面就不會(huì)顯示對(duì)應(yīng)的節(jié)點(diǎn),又或者監(jiān)聽(tīng)用戶的行為沉删,根據(jù)用戶的行為添加醉途、顯示和刪除頁(yè)面相應(yīng)的節(jié)點(diǎn)砖茸。頁(yè)面加載數(shù)據(jù)的方式有很多種:加載本地文件、自定義AJAX凉夯、使用JQuery采幌、或者是客戶端請(qǐng)求數(shù)據(jù)庫(kù)返回?cái)?shù)據(jù)震桶。在上述任何一種情況下當(dāng)想要操縱的節(jié)點(diǎn)沒(méi)有加載完成時(shí),python腳本又已經(jīng)開(kāi)始執(zhí)行蹲姐,那么結(jié)果只能是報(bào)錯(cuò)。順著這個(gè)問(wèn)題往下想柴墩,還有許許多多地情況都會(huì)阻礙自動(dòng)化腳本的執(zhí)行。為了解決這樣類似的問(wèn)題逢净,有人提出了元素等待的概念歼指,簡(jiǎn)單的來(lái)說(shuō)就是讓腳本等待頁(yè)面元素的出現(xiàn)。
元素等待的三種方式
- 強(qiáng)制等待
- 顯式等待
- 隱式等待
強(qiáng)制等待sleep()
強(qiáng)制等待是指讓腳本等待一定的固定時(shí)間再執(zhí)行踩身,需要說(shuō)明的是,強(qiáng)制等待sleep()
方法由Python的time
模塊提供
from time import sleep
sleep(10)
print('等待10秒之后執(zhí)行')
當(dāng)腳本執(zhí)行到第三行時(shí)宰掉,會(huì)等待10秒的事件,然后再往下執(zhí)行轨奄,參數(shù)就是要等待的時(shí)間拒炎。
比如下面的例子,在很多地方都需要使用到強(qiáng)制等待sleep()
方法
from time import sleep
browser = webdriver.Chrome()
browser.get('https://weibo.com/')
sleep(10)
微博的首頁(yè)繁冗且雜長(zhǎng)击你,需要等待一定的時(shí)間再去獲取相應(yīng)的元素,否則直接去查找還未加載出來(lái)或者需要特定操作才可以出現(xiàn)的元素丁侄,會(huì)拋出NoSuchElementException
的異常。
顯式等待
顯式等待和隱式等待都是由WebDriver提供的石景,顯式等待是在一段固定時(shí)間內(nèi),每隔一定的時(shí)間檢測(cè)一次相應(yīng)的元素是否存在潮孽,如果在這段固定時(shí)間內(nèi)想要檢測(cè)的元素依然不存在的話,會(huì)拋出TimeoutException
的異常仗颈。
顯式等待WebDriverWait的使用
WebDriverWait
類是由WebDriver
提供的方法椎例,具體格式如下:
WebDriverWait(driver,timeout,interval,err)
-
driver
:瀏覽器的實(shí)例 -
timeout
:設(shè)置等待的總時(shí)間 -
interval
:設(shè)置間隔的時(shí)間 -
err
:超時(shí)后的異常信息,上面已經(jīng)提到了粟矿,默認(rèn)拋出TimeoutException
的異常。
WebDriverWait()
一般由until()
或者until_not
配合使用撒犀,具體格式如下:
from selenium import webdriver
#從selenium導(dǎo)入webdriver包
from selenium.webdriver.common.by import By
#從selenium.webdriver.common.by 導(dǎo)入By包進(jìn)行元素定位
from selenium.webdriver.support.wait import WebDriverWait
#從selenium.webdriver.common.wait 導(dǎo)入WebDriverWait包進(jìn)行顯式等待
from selenium.webdriver.support import expected_conditions as Ec
#從selenium.webdriver.common.wait 導(dǎo)入expected_conditions類
#并通過(guò)關(guān)鍵字as將expected_conditions重命名為Ec
WebDriverWait(driver,timeout,interval,err).until(
method,message=""
)
#或者
WebDriverWait(driver,timeout,interval,err).until_not(
method,message=""
)
#message=""可以忽略掏秩,但是忽略時(shí)method外是兩層括號(hào),下面會(huì)繼續(xù)提到message
#from selenium.webdriver.support.wait import WebDriverWait有的文檔并不是這個(gè)寫的
#而是from selenium.webdriver.support.ui import WebDriverWait,其實(shí)ui和wait并沒(méi)有什么差別蒙幻,隨心寫就好
上述代碼中,until
和until_not
都是以method
作為參數(shù)邮破,直到until
的返回值為true或者直到until_not
的返回值為false
,method
方法是由expected_conditions
類提供的內(nèi)置方法矫渔,具體方法如下:
內(nèi)置方法示例前提條件:
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
判斷當(dāng)前頁(yè)面的標(biāo)題是否等于預(yù)期
element = WebDriverWait(browser,10,1).until(
Ec.title_is(('微博隨時(shí)隨地發(fā)現(xiàn)新鮮事')),message='沒(méi)有找到字符串'
)
值得注意的是摧莽,如果超時(shí)(until
的返回值為false),腳本會(huì)拋出TimeoutException
镊辕,同時(shí)將message
傳入異常,下面的很多種方法都是如此征懈,下圖就是將message
傳入異常并拋出的例子
-
title_contains
判斷當(dāng)前頁(yè)面的標(biāo)題是否包含預(yù)期字符串
element = WebDriverWait(browser,10,1).until(
Ec.title_contains(('京東'))
)
-
presence_of_element_located
判斷元素是否被加在DOM樹(shù)里卖哎,不代表該元素一定可見(jiàn)
element = WebDriverWait(browser,10,1).until(
Ec.presence_of_element_located((By.ID,'loginname'))
)
-
visibility_of_element_located
判斷元素是否可見(jiàn)(可見(jiàn)代表沒(méi)有隱藏虏束,并且寬高都不為0)
element = WebDriverWait(browser,10,1).until(
Ec.visibility_of_element_located((By.ID,'loginname'))
)
#可見(jiàn)返回true厦章,不可見(jiàn)拋出TimeoutException異常
-
invisibility_of_element_located
判斷某個(gè)元素是否不存在與DOM樹(shù)中或者不可見(jiàn)
element = WebDriverWait(browser,10,1).until(
Ec.invisibility_of_element_located((By.ID,'loginname'))
)
#和上一個(gè)方法相反不可見(jiàn)返回true,可見(jiàn)拋出TimeoutException異常
-
visibility_of
與上一個(gè)方法相同袜啃,但是上一個(gè)方法的參數(shù)為定位幸缕,該方法的參數(shù)為定位后的元素
loginModel = browser.find_element_by_id('loginname')
element = WebDriverWait(browser,10,1).until(
Ec.visibility_of((loginModel))
)
#值得注意的是,visibility_of的參數(shù)必須為一個(gè)經(jīng)過(guò)定位之后找到的元素发乔,而不是元素的定位
-
presence_of_all_elements_located
判斷DOM樹(shù)中是否存在這個(gè)元素,可以存在一個(gè)或多個(gè)栏尚,只要存在一個(gè)就返回true
element = WebDriverWait(browser,10,1).until(
Ec.presence_of_all_elements_located((By.ID,'loginname'))
)
-
text_to_be_present_in_element
判斷某個(gè)元素中的text是否包含了預(yù)期的字符串
element = WebDriverWait(browser,10,1).until(
Ec.text_to_be_present_in_element((By.CLASS_NAME,'enter_psw'),'密碼')
)
#這個(gè)方法的使用格式和其他的方法有點(diǎn)不一樣
-
text_to_be_present_in_element_value
判斷某個(gè)元素中的value是否包含了預(yù)期的字符串
element = WebDriverWait(browser,10,1).until(
Ec.text_to_be_present_in_element_value((By.CLASS_NAME,'enter_psw'),'密碼')
)
-
element_to_be_clickableo
判斷某個(gè)元素是否可見(jiàn)并且是可以點(diǎn)擊的
element = WebDriverWait(browser,10,1).until(
Ec.element_to_be_clickable((By.ID,'login_form_savestate'))
)
-
staleness_of
等待一個(gè)元素從DOM樹(shù)中移除
node = browser.find_element_by_id('loginname')
element = WebDriverWait(browser,50,1).until(
Ec.staleness_of((node))
)
-
element_to_be_selected
判斷某個(gè)元素是否被選中译仗,一般用于下拉列表
#因?yàn)槲⒉┦醉?yè)沒(méi)有下拉列表,所以此測(cè)試在12306的注冊(cè)頁(yè)面上
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
判斷某個(gè)元素的選中狀態(tài)是否符合預(yù)期
#因?yàn)槲⒉┦醉?yè)沒(méi)有下拉列表纵菌,所以此測(cè)試在12306的注冊(cè)頁(yè)面上
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表示預(yù)期選中,false表示預(yù)期不選中
-
element_located_selection_state_to_be
和上面一樣判斷元素的選中狀態(tài)是否和預(yù)期一樣笛辟,只不過(guò)仔細(xì)看就會(huì)發(fā)現(xiàn),此方法的參數(shù)為定位手幢,而上面的方法的參數(shù)是定位后的元素
#因?yàn)槲⒉┦醉?yè)沒(méi)有下拉列表杠览,所以此測(cè)試在12306的注冊(cè)頁(yè)面上
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()
判斷頁(yè)面上是否存在alert
element = WebDriverWait(browser,10,1).until(
Ec.alert_is_present()
)
至此,expected_conditions
類提供的所有的方法的實(shí)例都已經(jīng)舉例完畢了
隱式等待
如果 WebDriver
沒(méi)有在 DOM 中找到元素踱阿,將繼續(xù)等待,超出設(shè)定時(shí)間后則拋出找不到元素的異常,
換句話說(shuō)软舌,當(dāng)查找元素或元素并沒(méi)有立即出現(xiàn)的時(shí)候,隱式等待將等待一段時(shí)間再查找 DOM醇滥,默認(rèn)的時(shí)間是 0
implictly_wait()方法具體格式
browser.implicitly_wait(10)
#參數(shù)為秒數(shù)黎比,默認(rèn)為0
實(shí)例:
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')
上述代碼表示在腳本查找元素時(shí)鸳玩,即使當(dāng)前沒(méi)有找到相應(yīng)的元素也不會(huì)立即報(bào)錯(cuò),而是等待10秒的時(shí)間繼續(xù)查找相應(yīng)的元素颓帝,如果依然未找到,則超時(shí)TimeoutException
報(bào)錯(cuò)
總結(jié):顯式等待和隱式等待的區(qū)別
- 顯式等待更節(jié)省時(shí)間
- 顯式等待有等待條件购城,而隱式等待沒(méi)有
- 隱式等待是一個(gè)全局等待虐译,前面設(shè)置了隱式等待時(shí)間瘪板,那么后面的元素找不到的話也都會(huì)進(jìn)行隱式等待
- 顯式等待可以針對(duì)特定的元素等待漆诽,而隱式等待不是,當(dāng)隱式等待執(zhí)行測(cè)試時(shí)拴泌,如果沒(méi)有找到元素將繼續(xù)等待,超出時(shí)間會(huì)拋出異常