前言
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元素
 
                        
                        