Selenium系列(六) – 強制等待、隱式等待、顯式等待詳細介紹和源碼解讀
- 2020 年 3 月 28 日
- 筆記
如果你還想從頭學起Selenium,可以看看這個系列的文章哦!
https://www.cnblogs.com/poloyy/category/1680176.html
其次,如果你不懂前端基礎知識,需要自己去補充哦,部落客暫時沒有總結(雖然我也會,所以我學selenium就不用複習前端了哈哈哈…)
設置元素等待
為什麼需要設置元素等待?
- 因為,目前大多數Web應用程式都是使用Ajax和Javascript開發的;每次載入一個網頁,就會載入各種HTML標籤、JS文件
- 但是,載入肯定有載入順序,大型網站很難說一秒內就把所有東西載入出來,不僅如此,載入速度也受網路波動影響
- 因此,當我們要在網頁中做元素定位的時候,有可能我們打開了網頁但元素未載入出來,這個時候就定位不到元素,就會報錯
- 所以,我們需要設置元素等待,意思就是:等待指定元素已被載入出來之後,我們才去定位該元素,就不會出現定位失敗的現象了
如果我們不設置元素等待,那怎麼避免 因元素未載入出來而定位失敗 的情況出現呢?
- 答案很簡單,就是調用 sleep() ,也叫強制等待
- 但是缺點就是:如果指定的時間過長,即使元素已被載入出來了,但還是要繼續等,這樣會浪費很多時間
強制等待的栗子
#!/usr/bin/env python # -*- coding: utf-8 -*- """ __title__ = __Time__ = 2020/3/25 17:52 __Author__ = 小菠蘿測試筆記 __Blog__ = https://www.cnblogs.com/poloyy/ """ from time import sleep from selenium import webdriver driver = webdriver.Chrome("../resources/chromedriver.exe") 20) # 訪問網址 driver.get("http://www.baidu.com") # ===強制等待3秒才執行下一步=== sleep(3) # 找到搜索框 inputElement = driver.find_element_by_id("kw")
WebDriver提供了兩種類型的等待:顯式等待和隱式等待
隱式等待
什麼是隱式等待?
- 如果某些元素不是立即可用的,隱式等待是告訴WebDriver去等待一定的時間後去查找元素
- 默認等待時間是0秒,隱式等待對整個WebDriver的周期都起作用,所以只要設置一次即可
如何體現隱式等待?
如果在規定時間內,整個網頁都載入完成,則執行下一步,否則會拋出異常
隱式等待的弊端
可以把隱式等待當做全局變數,它影響整個頁面,所以程式需要等待整個頁面載入完成(就是瀏覽器標籤欄那個小圈不再轉)時,才會執行下一步【頁面載入完成,才能執行下一步】
但可能頁面載入未完成的時候,需要定位的元素已經載入完成了,但受限於某些JS文件、圖片載入特別慢,我們不能執行下一步,必須得等到網頁所有東西都載入完了才能下一步【增加不必要的載入時間】
隱式等待的程式碼
很簡單,就調用一個方法即可,畢竟是作用於WebDriver的
#!/usr/bin/env python # -*- coding: utf-8 -*- """ __title__ = __Time__ = 2020/3/25 17:52 __Author__ = 小菠蘿測試筆記 __Blog__ = https://www.cnblogs.com/poloyy/ """ from selenium import webdriver # 載入驅動 driver = webdriver.Chrome("../resources/chromedriver.exe") # ===隱性等待20s=== driver.implicitly_wait(20) # 訪問網址 driver.get("http://www.baidu.com") # 找到搜索框 inputElement = driver.find_element_by_id("kw")
顯式等待
什麼是顯式等待?
- 需要定位某個元素的時候,但元素可能不可見,這個時候針對這個元素就可以使用顯式等待了
- 顯式等待和隱式等待最大的不同就是:你可以它看成是局部變數,作用於指定元素
顯式等待的優勢
相比隱式等待,顯式等待只對指定元素生效,不再是在整個WebDriver生命周期內生效【僅對元素生效】
可以根據需要定位的元素來設置顯式等待,無需等待頁面完全載入,節省大量因載入無關緊要文件而浪費掉的時間【針對元素設置,無需等待頁面載入完成,節省載入時間】
顯式等待的程式碼
#!/usr/bin/env python # -*- coding: utf-8 -*- """ __title__ = __Time__ = 2020/3/25 17:52 __Author__ = 小菠蘿測試筆記 __Blog__ = https://www.cnblogs.com/poloyy/ """ from time import sleep 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 driver = webdriver.Chrome("../resources/chromedriver.exe") # 訪問網址 driver.get("http://www.baidu.com") # ===顯式等待=== # 設置元素等待實例,最多等10秒,每0.5秒查看條件是否成立 element = WebDriverWait(driver, 10, 0.5).until( # 條件:直到元素載入完成 EC.presence_of_element_located((By.ID, "kw")) )
WebDriverWait源碼解讀
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.
WebDriverWait實例初始化傳參
- driver:WebDriver實例,傳入前面聲明的driver即可
- timeout:最大超時時間;
- poll_frequency:執行間隔,默認0.5s
- ignored_exceptions:需要忽略的異常
- 如果在調用 until() 或 until_not() 的過程中拋出這個元組中的異常, 則不中斷程式碼,繼續等待;
- 如果拋出的是這個元組外的異常,則中斷程式碼;
- 忽略的異常默認只有 NoSuchElementException
通俗易懂的 WebDriverWait
WebDriverWait(driver實例, 超時時長, 調用頻率, 忽略的異常).until(要調用的 方法, 超時時返回的資訊)
WebDriverWait實例的兩個方法
until(self, method, message=”)
作用:每隔一段時間(上面的poll_frequency)調用method,直到返回值不為False或不為空
method:需要執行的method
message:拋出異常時的文案,會返回 TimeoutException ,表示超時
注意:這個才是常用的,如:定位元素直到不返回空
until_not(self, method, message=”)
作用:調用method,直到返回值為False或為空
method:需要執行的method
message:拋出異常時的文案,會返回 TimeoutException ,表示超時
兩個方法的 method參數注意點
如果直接傳入WebElement(頁面元素)對象
WebDriverWait(driver, 10).until(driver.find_element_by_id('kw'))
則會拋出異常
TypeError: 'xxx' object is not callable
method 參數需要傳入的對象必須包含 __call()__ 方法 ,什麼意思?讓對象可以直接被調用
官方提供的兩個小例子
element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).until_not(lambda x: x.find_element_by_id("someId").is_displayed())
可以看到,通過匿名函數也是可以的,可以說比後面介紹的 expected_conditions 模組要方便多了
那麼有哪些是包含 __call()__ 的對象呢?
- expected_conditions 模組(接下來重點講的)
- WebElement的 is_displayed() 、 is_enabled() 、 is_selected()
expected_conditions源碼解讀
expected_conditions的介紹
是selenium中的一個模組,包含一系列用於判斷的條件類,一共26個類
這裡就只介紹兩個在設置元素等待裡面最常用的判斷條件類
其一:presence_of_element_located
class presence_of_element_located(object): """ An expectation for checking that an element is present on the DOM of a page. This does not necessarily mean that the element is visible. locator - used to find the element returns the WebElement once it is located """ def __init__(self, locator): self.locator = locator def __call__(self, driver): return _find_element(driver, self.locator)
作用
檢查當前DOM樹種是否存在該元素(和是否可見沒有關係),只要有一個元素載入出來則通過
locator參數
傳入一個元組,格式如下 (By.ID, “元素ID“)
- 第一個參數:定位元素的方式,和那八種元素定位方式一樣,只是這裡需要引入 By 模組,然後再調用類屬性
- 第二個參數:和之前調用元素定位方法一樣傳參即可
- 所以正確寫法是: presence_of_element_located((By.ID, “kw“))
一起來看看By模組的源碼
class By(object): """ Set of supported locator strategies. """ ID = "id" XPATH = "xpath" LINK_TEXT = "link text" PARTIAL_LINK_TEXT = "partial link text" NAME = "name" TAG_NAME = "tag name" CLASS_NAME = "class name" CSS_SELECTOR = "css selector"
其二:presence_of_all_elements_located
源碼幾乎一樣
class presence_of_all_elements_located(object): def __init__(self, locator): self.locator = locator def __call__(self, driver): return _find_elements(driver, self.locator)
唯一要注意的點就是
- 因為調用的是 _find_elements ,會返回多個元素
- 如果用這個條件類,必須等所有匹配到的元素都載入出來才通過