Selenium之元素定位幾種方式


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標簽綁定事件,然后一切就會循環起來了。

see also:luffycity | pythonav

通過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表示所有父節點為CD元素。
  • *表示路徑的通配符,如/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 教程

 


免責聲明!

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



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