XPath详解


前言

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)

每个元素以及属性都有一个父

htmlbodyhead节点的父节点;

<html>

    <head>
				...
    </head>

    <body>
        ...
    </body>

</html>

子节点(Children)

元素节点可有零个、一个或多个子

headbodyhtml的子节点;metatitlehead的子节点

<html>

    <head>
        <meta charset="utf-8" />
        <title>关于ShopXO</title>
    </head>

    <body>
     	...
    </body>

</html>

同胞(兄弟)节点(Sibling)

拥有相同的父节点,headbody 就是兄弟节点。

<html>

    <head>
        <meta charset="utf-8" />
        <title>关于ShopXO</title>
    </head>

    <body>
     	...
    </body>

</html>

先辈节点(Ancestor)

某个节点的父、父的父,等等

在下面的例子中,元素type input的先辈是div am-form-groupformbody

<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的后代是formdiv am-form-groupinput 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元素


文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
 上一篇
Python之多进程和多线程 Python之多进程和多线程
进程(Process)是系统进行资源分配和调度的基本单位,线程(Thread)是CPU调度和分派的基本单位。
2022-10-26
下一篇 
Python之反射 Python之反射
在执行对象的某个方法或调用对象中的某个变量,在无法确定这个方法或变量是否存在是,需要用一个特殊的方法或机制来访问和操作这个位置的方法或变量,称之为反射。
2022-10-20
  目录