文章目錄
- 設定元素等待
- 顯式等待
- 隱式等待
- 強制等待
- 定位一組元素
- 切換操作
- 視窗切換
- 表單切換
- 彈窗處理
? 系列內容 ?
爬蟲+自動化利器 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 By
from selenium import webdriver
driver = 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 webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
driver = 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 webdriver
from time import time
driver = 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 webdriver
from time import time, sleep
driver = 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("//p[@class='name']")
name = [p.text for p in p_list]
name

切換操作
視窗切換
在 selenium 操作頁面的時候,可能會因為點擊某個鏈接而跳轉到一個新的頁面(打開了一個新標簽頁),這時候 selenium 實際還是處于上一個頁面的,需要我們進行切換才能夠定位最新頁面上的元素,
視窗切換需要使用 switch_to.windows() 方法,
首先我們先看看下面的代碼,
代碼流程:先進入 【CSDN首頁】,保存當前頁面的句柄,然后再點擊左側 【CSDN官方博客】跳轉進入新的標簽頁,再次保存頁面的句柄,我們驗證一下 selenium 會不會自動定位到新打開的視窗,

from selenium import webdriver
handles = []
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('//*[@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 webdriver
handles = []
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('//*[@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>Document</title>
<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丶Killer</p>
<p>微信:python-sun</p>
</div>
<iframe src="https://blog.csdn.net/qq_43965708" width="400" height="200"></iframe>
<!-- <iframe id="CSDN_info" name="Dream丶Killer" src="https://blog.csdn.net/qq_43965708" width="400" height="200"></iframe> -->
</body>
</html>

現在我們定位紅框中的 CSDN 按鈕,可以跳轉到 CSDN 首頁,
from selenium import webdriver
from pathlib import Path
driver = 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('//*[@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">alert</button>
<button id="confirm">confirm</button>
<button id="prompt">prompt</button>
<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 webdriver
from pathlib import Path
from time import sleep
driver = webdriver.Firefox()
driver.get('file:///' + str(Path(Path.cwd(), '彈窗.html')))
sleep(2)
# 點擊alert按鈕
driver.find_element_by_xpath('//*[@id="alert"]').click()
sleep(1)
alert = driver.switch_to.alert
# 列印alert彈窗的文本
print(alert.text)
# 確認
alert.accept()
sleep(2)
# 點擊confirm按鈕
driver.find_element_by_xpath('//*[@id="confirm"]').click()
sleep(1)
confirm = driver.switch_to.alert
print(confirm.text)
# 取消
confirm.dismiss()
sleep(2)
# 點擊confirm按鈕
driver.find_element_by_xpath('//*[@id="prompt"]').click()
sleep(1)
prompt = driver.switch_to.alert
print(prompt.text)
# 向prompt的輸入框中傳入文本
prompt.send_keys("Dream丶Killer")
sleep(2)
prompt.accept()
'''輸出
alert hello
confirm hello
prompt 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給她做了一個檔案整理工具??
更多有趣的文章及干貨,盡在
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/305986.html
標籤:python
上一篇:僅需10道題輕松掌握Python檔案處理 | Python技能樹征題
下一篇:Python中的運算子介紹(上)

