元素定位專項之:Xpath


Xpath簡介

Xpath(Xml Path Language),即XML路徑語言,它是一種用來確定XML標准通用標記語言的子集)文檔中某部分位置的語言。XPath基於XML的樹狀結構,提供在數據結構樹中找尋節點功能

Xpath用途

HTML文檔本身就是一個標准的XML頁面,所以在web測試項目中使用Xpath來定位元素,使用xpath幾乎可以定位到頁面上的任意元素。

Xpath語法

下面是常用的xpath的路徑表達式:

 

表達式 描述
/ 從根節點選取。
// 從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置。
@ 選取屬性。

下面通過實際案例來實踐上面幾種表達式的用法:

上圖是某網頁的的登錄頁面,其HTML代碼如下(僅截取部分):

使用絕對路徑定位元素

我們使用絕對路徑定位用戶名輸入框,如下:

driver = webdriver.Firefox()
driver.get('https://192.168.45.163/Terminal/logon.do')
user=driver.find_element(By.XPATH, '/html/body/div[2]/div/div/div[1]/table/tbody/tr[2]/td[3]/input')

絕對路徑的表達式/開頭,XPath 的絕對路徑主要用標簽名的層級關系來定位元素的絕對路徑。最外層為html 語言,body 文本內,一級一級往下查找,如果一個層級下有多個相同的標簽名,那么就按上下順序確定是第幾個,div[2]表示第二個div 標簽。

使用相對路徑定位元素

下面示例使用相對路徑定位登錄界面的所有輸入框,說明下:該登錄框總計有5個input標簽元素,代碼如下:

inputs=driver.find_elements(By.XPATH, '//input')
print len(inputs)
for input in inputs:
    print input.tag_name
    input.send_keys("s")

說明:代碼目的是找到登錄頁面中的一組input標簽——>打印出總數——>循環打印出所有的input標簽的tag_name,並給每個輸入框賦值:s。

這里要注意以下幾點

1. 一般不推薦使用絕對路徑的寫法,因為一旦頁面結構發生變化,該路徑也隨之失效,必須重新寫。

2. 絕對路徑以單/號表示,而相對路徑則以//表示:

  • 當xpath的路徑以/開頭時,表示讓Xpath解析引擎從文檔的根節點開始解析
  • 當xpath路徑以//開頭時,則表示讓xpath引擎從文檔的任意符合的元素節點開始進行解析。
  • 當/出現在xpath路徑中時,則表示尋找父節點的直接子節點
  • 當//出現在xpath路徑中時,表示尋找父節點下任意符合條件的子節點,不管嵌套了多少層級

下面通過示例驗證2中的說明:

input=driver.find_elements(By.XPATH,'//tr//input')
print len(input)

代碼輸出結果為:5

input=driver.find_elements(By.XPATH,'//tr/input')
print len(input)

代碼輸出結果為:0

因為input標簽並非tr標簽的直接子節點,所以在路徑中使用/,定位的結果為0;而使用//則表示不限制為直接子節點,所以結果為5

利用元素屬性定位

下面通過示例說明利用屬性定位:

Id屬性定位
user=driver.find_element(By.XPATH, "//*[@id='inputUserName']")
user.send_keys("guest")

如果不想指定標簽名也可以用星號(*)代替,上述方法表示通過id定位元素,該方法其實等同於id定位

user=driver.find_element(By.XPATH, "//input[@id='inputUserName']")
user.send_keys("guest")

//表示當前頁面某個目錄下,input 表示定位元素的標簽名,[@id='inputUserName'] 表示這個元素的id 屬性值等於'inputUserName',同樣也可以使用其他元素的屬性值進行元素定位,下面示例通過type 和class 屬性值來定位:

Type屬性定位
pwd=driver.find_element(By.XPATH, "//input[@type='password']")
pwd.send_keys("guest")

通過密碼輸入框的type屬性值來定位

Class屬性定位
code=driver.find_element(By.XPATH, "//input[@class='codeInput']")
code.send_keys("guest")

