摘要:耗時代碼運行到這句之后觸發隱式等待,在輪詢檢查后仍然沒有定位到元素,拋出異常。耗時值得一提的是,對于定位不到元素的時候,從耗時方面隱式等待和強制等待沒什么區別。
? 系列內容 ?
爬蟲+自動化利器 selenium 之自學成才篇(一)
主要內容:selenium 簡介、selenium 安裝、安裝瀏覽器驅動、8 種方式定位頁面元素、瀏覽器控制、鼠標控制、鍵盤控制
爬蟲+自動化利器 selenium 之自學成才篇(二)
主要內容:三種等待方式(顯式等待、隱式等待、強制等待)、一組元素的定位方式、切換操作(窗口切換、表單切換)、彈窗處理等。
爬蟲+自動化利器 selenium 之自學成才篇(三)
主要內容:文件上傳 & 下載、cookie 操作、調用 JavaScript(滑動滾動條)、關閉操作、頁面截圖等。
很多頁面都使用 ajax
技術,頁面的元素不是同時被加載出來的,為了防止定位這些尚在加載的元素報錯,可以設置元素等來增加腳本的穩定性。webdriver
中的等待分為 顯式等待 和 隱式等待。
顯式等待:設置一個超時時間,每個一段時間就去檢測一次該元素是否存在,如果存在則執行后續內容,如果超過最大時間(超時時間)則拋出超時異常(TimeoutException
)。顯示等待需要使用 WebDriverWait
,同時配合 until
或 not until
。下面詳細講解一下。
WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
driver
:瀏覽器驅動timeout
:超時時間,單位秒poll_frequency
:每次檢測的間隔時間,默認為0.5秒ignored_exceptions
:指定忽略的異常,如果在調用 until
或 until_not
的過程中拋出指定忽略的異常,則不中斷代碼,默認忽略的只有 NoSuchElementException
。until(method, message=’ ‘)
until_not(method, message=’ ")
method
:指定預期條件的判斷方法,在等待期間,每隔一段時間調用該方法,判斷元素是否存在,直到元素出現。until_not
正好相反,當元素消失或指定條件不成立,則繼續執行后續代碼message
: 如果超時,拋出 TimeoutException
,并顯示 message
中的內容method
中的預期條件判斷方法是由 expected_conditions
提供,下面列舉常用方法。
先定義一個定位器
from selenium.webdriver.common.by import Byfrom selenium import webdriverdriver = webdriver.Chrome()locator = (By.ID, "kw")element = driver.find_element_by_id("kw")
方法 | 描述 |
---|---|
title_is(‘百度一下’) | 判斷當前頁面的 title 是否等于預期 |
title_contains(‘百度’) | 判斷當前頁面的 title 是否包含預期字符串 |
presence_of_element_located(locator) | 判斷元素是否被加到了 dom 樹里,并不代表該元素一定可見 |
visibility_of_element_located(locator) | 判斷元素是否可見,可見代表元素非隱藏,并且元素的寬和高都不等于0 |
visibility_of(element) | 跟上一個方法作用相同,但傳入參數為 element |
text_to_be_present_in_element(locator , ‘百度’) | 判斷元素中的 text 是否包含了預期的字符串 |
text_to_be_present_in_element_value(locator , ‘某值’) | 判斷元素中的 value 屬性是否包含了預期的字符串 |
frame_to_be_available_and_switch_to_it(locator) | 判斷該 frame 是否可以 switch 進去,True 則 switch 進去,反之 False |
invisibility_of_element_located(locator) | 判斷元素中是否不存在于 dom 樹或不可見 |
element_to_be_clickable(locator) | 判斷元素中是否可見并且是可點擊的 |
staleness_of(element) | 等待元素從 dom 樹中移除 |
element_to_be_selected(element) | 判斷元素是否被選中,一般用在下拉列表 |
element_selection_state_to_be(element, True) | 判斷元素的選中狀態是否符合預期,參數 element,第二個參數為 True/False |
element_located_selection_state_to_be(locator, True) | 跟上一個方法作用相同,但傳入參數為 locator |
alert_is_present() | 判斷頁面上是否存在 alert |
下面寫一個簡單的例子,這里定位一個頁面不存在的元素,拋出的異常信息正是我們指定的內容。
from selenium import webdriverfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Bydriver = webdriver.Chrome()element = WebDriverWait(driver, 5, 0.5).until( EC.presence_of_element_located((By.ID, "kw")), message="超時啦!")
隱式等待也是指定一個超時時間,如果超出這個時間指定元素還沒有被加載出來,就會拋出 NoSuchElementException
異常。
除了拋出的異常不同外,還有一點,隱式等待是全局性的,即運行過程中,如果元素可以定位到,它不會影響代碼運行,但如果定位不到,則它會以輪詢的方式不斷地訪問元素直到元素被找到,若超過指定時間,則拋出異常。
使用 implicitly_wait()
來實現隱式等待,使用難度相對于顯式等待要簡單很多。
示例:打開個人主頁,設置一個隱式等待時間 5s,通過 id
定位一個不存在的元素,最后打印 拋出的異常 與 運行時間。
from selenium import webdriverfrom time import timedriver = webdriver.Chrome()driver.get("https://blog.csdn.net/qq_43965708")start = time()driver.implicitly_wait(5)try: driver.find_element_by_id("kw")except Exception as e: print(e) print(f"耗時:{time()-start}")
代碼運行到 driver.find_element_by_id("kw")
這句之后觸發隱式等待,在輪詢檢查 5s 后仍然沒有定位到元素,拋出異常。
使用 time.sleep()
強制等待,設置固定的休眠時間,對于代碼的運行效率會有影響。以上面的例子作為參照,將 隱式等待 改為 強制等待。
from selenium import webdriverfrom time import time, sleepdriver = webdriver.Chrome()driver.get("https://blog.csdn.net/qq_43965708")start = time()sleep(5)try: driver.find_element_by_id("kw")except Exception as e: print(e) print(f"耗時:{time()-start}")
值得一提的是,對于定位不到元素的時候,從耗時方面隱式等待和強制等待沒什么區別。但如果元素經過 2s 后被加載出來,這時隱式等待就會繼續執行下面的代碼,但 sleep還要繼續等待 3s。
上篇講述了定位一個元素的 8 種方法,定位一組元素使用的方法只需要將 element
改為 elements
即可,它的使用場景一般是為了批量操作元素。
find_elements_by_id()
find_elements_by_name()
find_elements_by_class_name()
find_elements_by_tag_name()
find_elements_by_xpath()
find_elements_by_css_selector()
find_elements_by_link_text()
find_elements_by_partial_link_text()
這里以 CSDN 首頁的一個 博客專家欄 為例。
下面使用 find_elements_by_xpath
來定位三位專家的名稱。
這是專家名稱部分的頁面代碼,不知各位有沒有想到如何通過 xpath
定位這一組專家的名稱呢?
from selenium import webdriver# 設置無頭瀏覽器option = webdriver.ChromeOptions()option.add_argument("--headless")driver = webdriver.Chrome(options=option)driver.get("https://blog.csdn.net/")p_list = driver.find_elements_by_xpath("http://p[@class="name"]")name = [p.text for p in p_list]name
在 selenium
操作頁面的時候,可能會因為點擊某個鏈接而跳轉到一個新的頁面(打開了一個新標簽頁),這時候 selenium
實際還是處于上一個頁面的,需要我們進行切換才能夠定位最新頁面上的元素。
窗口切換需要使用 switch_to.windows()
方法。
首先我們先看看下面的代碼。
代碼流程:先進入 【CSDN首頁】,保存當前頁面的句柄,然后再點擊左側 【CSDN官方博客】跳轉進入新的標簽頁,再次保存頁面的句柄,我們驗證一下 selenium
會不會自動定位到新打開的窗口。
from selenium import webdriverhandles = []driver = webdriver.Chrome()driver.get("https://blog.csdn.net/")# 設置隱式等待driver.implicitly_wait(3)# 獲取當前窗口的句柄handles.append(driver.current_window_handle)# 點擊 python,進入分類頁面driver.find_element_by_xpath("http://*[@id="mainContent"]/aside/div[1]/div").click()# 獲取當前窗口的句柄handles.append(driver.current_window_handle)print(handles)# 獲取當前所有窗口的句柄print(driver.window_handles)
可以看到第一個列表 handle
是相同的,說明 selenium
實際操作的還是 CSDN首頁 ,并未切換到新頁面。
下面使用 switch_to.windows()
進行切換。
from selenium import webdriverhandles = []driver = webdriver.Chrome()driver.get("https://blog.csdn.net/")# 設置隱式等待driver.implicitly_wait(3)# 獲取當前窗口的句柄handles.append(driver.current_window_handle)# 點擊 python,進入分類頁面driver.find_element_by_xpath("http://*[@id="mainContent"]/aside/div[1]/div").click()# 切換窗口driver.switch_to.window(driver.window_handles[-1])# 獲取當前窗口的句柄handles.append(driver.current_window_handle)print(handles)print(driver.window_handles)
上面代碼在點擊跳轉后,使用 switch_to
切換窗口,window_handles
返回的 handle
列表是按照頁面出現時間進行排序的,最新打開的頁面肯定是最后一個,這樣用 driver.window_handles[-1]
+ switch_to
即可跳轉到最新打開的頁面了。
那如果打開的窗口有多個,如何跳轉到之前打開的窗口,如果確實有這個需求,那么打開窗口是就需要記錄每一個窗口的 key
(別名) 與 value
(handle
),保存到字典中,后續根據 key
來取 handle
。
很多頁面也會用帶 frame/iframe
表單嵌套,對于這種內嵌的頁面 selenium
是無法直接定位的,需要使用 switch_to.frame()
方法將當前操作的對象切換成 frame/iframe
內嵌的頁面。
switch_to.frame()
默認可以用的 id
或 name
屬性直接定位,但如果 iframe
沒有 id
或 name
,這時就需要使用 xpath
進行定位。下面先寫一個包含 iframe
的頁面做測試用。
DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> <style> div p { color: #red; animation: change 2s infinite; } @keyframes change { from { color: red; } to { color: blue; } } style>head><body> <div> <p>公眾號:Python新視野p> <p>CSDN:Dream丶Killerp> <p>微信:python-sunp> div> <iframe src="https://blog.csdn.net/qq_43965708" width="400" height="200">iframe>body>html>
現在我們定位紅框中的 CSDN 按鈕,可以跳轉到 CSDN 首頁。
from selenium import webdriverfrom pathlib import Pathdriver = webdriver.Chrome()# 讀取本地html文件driver.get("file:///" + str(Path(Path.cwd(), "iframe測試.html")))# 1.通過id定位driver.switch_to.frame("CSDN_info")# 2.通過name定位# driver.switch_to.frame("Dream丶Killer")# 通過xpath定位# 3.iframe_label = driver.find_element_by_xpath("/html/body/iframe")# driver.switch_to.frame(iframe_label)driver.find_element_by_xpath("http://*[@id="csdn-toolbar"]/div/div/div[1]/div/a/img").click()
這里列舉了三種定位方式,都可以定位 iframe
。
JavaScript
有三種彈窗 alert
(確認)、confirm
(確認、取消)、prompt
(文本框、確認、取消)。
處理方式:先定位(switch_to.alert
自動獲取當前彈窗),再使用 text
、accept
、dismiss
、send_keys
等方法進行操作
方法 | 描述 |
---|---|
text | 獲取彈窗中的文字 |
accept | 接受(確認)彈窗內容 |
dismiss | 解除(取消)彈窗 |
send_keys | 發送文本至警告框 |
這里寫一個簡單的測試頁面,其中包含三個按鈕,分別對應三個彈窗。
DOCTYPE html><html lang="en"><head>head><body> <button id="alert">alertbutton> <button id="confirm">confirmbutton> <button id="prompt">promptbutton> <script type="text/javascript"> const dom1 = document.getElementById("alert") dom1.addEventListener("click", function(){ alert("alert hello") }) const dom2 = document.getElementById("confirm") dom2.addEventListener("click", function(){ confirm("confirm hello") }) const dom3 = document.getElementById("prompt") dom3.addEventListener("click", function(){ prompt("prompt hello") }) script>body>html>
下面使用上面的方法進行測試。為了防止彈窗操作過快,每次操作彈窗,都使用 sleep
強制等待一段時間。
from selenium import webdriverfrom pathlib import Pathfrom time import sleepdriver = webdriver.Firefox()driver.get("file:///" + str(Path(Path.cwd(), "彈窗.html")))sleep(2)# 點擊alert按鈕driver.find_element_by_xpath("http://*[@id="alert"]").click()sleep(1)alert = driver.switch_to.alert# 打印alert彈窗的文本print(alert.text)# 確認alert.accept()sleep(2)# 點擊confirm按鈕driver.find_element_by_xpath("http://*[@id="confirm"]").click()sleep(1)confirm = driver.switch_to.alertprint(confirm.text)# 取消confirm.dismiss()sleep(2)# 點擊confirm按鈕driver.find_element_by_xpath("http://*[@id="prompt"]").click()sleep(1)prompt = driver.switch_to.alertprint(prompt.text)# 向prompt的輸入框中傳入文本prompt.send_keys("Dream丶Killer")sleep(2)prompt.accept()"""輸出alert helloconfirm helloprompt hello"""
注:細心地讀者應該會發現這次操作的瀏覽器是
Firefox
,為什么不用Chrome
呢?原因是測試時發現執行prompt
的send_keys
時,不能將文本填入輸入框。嘗試了各種方法并查看源碼后確認不是代碼的問題,之后通過其他渠道得知原因可能是Chrome
的版本與selenium
版本的問題,但也沒有很方便的解決方案,因此沒有繼續深究,改用Firefox
可成功運行。這里記錄一下我的Chrome
版本,如果有大佬懂得如何在Chrome
上解決這個問題,請在評論區指導一下,提前感謝!
selenium:3.141.0
Chrome:94.0.4606.71
未完待續~
??往期精彩,不容錯過??
總結篇
??兩萬字,50個pandas高頻操作【圖文并茂,值得收藏】??
??吐血總結《Mysql從入門到入魔》,圖文并茂(建議收藏)??
工具篇
??Python實用小工具之制作酷炫二維碼(有界面、附源碼)??
??Python實用工具之制作證件照(有界面、附源碼)??
??女朋友桌面文件雜亂無章?氣得我用Python給她做了一個文件整理工具??
更多有趣的文章及干貨,盡在
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/122012.html
文章目錄 selenium 簡介selenium安裝安裝瀏覽器驅動確定瀏覽器版本下載驅動 定位頁面元素打開指定頁面id 定位name 定位class 定位tag 定位xpath 定位css 定位link 定位partial_link 定位 瀏覽器控制修改瀏覽器窗口大小瀏覽器前進&后退瀏覽器刷新瀏覽器窗口切換常見操作 鼠標控制單擊左鍵單擊右鍵雙擊拖動鼠標懸停 鍵盤控制 seleni...
摘要:楚江數據是專業的互聯網數據技術服務,現整理出零基礎如何學爬蟲技術以供學習,。本文來源知乎作者路人甲鏈接楚江數據提供網站數據采集和爬蟲軟件定制開發服務,服務范圍涵蓋社交網絡電子商務分類信息學術研究等。 楚江數據是專業的互聯網數據技術服務,現整理出零基礎如何學爬蟲技術以供學習,http://www.chujiangdata.com。 第一:Python爬蟲學習系列教程(來源于某博主:htt...
摘要:時間永遠都過得那么快,一晃從年注冊,到現在已經過去了年那些被我藏在收藏夾吃灰的文章,已經太多了,是時候把他們整理一下了。那是因為收藏夾太亂,橡皮擦給設置私密了,不收拾不好看呀。 ...
摘要:軟件測試江湖二神兵利器篇在上一篇文章中我們介紹了江湖上流傳的各種軟件測試的武功秘籍和心法,相信看過的小伙伴內力得到了很大的提升。功能測試篇功能測試,是軟件測試里的入門級心法,自然也有與之相對應的兵器來發揮心法的最大功力。 軟件測試江湖(二)神兵利器篇 在上一篇文章中我們介紹了江湖上流傳的各種軟件測試的武功秘籍和心法,相信看過的小伙伴內力得到了很大的提升。如果沒有,一定是你看的姿勢不對,...
摘要:軟件測試江湖二神兵利器篇在上一篇文章中我們介紹了江湖上流傳的各種軟件測試的武功秘籍和心法,相信看過的小伙伴內力得到了很大的提升。功能測試篇功能測試,是軟件測試里的入門級心法,自然也有與之相對應的兵器來發揮心法的最大功力。 軟件測試江湖(二)神兵利器篇 在上一篇文章中我們介紹了江湖上流傳的各種軟件測試的武功秘籍和心法,相信看過的小伙伴內力得到了很大的提升。如果沒有,一定是你看的姿勢不對,...
閱讀 2737·2021-10-09 09:44
閱讀 3549·2019-08-30 15:54
閱讀 2159·2019-08-30 14:16
閱讀 2790·2019-08-30 13:09
閱讀 825·2019-08-30 13:08
閱讀 1280·2019-08-29 16:29
閱讀 1661·2019-08-26 13:57
閱讀 1925·2019-08-26 13:53