selenium提供了8種定位方式,下表列出了各個定位方式和在Python中對應的方法:
selenium中各定位方式 | 對應的Python方法 |
---|---|
id | find_element_by_id() |
name | find_element_by_name() |
class name | find_element_by_class_name() |
tag name | find_element_by_tag_name() |
link text | find_element_by_link_text() |
partial link text | find_element_by_partial_link_text() |
xpath | find_element_by_xpath() |
css selector | find_element_by_css_selector() |
接下來,具體的示例來學習一下各定位方式是如何使用的。
通過id獲取元素
打開Pythonav的注冊頁面。鍵盤F12
查看源碼,定位到用戶注冊輸入框(其他輸入框都是這么玩的),如下圖。
運行如下代碼:
from selenium import webdriver # 創建Chrome WebDriver實例 browser = webdriver.Chrome() # 獲取URL網頁 browser.get('https://pythonav.com/register/') # 根據id獲取對應的元素 username = browser.find_element_by_id('id_username') # 打印獲取結果 print(username) # <selenium.webdriver.remote.webelement.WebElement (session="2e4891511f03cc1baaa12c0426dc7b05", element="0.5508062663208302-1")> # 打印id print(id(username)) # 61771472 # 打印類型 print(type(username)) # <class 'selenium.webdriver.remote.webelement.WebElement'> # 關閉瀏覽器 browser.quit()
會看到,打開的瀏覽器,很快就關閉了,但我們成功的打印出結果,也就是獲取到了想要的元素了。
為指定元素的屬性賦值
現在,已經能定位到用戶名的輸入框,也就可以自動的填寫該input框,來試試看:
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('http://www.pythonav.com/register/') # 根據id獲取對應的元素 username = browser.find_element_by_id('id_username') # 為元素的value屬性賦值 username.send_keys('張開') # 睡3秒 time.sleep(3) # 清空元素的value屬性 username.clear() # 再睡2秒 time.sleep(2) # 關閉瀏覽器 browser.quit()
模擬短信驗證操作
繼續來模擬一下發送短信驗證碼的操作:
import time from selenium import webdriver browser = webdriver.Chrome() browser.get("http://www.pythonav.com/register/") # 根據id獲取對應的元素 phone = browser.find_element_by_id('id_telephone') # 回填手機號 phone.send_keys('18xxooxxoo98k') # 這里填寫一個真實可用的手機號 # 睡一會兒 time.sleep(5) # 獲取發送短信的按鈕id ele = browser.find_element_by_id('smsBtn') # 模擬按鈕的點擊事件 ele.click() time.sleep(2) browser.quit()
自動化注冊Pythonav網站
繼續完善自動用戶注冊的代碼:
import time from selenium import webdriver browser = webdriver.Chrome() browser.get("http://www.pythonav.com/register/") # 填寫用戶名 browser.find_element_by_id('id_username').send_keys('張開11') # 填寫密碼 browser.find_element_by_id('id_password').send_keys('zhangkai111') # 確認密碼 browser.find_element_by_id('id_confirm_password').send_keys('zhangkai111') # 回填手機號 browser.find_element_by_id('id_telephone').send_keys('18xxooxx0098k') # 需要一個真實的手機號 # 點擊獲取短信驗證碼 browser.find_element_by_id('smsBtn').click() # 睡它40秒,這里是為了等發送短信驗證碼,然后我們手動填進去 time.sleep(40) # 最后點擊注冊按鈕 browser.find_element_by_id('submit').click() # browser.find_element_by_id('submit').submit() time.sleep(100) browser.quit()
如果順利的收到驗證碼,並且在40秒內填進去了,然后就會發現,注冊成功了!下圖為證!
需要注意的是:按鈕提交時,除了click之外,還有一個submit,區別是click就是單純的點一下。而submit是完成了表單提交,需要攜帶表單信息進行提交的。如果是button按鈕的話,只能使用click,而不能使用submit。
name
通過PythonAV來學習name
定位是什么鬼。
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://pythonav.com/login/') # 通過標簽的name屬性 img = browser.find_element_by_name("username") print(img.tag_name) # input time.sleep(2) browser.quit()
上例,通過獲取用戶名的inpu框來完成定位。
需要注意的是:browser.find_element_by_name("username")
這種定位方式只能通過name
屬性來定位。
通過class name獲取單個元素屬性
通過路飛學城的登錄頁面來學習通過class name
定位。
由上圖可以看到,頁面中的記住密碼前的復選框,是由span標簽通過css樣式實現的。我們用class name
定位來完成自動勾選任務。
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/signin') # 通過class獲取span標簽 span = browser.find_element_by_class_name(name='no') # 點擊事件 span.click() print(span.get_attribute(name='class')) # no yes # 獲取當前元素的內容 print(span.text) # ✓ time.sleep(3) # 再次點擊span標簽 span.click() time.sleep(5) browser.quit()
實現相對簡單,再來點有點難度的,我們通過class name
定位來獲取路飛學城首頁輪播圖信息。
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/home') # 根據class獲取img元素 img = browser.find_element_by_class_name('banner') # 獲取元素的指定(src)屬性 print(img.get_attribute('src')) # https://hcdn1.luffycity.com/static/frontend/index/home-banner4_1535545832.4715614.png print(img.get_property('src')) # https://hcdn1.luffycity.com/static/frontend/index/home-banner4_1535545832.4715614.png time.sleep(5) browser.quit()
通過打印結果來看,我們已經成功的獲取到了指定的元素屬性。這里需要補充的是:get_attribute
方法獲取元素的特性,類似js
中的setattribute("","")
,自定義屬性設置,而get_property
方法也是獲取當前元素的屬性,但一般都是獲取原生的屬性,兩種方法有一定的互通性。
通過class name獲取多個元素屬性
上例代碼沒有問題,但我們僅獲取了一個圖片的屬性,那如何獲取多個呢?
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/home') # 根據class獲取img元素 imgs = browser.find_elements_by_class_name('banner') # 獲取元素的指定(src)屬性 print([i.get_attribute(name='src') for i in imgs]) time.sleep(5) browser.quit() '''打印結果 [ 'https://hcdn1.luffycity.com/static/frontend/index/home-banner4_1535545832.4715614.png', 'https://hcdn1.luffycity.com/static/frontend/index/PCbanner_1548828967.892487.jpeg', 'https://hcdn1.luffycity.com/static/frontend/activity/banner1_1553586989.5872993.png', 'https://hcdn1.luffycity.com/static/frontend/index/banner1(4)_1539945492.0492468.png', 'https://hcdn1.luffycity.com/static/frontend/index/banner_1553684636.466433.png', 'https://hcdn1.luffycity.com/static/frontend/index/banner11_1538122470.2779157.png', 'https://hcdn1.luffycity.com/static/frontend/index/home-banner4_1535545832.4715614.png', 'https://hcdn1.luffycity.com/static/frontend/index/PCbanner_1548828967.892487.jpeg' ] '''
上例中,獲取單個元素使用browser.find_element_by_class_name('banner')
,而要想獲取多個,就要使用browser.find_elements_by_class_name('banner')
,別忘了加s
!而當加了s
后,獲取到的是一個列表,而列表的話,不能直接img.get_attribute('src')
,而是想方設法的取列表中的每一個元素,我們對每一個元素再使用i.get_attribute(name='src')
就沒問題了。
see also:luffycity
限定span標簽范圍
還是通過路飛學城首頁的菜單列表來完成tag name
演示,我們要使用selenium自動完成點擊任務。
首先,要獲取所有的span標簽,但我們思考,這個頁面中有很多個span標簽,而只需要對這個幾個span標簽做點擊事件,所以,我們首先要限定范圍。經過一番觀察,先獲取所有span標簽外部的nav標簽。
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/home') nav = browser.find_element_by_tag_name(name='nav') print(nav.text) time.sleep(3) browser.quit() '''打印結果 免費課 輕課 學位課 題庫 '''
循環綁定click事件
有了范圍就好辦了,再從nav標簽內部找所有的span標簽,然后給每一個span標簽綁定一個click事件,然后自動執行就完成了。
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/home') # 獲取span的外部nav標簽 nav = browser.find_element_by_tag_name(name='nav') # 然后再獲取nav內部的所有span標簽 span_list = nav.find_elements_by_tag_name(name='span') for i in span_list: i.click() time.sleep(3) time.sleep(3) browser.quit() ''' selenium.common.exceptions.WebDriverException: Message: unknown error: Element <span _v-0ec42e90="">...</span> is not clickable at point (197, 45). Other element would receive the click: <div class="mbox" _v-5f981f7a="" style="">...</div> (Session info: chrome=73.0.3683.86) (Driver info: chromedriver=2.46.628402 (536cd7adbad73a3783fdc2cab92ab2ba7ec361e1),platform=Windows NT 10.0.14393 x86_64) '''
處理彈出框
上例代碼邏輯沒有問題!但是報錯了,觀察報錯信息(說的好像看的懂似的!),每當打開路飛學城首頁的時候,首先會彈出一個彈出框,不把它叉掉就沒有辦法干別的,肯定是它搞的鬼,必須先干掉他才能執行后續的點擊事件!
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/home') time.sleep(1) # 首先關閉彈出框 browser.find_element_by_class_name('guan').click() time.sleep(1) # 獲取span的外部nav標簽 nav = browser.find_element_by_tag_name(name='nav') # 然后再獲取nav內部的所有span標簽 span_list = nav.find_elements_by_tag_name(name='span') for i in span_list: i.click() time.sleep(3) time.sleep(3) browser.quit()
上例代碼,首先關閉彈出框,在為nav標簽內部的所有span標簽綁定事件,然后一切就會循環起來了。
通過link text定位
通過PythonAV來學習link text/partial link text
定位。
由上圖可以看到,PythonAV和路飛學城在標簽切換這里是不一樣的,PythonAV是a標簽(畢竟是av嘛)!而路飛學城是span標簽。所以,針對PythonAV,采用另一個元素定位方式來完成。
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://pythonav.com/index/') time.sleep(2) # 獲取超鏈接,並且點擊 browser.find_element_by_link_text('免費視頻').click() time.sleep(3) browser.quit()
上例,使用browser.find_element_by_link_text('免費視頻').click()
來完成定位和點擊任務。
通過partial link text定位
那partial link text
是什么鬼?
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://pythonav.com/index/') browser.find_element_by_partial_link_text('免費').click() time.sleep(2) browser.find_element_by_partial_link_text('知識庫').click() time.sleep(2) # browser.find_element_by_link_text('內部').click() # 報錯 browser.find_element_by_link_text('內部資料').click() # 報錯 time.sleep(2) browser.quit()
由上例可以看到,使用partial link text
定位時,使用標簽連接內容部分內容即可,而通過link text
定位時,鏈接內容必去是完全匹配才能成功。
小結:
link text/partial link text
都僅能用於超鏈接標簽的定位,其他的標簽都不可以。link text
是精確定位,也就是說超鏈接內的內容必須完全匹配才能定位成功。partial link text
是模糊定位,只要超鏈接的內容包含該指定內容既可以匹配到。根據不同的場景,需謹慎使用。
css selector
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/home') time.sleep(1) # 首先關閉彈出框 browser.find_element_by_class_name('guan').click() # 通過css selector res = browser.find_element_by_css_selector('.luffy-home .title p[_v-2f0761bc]') print(res.tag_name) # p time.sleep(2) browser.quit() 除此之外,css selector還可以這么用: import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/home') time.sleep(1) # 首先關閉彈出框 browser.find_element_by_css_selector("img[class='guan']").click() time.sleep(2) browser.quit()
雖然css selector
定位不如id class tag
等定位方法直接,但是css selector
性能高,尤其是在IE瀏覽器中(老版IE瀏覽器對XPath支持不太好,尤其是IE11之前)。並且selenium也推薦使用css selector
定位。
see also:【Selenium專題】元素定位之CssSelector
XPath
選取節點
XPath 使用路徑表達式在 XML 文檔中選取節點。節點是通過沿着路徑或者 step 來選取的。
下表列舉了常用的路徑表達式:
表達式 | 描述 |
---|---|
node name | 選取此節點的所有子節點 |
/ | 從根節點選取 |
// | 從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置 |
. | 選取當前節點 |
.. | 選取當前節點的父節點 |
@ | 選取屬性 |
實例
下表列舉了一些表達式的應用及描述:
路徑表達式 | 描述 |
---|---|
bookstore | 選取bookstore元素的所有子節點 |
/bookstore | 選取根元素bookstore。如果路徑起始於/ ,則此路徑始終代表到某元素的絕對路徑 |
bookstore/book | 選取屬於bookstore元素下的所有book元素 |
//book | 選取book子元素,而不管它們在文檔中的位置 |
bookstore//book | 選擇屬於bookstore元素的所有book元素,而不管它們位於bookstore之下的什么位置 |
//@book | 選取名為book的所有屬性 |
謂語(Predicates)
謂語用來查找某個特定的節點或者包含某個指定的值的節點。謂語被嵌在方括號中。
下表列舉了帶有謂語的一些路徑表達式,以及表達式結果:
路徑表達式 | 結果 |
---|---|
/bookstore/book[1] | 選取屬於bookstore子元素的第一個book元素 |
/bookstore/book[last()] | 選取屬於bookstore子元素的最后一個book元素 |
/bookstore/book[last()-1] | 選取屬於bookstore子元素的倒數第二個book元素 |
/bookstore/book[position()<3] | 選取最前面的兩個屬於bookstore元素的子元素的book元素 |
//title[@book] | 選取所有擁有名為book的屬性的title元素 |
//title[@book='lang'] | 選取所有book屬性為lang的title元素 |
/bookstore/book[price>35.00] | 選取bookstore元素的所有book元素,並且其中price屬性的值必須大於35.00 |
/bookstore/book[price>35.00]/title | 選取bookstore元素中的title元素,過濾條件是price元素值必須大於35.00 |
選取未知節點
XPath 通配符可用來選取未知的 XML 元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素節點 |
@* | 匹配任何屬性節點 |
node() | 匹配任何類型的節點 |
實例
下表中列舉了一些路徑表達式及對應的結果:
路徑表達式 | 結果 |
---|---|
/bookstore/* | 選取bookstore元素的所有子元素 |
//* | 選取文檔中的所有元素 |
//title[@*] | 選取所有帶有屬性的title元素 |
選取若干路徑
通過在路徑表達式中使用|
運算符,我們可以選取若干個路徑。
實例
下表中列舉了一些路徑表達式及對應的結果:
路徑表達式 | 結果 |
---|---|
//book/title | //book/price | 選取book元素下所有title和price元素 |
//title | //price | 選取文檔中所有title和price元素 |
/bookstore/book/title | //price | 選取屬於bookstore元素下的book元素中的所有title元素,以及文檔中所有price元素 |
是時候寫點代碼了:
import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.luffycity.com/home') time.sleep(1) # 首先關閉彈出框 browser.find_element_by_css_selector("img[class='guan']").click() img = browser.find_element_by_xpath('//ul/li/img[@_v-2f0761bc]') # img = browser.find_element_by_xpath('/ul/li/img[@_v-2f0761bc]') # 報錯 print(img.get_attribute('src')) time.sleep(2) browser.quit()
上例中,報錯的原因是在根據XPath找路徑的時候,其實位置如果是單個/
,表示絕對路徑,要從HTML的根節點開始查找,而雙//
則是相對路徑。
除此之外,Chrome瀏覽器還支持無腦獲取標簽的XPath,那就是萬能的右鍵copy XPath
:
妥妥的准!
節點匹配
再介紹一些XPath中節點匹配的基本方法。
路徑匹配
路徑匹配與文件路徑的表示相仿,比較好理解,有以下幾個符號:
- 用
/
表示節點路徑,如/A/B/C
表示節點A
的子節點B
的子節點C
,/
表示根節點。 - 用
//
表示所有路徑以//
后指定的子路徑結尾的元素,如//D
表示所有的D
元素;如果是//C/D
表示所有父節點為C
的D
元素。 - 用
*
表示路徑的通配符,如/A/B/C/*
表示A
元素下的B
元素下的C
元素下的所有子元素。
位置匹配
對於每一個元素,它的各個子元素都是有序的:
/A/B/C[1]
表示A
元素下的B
元素下的C
元素下的第一個子元素。/A/B/C[last()]
表示A
元素下的B
元素下的C
元素下最后一個子元素。/A/B/C[position()>2]
表示A
元素下的B
元素下的C
元素下的位置號大於2的元素。
屬性及屬性值
在XPath中可以利用屬性及屬性值來匹配元素,需要注意的是:元素的屬性名前要有@
前綴,例如:
//B[@id]
表示所有具有屬性id的B元素。//B[@*]
表示所有具有屬性的B元素。//B[not(@*)]
表示所有不具有屬性的B元素。//B[@id="b1"]
表示id值為b1
的B元素。
see also: XPath 教程