通過驗證碼輸入框的class屬性來定位

name屬性定位
name=driver.find_element(By.XPATH, "//input[@name='userName']")
name.send_keys("guest")

通過用戶名輸入框的name屬性來定位

value屬性定位
val=driver.find_element(By.XPATH, "//input[@value='資源下載']")
val.click()

通過資源下載按鈕的vlaue屬性看來定位

層級與屬性結合定位

如果一個元素本身並沒有可以唯一標識這個元素的屬性值,我們可以找其上一級元素,如果它的上級有可以唯一標識屬性的值,也可以拿來使用,如下:通過定位到用戶輸入框的元素的父元素,來定位目標元素。

val=driver.find_element(By.XPATH,//div[@id='loginFormField']/descendant::input[1]")
val.send_keys("ha")

注意,上面有用到xpath軸的只是,descendant是指定位到div的input子孫元素,后面的【1】是索引,表示符合條件的元素中的第一個

使用邏輯運算符

下面為w3c School中列出的可用在 XPath 表達式中的運算符:

運算符

描述

實例

返回值

|

計算兩個節點集

//book | //cd

返回所有擁有 book 和 cd 元素的節點集

+

加法

6 + 4

10

-

減法

6-4

2

*

乘法

6 * 4

24

div

除法

8 div 4

2

=

等於

price=9.80

如果 price 是 9.80,則返回 true

如果 price 是 9.90,則返回 false

!=

不等於

price!=9.80

如果 price 是 9.90,則返回 true

如果 price 是 9.80,則返回 false

<

小於

price<9.80

如果 price 是 9.00,則返回 true

如果 price 是 9.90,則返回 false

<=

小於或等於

price<=9.80

如果 price 是 9.00,則返回 true

如果 price 是 9.90,則返回 false

>

大於

price>9.80

如果 price 是 9.90,則返回 true

如果 price 是 9.80,則返回 false

>=

大於或等於

price>=9.80

如果 price 是 9.90,則返回 true

如果 price 是 9.70,則返回 false

or

price=9.80 or price=9.70

如果 price 是 9.80,則返回 true

如果 price 是 9.50,則返回 false

and

price>9.00 and price<9.90

如果 price 是 9.80,則返回 true

如果 price 是 8.50,則返回 false

mod

計算除法的余數

5 mod 2

1

具體示例如下:

find_element_by_xpath("//input[@id='kw' and @class='su']/span/input")

結合函數與屬性值匹配元素

W3CSchool中關於xpath介紹了很多函數的使用,具體見:http://www.w3school.com.cn/xpath/xpath_functions.asp

下面介紹三個函數的使用,函數具體功能如下:

函數 描述
fn:contains(string1,string2) 如果 string1 包含 string2,則返回 true,否則返回false。
例子:contains('XML','XM')
結果:true。
fn:starts-with(string1,string2) 如果 string1 以 string2 開始,則返回 true,否則返回false
例子:starts-with('XML','X')
結果:true
fn:ends-with(string1,string2) 如果 string1 以 string2 結尾,則返回 true,否則返回 false
例子:ends-with('XML','X')
結果:false

下面通過示例實踐這三個函數在實際項目中的應用,下面定位登錄窗口的用戶名輸入框,使用三個函數分別如下:

val=driver.find_element(By.XPATH, "//div[contains(@id,'loginForm')]//input[1]")
val.send_keys("contains")

val=driver.find_element(By.XPATH, "//div[starts-with(@id,'login')]//input[1]")
val.send_keys("starts-with")

val=driver.find_element(By.XPATH, "//div[ends-with(@id,'Field')]//input[1]")
val.send_keys("ends-with")

使用xpath軸來定位元素

Xpath軸

XPath軸(XPath Axes)可定義某個相對於當前節點的節點集

下表描述了XPath數據模型中的各個節點與上下文節點(context node)的關系(參考:http://www.cnblogs.com/gakusei/articles/1582152.html

軸名稱 結果
ancestor 選取當前節點的所有先輩(父、祖父等)
ancestor-or-self 選取當前節點的所有先輩(父、祖父等)以及當前節點本身
attribute 選取當前節點的所有屬性
child 選取當前節點的所有子元素
descendant 選取當前節點的所有后代元素(子、孫等)
descendant-or-self 選取當前節點的所有后代元素(子、孫等)以及當前節點本身
following 選取文檔中當前節點的結束標簽之后的所有節點
namespace 選取當前節點的所有命名空間節點
parent 選取當前節點的父節點
preceding 選取文檔中當前節點的開始標簽之前的所有節點
preceding-sibling 選取當前節點之前的所有同級節點
self 選取當前節點

Child相當於默認節點,可以不寫,如上面的相對路徑中的xpath表達式:/html/body/div其實完整寫法應該是:/html/child::body/div

Xpath步(step)

上面描述了節點與節點之間的關系,在xpath中節點是通過沿着路徑 (path) 或者步 (steps) 來選取的,下面看看步(step)的內容:

步(step)包括:

  • 軸(axis):定義所選節點與當前節點之間的樹關系
  • 節點測試(node-test):識別某個軸內部的節點
  • 謂語(predicate):更深入地提煉所選的節點集 用[]包括起來(零個或多個)
  • 步的語法:軸名稱::節點測試[謂語]

實例參考下表

例子 結果
child::book 選取所有屬於當前節點的子元素的 book 節點
attribute::lang 選取當前節點的 lang 屬性
child::* 選取當前節點的所有子元素
attribute::* 選取當前節點的所有屬性
child::text() 選取當前節點的所有文本子節點
child::node() 選取當前節點的所有子節點
descendant::book 選取當前節點的所有 book 后代
ancestor::book 選擇當前節點的所有 book 先輩
ancestor-or-self::book 選取當前節點的所有book先輩以及當前節點(假如此節點是book節點的話)
child::*/child::price 選取當前節點的所有 price 孫子--節點。
Xpath定位案例

下面同樣使用cccm的登錄框作為示范,使用前面xpath軸及步來定位輸入框部分內容

案例一:選取用戶名輸入框后的所有input節點,下面代碼返回的結果則是下圖中紅色部分的四個元素

users=driver.find_elements(By.XPATH, //*[@id='inputUserName']/following::input" )
for user in users:
     user.send_keys("xpathz")

案例二:當前節點為一個div,選取其所有子節點,而不關心節點是否是其直接子節點

users1=driver.find_elements(By.XPATH,//div[@id='loginFormField']//child::*")
print len(users1)

該案例其實也可以理解為是當前節點的子孫節點,注意細節點,上述表達式child前使用了//而不是/,上述黃色背景部分的執行結果等同與下列代碼執行結果:

users1=driver.find_elements(By.XPATH,"//div[@id='loginFormField']/descendant::*"

下面通過案例來驗證/與//區別(該部分在前面相對路徑定位的章節中有介紹及案例驗證)此處使用xpath軸部分的案例再驗證下,如下圖:

圖一是//應用,所以有45個子元素,而圖二/,所以有1個子元素,並且值是圖一的直接子元素table。

案例三、屬性字段的應用,如下,通過定位到div后再定位到其子孫節點中的input元素,且name屬性為指定值

namez=driver.find_element(By.XPATH,//div[@id='loginFormField']//child::input[@name='userName']")
namez.send_keys("zhou[x]")

參考資料

XPath Tutorial http://www.w3schools.com/xpath/default.asp

擴展資料

W3C XPath Recommendation: http://www.w3.org/TR/xpath/

XPath Tutorial: http://www.zvon.org/xxl/XPathTutorial/General/examples.html

DOM:http://www.w3school.com.cn/htmldom/dom_nodes.asp

Firefox插件,可以幫助你獲取頁面元素的XPath:XPath Checker、Firebug

Xpath相關文檔:http://www.cnblogs.com/gakusei/articles/1582152.html

文檔中有如下圖可以直觀的確認:


免責聲明!

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



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