前言
XPath 于 1999 年 11 月 16 日 成为 W3C 标准,它被设计为供 XSLT、XPointer 以及其他 XML 解析软件使用,更多的文档可以访问其官方网站:https://www.w3.org/TR/xpath
XPath在UI自动化测试和爬虫方面都有广泛的应用,本文介绍的内容更偏重于UI自动化方向。
UI自动化测试,在元素定位方面有八种方式:
- id 元素的ID进行定位
- name 通过元素的name属性进行定位
- class_name 通过class属性名称进行定位
- tag_name 通过标签名进行定位
- link_text 通过超链接的a标签进行完全匹配定位
- partial_link_text 通过超链接的a标签进行模糊匹配定位
- xpath 通过元素路径进行定位
- css_selector 通过css选择器进行定位
而在实际工作中,下面四种方式是经常用到的:
- id 会遇到没有id的情况
- name 会遇到没有name或name重复的情况
- xpath
- css_selector
xpath和css_selector是元素定位最常用方式。css_selector是selenium官方更推荐的定位方式,但需要使用者有一定的css的基础。(官方推荐css选择器是因为在IE浏览器下,css选择器定位元素的速度要快于xpath(IE没有自己的xpath解释器(parser)),但IE浏览器已经逐步退出历史舞台)
xpath和css_selector的简单比较:
- xpath可以通过元素文本来定位,css_selector不能
- xpath可以通过子节点来定位父节点,css_selector不能
- xpath 定位,需要扫描页面所有元素来定位,相对比较耗时(IE浏览器比较明显)
示例对象
以搭建在云服务器上的shopxo.net
开源商城( https://shopxo.net/ )为例,进行xpath的定位讲解演示。
访问地址:http://101.43.8.10/ (有效期至2022年12月31日)
XPath表达式
XPath使用表达式来选取HTML/XML文档中的节点或者节点集。XPath规范指定了七种类型的节点,这些节点可以是XPath表达式的执行输出,即要查找匹配的结果。
- 根节点(文档节点)
- 元素
- 文本
- 属性
- 注释
- 处理指令
- 命名空间
HTML/XML文档是被作为节点树来对待的。树的根被称为文档节点或根节点。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>关于ShopXO</title>
</head>
<body>
<div class="header-top">
<div class="am-container header">
<ul class="top-nav-left">
<div class="top-nav-items">
<div class="menu-hd">
<em>您好,欢迎来到</em>
<em>ShopXO</em> [
<a href="http://101.43.8.10/?s=user/logininfo.html">登录</a>] [
<a href="http://101.43.8.10/?s=user/reginfo.html">注册</a>]
</div>
</div>
</ul>
</div>
</div>
</body>
</html>
上面的HTML文档中的节点例子:
<html>
根节点 (文档节点)<title>关于ShopXO</title>
元素节点class="menu-hd"
属性节点menu-hd
以及关于ShopXO
基本值(无父或父子的节点)
节点关系
父节点(Parent)
每个元素以及属性都有一个父
html
是body
和head
节点的父节点;
<html>
<head>
...
</head>
<body>
...
</body>
</html>
子节点(Children)
元素节点可有零个、一个或多个子
head
和body
是html
的子节点;meta
和title
是head
的子节点
<html>
<head>
<meta charset="utf-8" />
<title>关于ShopXO</title>
</head>
<body>
...
</body>
</html>
同胞(兄弟)节点(Sibling)
拥有相同的父节点,head
和body
就是兄弟节点。
<html>
<head>
<meta charset="utf-8" />
<title>关于ShopXO</title>
</head>
<body>
...
</body>
</html>
先辈节点(Ancestor)
某个节点的父、父的父,等等
在下面的例子中,元素type input
的先辈是div am-form-group
、form
、body
<body>
<form class="am-form form-validation-sms" method="post" action="http://101.43.8.10/?s=user/login.html" request-type="ajax-fun" request-value="LoginBackHandle">
<div class="am-form-group am-form-group-refreshing am-margin-top-lg">
<input type="hidden" name="type" value="sms" />
<button type="submit" class="am-btn am-btn-primary am-btn-block am-radius am-btn-sm btn-loading-example" data-am-loading="{loadingText: '登录中...'}">登录</button>
</div>
</form>
</body>
后代节点(Descendant)
某个节点的子,子的子,等等
在下面的例子中,body
的后代是form
、div am-form-group
、input type
、登录button
<body>
<form class="am-form form-validation-sms" method="post" action="http://101.43.8.10/?s=user/login.html" request-type="ajax-fun" request-value="LoginBackHandle">
<div class="am-form-group am-form-group-refreshing am-margin-top-lg">
<input type="hidden" name="type" value="sms" />
<button type="submit" class="am-btn am-btn-primary am-btn-block am-radius am-btn-sm btn-loading-example" data-am-loading="{loadingText: '登录中...'}">登录</button>
</div>
</form>
</body>
路径
绝对路径
绝对路径:以/
单斜杠开始,从根节点开始查找元素

从上图中,搜索输入框,定位这个元素,通过chrome浏览器的“开发者工具”,Copy full XPath
就是绝对路径
/html/body/div[2]/div/div[3]/form/div/input
相对路径
相对路径:以//
双斜杠开始,从HTML/XML
文档的任意位置开始查找元素
从“绝对路径”的所示图中,通过chrome浏览器的“开发者工具”定位到搜索输入框,Copy XPath
是优先复制相对路径,但有可能复制出来还是绝对路径,因为不是所有元素都可以通过相对路径进行定位的。
//*[@id="search-input"]
确认查找元素
无论是用绝对路径,还是用相对路径,当写好了XPath后,可能需要验证一下,是否可以匹配查找到我们需要的元素,可以在chrome浏览器的开发者通过CTRL+F
调出搜索框,将写好的XPath表达式填入进行搜索匹配,匹配到唯一的结果(UI自动化中的定位,多数情况下是需要通过XPath表达式定位到唯一的一个元素进行后续的操作),这个XPath表达式才是符合要求的。


基本语法
XPath使用路径表达式来选取HTML/XML文档中的节点或节点集。节点是通过沿着路径(path)或者步(steps)来选取的。
选取节点
常用的路径表达式:
表达式 | 作用 |
---|---|
nodename |
选取此层级节点下的所有子节点 |
/ |
代表从根节点进行选取 |
// |
在所有节点中选取此节点(不需要一定从根节点开始) |
. |
选取当前节点 |
... |
选取当前节点的父节点 |
@ |
选取属性 |
示例如下:
a. /html/body/div[4]/div/div[2]/div[2]/div/div/div[1]/form/div[1]/input
表示从根节点开始查找,标签与标签之间/
表示一个层级
b. /html/body//legend/a
表示多个层级作用于两个标签之间(可以理解为在body
下匹配查找标签legend
)
c. //legend
从任意节点开始查找所有的的legend
标签
d. //*[@id="search-input"]
查找所有元素,id
的值等于search-input
e. //legend/..
从legend
的上一级查找,即父节点
f. //legend/.
查找legend
所在的当前节点
g. //legend/*
查找legend
元素的所有子元素
h. //*
查找所有元素
i. //legend[@*]
查找所有带有属性的legend
元素 (id="search-input"
中,id
是属性名,search-input
是属性值)
谓语
谓语用来查找某个特定的节点或者包含某个指定值的节点,谓语被嵌在中括号[]
内。
a. //*[@id="user-offcanvas"]
查找所有元素,id
的值等于user-offcanvas
b. /div/ul/li[1]
查找div
标签中ul
标签的第一个li
元素
c. /div/ul/li[last()]
查找div
标签中ul
标签的最后一个li
元素
d. /div/ul/li[last()-1]
查找div
标签中ul
标签的倒数第二个li
元素
e. /div/ul/li[position()-1]
查找div
标签中ul
标签的前三个li
元素(position()
下标索引,从1开始)
XPath函数
常用函数,上面已经用到了last()
和position()
text()
以节点的文本内容进行定位查找
登录页的注册按钮
<div class="login-top">
<span class="">还没有帐号?</span>
<a href="http://101.43.8.10/?s=user/reginfo.html" class="am-btn am-btn-secondary am-btn-xs am-radius">注册</a>
</div>
查找定位的xpath为//div[@class='login-top']/a[text()='注册']
contains(@attr,’value’)
模糊查找,attr属性名的值包含value
修改头像:
<a href="javascript:;" class="am-icon-camera-retro" data-am-modal="{target:'#user-avatar-popup'}"> 修改头像</a>
查找定位的xpath为//a[contains(@class,'camera')]
顶部导航-个人中心:
<a href="http://101.43.8.10/?s=user/index.html" target="_top">
<i class="am-icon-fw am-icon-user"></i>
<span>个人中心</span>
</a>
查找定位的xpath为//span[contains(text(),'个人')]
starts-with(@attr,’value’)
模糊查找,attr属性名的值以value开头
会员修改个人资料:
<a href="http://101.43.8.10/?s=article/index/id/23.html" target="_blank">会员修改个人资料</a>
查找定位的xpath为//a[starts-with(text(),'会员修改')]
搜索框:
<input id="search-input" name="wd" type="text" placeholder="其实搜索很简单^_^ !" value="" autocomplete="off">
查找定位的xpath为//input[starts-with(@type,'text')]
轴
轴可以定位相对于当前节点的节点集。
轴名称 | 结果 |
---|---|
child | 查找当前节点的所有子元素 |
parent | 查找当前节点的父节点 |
descendant | 查找当前节点的所有后代元素(子、孙等) |
ancestor | 查找当前节点的所有先辈(父、祖父等) |
descendant-or-self | 查找当前节点的所有后代元素(子、孙等)以及当前节点本身 |
ancestor-or-self | 查找当前节点的所有先辈(父、祖父等)以及当前节点本身 |
preceding-sibling | 查找当前节点之前的所有同级节点 |
following-sibling | 查找当前节点之后的所有同级节点 |
preceding | 查找当前节点的开始标签之前的所有节点 |
following | 查找当前节点的结束标签之后的所有节点 |
self | 查找当前节点 |
attribute | 查找当前节点的所有属性 |
namespace | 查找当前节点的所有命名空间节点 |
语法格式:
轴名称::节点[谓语]
实例说明:
a. child
child是XPath默认的轴,可以省略不写。
/child::html
通常简写为/html
同样,/child::html/child::body
简写为/html/body
b. parent
parent是查询当前节点的父节点
//body/div/parent::*
查找//body/div
的父节点(可能有多个)
//*[@id="search-input"]/parent::*
查找搜索输入框的父节点
c. descendant
descendant是查询当前节点的所有后代元素(包括子节点、子孙节点等。。。)
//body/div/li/descendant::*
查询//body/div/li
的所有后代,即li
后面的元素全部被选择
//body/div/li/descendant::img
查询//body/div/li
的所有后代中的img
元素
d. ancestor
ancestor是查询当前节点的所有祖先节点,即会上述到根节点
//body/div/li/ancestor::*
查询这个路径中li
的所有祖先节点,包括li
的父div
和/div/li
的父body
//*[@id="search-input"]/ancestor::*
查询//*[@id="search-input"]
搜索输入框的所有祖先节点
e. preceding-sibling
preceding-sibling是查询当前节点之前的所有同级节点
//*[@id="ai-topsearch"]/preceding-sibling::input
查找搜索按钮同级向前的input
元素
f. following-sibling
following-sibling是查询当前节点之后的所有同级节点
//*[@id="search-input"]/following-sibling::button
查找搜索输入框同级向后的button
元素