元素定位专项之: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