為了更好的讓程式運行,或多或少會需要使用等待方式,順利的讓driver取得元素後順利運行,本篇會提到Selenium的三種等待方式,以及常用的操作語法。
等待方式有三種,分別是強制等待、隱性等待、顯性等待:
from time import sleep
#強制等待10秒
sleep(10)
強制等待在不管任何情況下,等待設定的時間,簡單易用,等待期間會完全暫停程式的執行,可用於單純的等待場景。
缺點:因為是無條件的固定等待時間,在一些比較複雜的情況下無法設定自己想要的情境,如果使用強制等待的目的是讓頁面某個元素出現,即便元素出現了,也是要繼續等待。
#隱性等待10秒
driver.implicitly_wait(10)
隱性等待不用導入任何套件,是一個全局設定,整個WebDriver都適用,當等待元素出現在頁面時,就會繼續執行後面的程式,或是等待設定的秒數。
可以跟顯性等待混用,會取時間長的那個。
缺點:等待時間不夠精確,每個場景所需要的等待時間不一定一樣,沒辦法個別針對場景進行設定等待時間,會造成資源不必要的浪費,另外也沒有辦法特定等待條件:比如點一個按鈕,才會出現提示框,這種隱性等待就沒有辦法處理。
#顯性等待
from selenium.webdriver.support.ui import WebDriverWait
#預期條件函數,網頁元素在瀏覽器中的的出現、可見、可點擊
from selenium.webdriver.support import expected_conditions
#等待超時異常處理
from selenium.common.exceptions import TimeoutException
#創造一個顯性等待,等待時間20秒,每1秒檢查一次、忽略找不到元素的錯誤
driver_wait = WebDriverWait(driver, timeout=20, poll_frequency=1, ignored_exceptions=[NoSuchElementException])
顯性等待可以設定許多條件,來達到完全我們需要的場景,明確等待某個特定條件,等出現後才會去執行:
timeout,等待時間,以秒為單位,強制等待跟隱性等待的等待時間也是以秒為單位。
poll_frequency,指定等待條件檢查的頻率,以秒為單位,預設值是0.5秒,可以不設定,如果不設定的情況下,那WebDriver會按照預設值每0.5秒檢查一次元素是否出現,有設定則按照預設值。
ignored_exceptions,在等待期間希望忽略的例外錯誤,有些錯誤會導致程式的中斷,它可以很好的讓程式順利運行,常見的忽略類型如下:
顯性等待語法我們可以簡化成:
#去除掉參數文字,簡化的情況下,順序一定要是 時間、頻率、忽略例外錯誤
driver_wait = WebDriverWait(driver, 20, 0.5, [NoSuchElementException])
如果想同時忽略多個例外錯誤,直接後面繼續加就可以了:
#同時忽略多個錯誤
driver_wait = WebDriverWait(driver, 20, 1, [NoSuchElementException, ElementNotVisibleException, StaleElementReferenceException])
#這裡我們拿expected_conditions的字首e跟c出來取別名叫做EC,不然整串程式碼太長了
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
driver.get("https://www.google.com/")
#等待時間20秒,搜尋頻率1秒一次
driver_wait = WebDriverWait(driver,20,1)
#顯性等待抓取元素
search_box = driver_wait.until(EC.presence_of_element_located((By.NAME,"q")))
#隱性等待抓取元素(對照組)
search_box = driver.find_element(By.NAME,"q")
說明:
1.創建WebDriverWait的物件時,需要有兩個參數是必要的,分別是瀏覽器的控制跟等待時間,這裡我們瀏覽器的控制是"driver",後面必填的是等待時間(timeout),至於頻率跟忽略例外錯誤是選配,可加可不加。
2.需要三層( )的原因是:
driver_wait.until( ) 第一層,用來指示要使用顯性等待。
EC.presence_of_element_located( )第二層,表示要使用的方法,像這裡是使用"元素出現"這個等待條件。
(By.NAME,"q")第三層,定位元素本身作為一種參數,外面需要一層套用在第二層上。
以下是 expected_conditions,或是說顯性等待常見的幾種等待條件:
presence_of_element_located:元素出現,元素存在DOM中並可以使用元素定位方法取得,字尾"located"指的是Locator(元素定位方法),像是(By.NAME,"皮卡丘")。
#presence_of_element_located
search_box = driver_wait.until(EC.presence_of_element_located((By.NAME,"q")))
presence_of_all_elements_located:所有元素出現,元素存在DOM,並透過元素定位方法,把指定的元素都找出來,並形成一個陣列。
#presence_of_all_elements_located
search_results = driver_wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "div.g")))
visibility_of_element_located:元素可見,元素存在瀏覽器畫面上,即元素沒有被隱藏(display:none、visibility:hidden),並且高寬都大於0,可用Locator定位。
#visibility_of_element_located
search_box = driver_wait.until(EC.visibility_of_element_located((By.NAME,"q")))
element_to_be_clickable:元素可點擊,元素存在瀏覽器畫面,沒有被隱藏,並且可點擊,可用Locator定位。
#element_to_be_clickable
search_box = driver_wait.until(EC.element_to_be_clickable((By.NAME,"q")))
search_box.click()
text_to_be_present_in_element:文本出現,等待網頁DOM可見,並且指定的元素出現特定的文本。
#text_to_be_present_in_element
search_box = driver_wait.until(EC.text_to_be_present_in_element((By.ID, "pikachu"), "皮卡丘"))
alert_is_present:出現彈出窗口,當瀏覽器畫面出現彈窗時,做相應的處理。
#alert_is_present,出現彈窗,印出提示,並接受
alert = driver_wait.until(EC.alert_is_present())
alert_text = alert.text
print("彈窗內容顯示:", alert_text)
alert.accept()
註:DOM,文件物件模型(Document Object Model),簡單來說就是瀏覽器開發者模式中,元素分頁那頁的內容。
至於顯性等待的缺點:它有點複雜