Selenium系列(六) - 詳細解讀強制等待、隱式等待、顯式等待的區別和源碼解讀


如果你還想從頭學起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 ,會返回多個元素
  • 如果用這個條件類,必須等所有匹配到的元素都加載出來才通過

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM