XPath 通常用来进行网站、XML (APP )和数据挖掘,通过元素和属性的方式来获取指定的节点,然后抓取需要的信息。

学习 XPath 语法之前,首先了解一下一些概念。

概念介绍

节点之间的关系

以上面的 HTML 节点树为例,节点之间包含了下列的关系:

  1. 父节点 (Parent): HTML 是 DIV 和 P 节点的父节点;
  2. 子节点 (Child):DIV 和 P 是 HTML 的子节点;
  3. 兄弟节点 (Sibling):拥有同样的一个父节点,DIV 和 P 就是兄弟节点。类似的 span、img 和 i 也是兄弟节点。
  4. 祖先节点 (Ancestor):html 是 span 的祖先节点,隔开一级;
  5. 后代节点 (Descendant):span 是 HTML 的后代节点,隔开一级。

除了了解这些概念,parent、sibling 等关键词也非常关键,在匹配复杂的结构时常常用到。

绝对和相对路径

xpath 中绝对路径使用 / 开始,比如:/html/body/div[1]/a/img,绝对路径较长,其中可能包含变化的部分,不建议单独使用绝对路径来选择元素,最好配合其它语法。

比如下面的情况单独使用绝对路径进行定位就会出错:

// 本意是匹配第三个div下的span,但因为第一个div因为是动态显隐的,导致匹配第而个div匹配到之前的第三个了

/html/body/div[2]/span

相对路径以 // 开始,比如 //*[@class],表示只要包含 class 属性的元素均可匹配,无论从哪一个节点开始。

下面是一些常见选择节点示例:

表达式 说明 举例
/ 下一个节点,或者根节点开始 /html/body/div
// 从任意节点开始 //img
. 选取当前节点 //a/.
.. 当前节点的父节点 //a/..
@ 选取包含某属性的元素 //div[@class]或//@class
* 表示任意元素或者任意属性 //*[@class]

除此之外,通过谷歌浏览器-元素上审查元素-复制 xpath,可以直接获取绝对路径和相对路径。

但复制下来的代码,通常还需要进行一些修改,才能具备通用性。

基础语法

定位需要的信息通常通过元素、属性名、属性值以及三者结合等方式进行。

下面来分别看一下,也这段 html 代码为例:

<div id="app">
<p class="title">喜欢的动物</p>
<ul>
<li class="cat">猫</li>
<li class="dog">狗</li>
<li id="panda">熊猫</li>
</ul>
<p class="title">喜欢的电影</p>
<ul>
<li>阿甘正传</li>
<li>霸王别姬</li>
<li>阿凡达</li>
</ul>
<p>其它不需要信息</p>
</div>

1. 通过元素名定位

示例:

1.1 //div/p

定位所有 div 下的 p 子元素,可以是任何 div,只要这个 div 的子节点包含 p 就可以匹配

1.2 //ul

会定位从任何节点开始的 ul 元素

1.3 /html/body/div/p

使用绝对路径定位元素,必须从 /html 开始,否则最好使用 // 相对路径开始

2. 通过属性名定位

通过元素是否包含某个属性来进行定位,属性名需要使用 @ 开始,同时放在 []

2.1 //*[@class]

定位包含 class 属性的元素

2.2 //@class

这种语法定位到的是属性里面的具体值 title,而不是元素,所有没有元素没选中

3. 通过属性值定位

示例:

//li[@class="cat"]

定位包含 class 属性,值为 cat 的 li 属性元素

4. 使用逻辑运算符定位

常用逻辑运算符包括:andornot 三种

示例:

4.1 //li[@class and @class="cat"]

选中包含 class 属性,并且属性值为 cat 的 li 元素对象。

4.2 //li[@class or @id]

选中包含 class 或者 id 属性的 li 元素对象。

4.3 //li[not(@class)]

选中不包含 class 属性的 li 元素对象。

5. 使用谓语定位

5.1 //li[1]

定位任意元素下的第一个 li。

注意

xpath 中索引从 1 开始。

5.2 (//li)[1]

两者区别如下:

//li[1] 任意元素下第一个li,也就是说这个 li 在任意的 ul 下是第一个就会被选中

(//li)[1] 将所有的 li 选出来的结果数组中取第一个,这两者是完全不同的含义

6. 使用文本定位

使用元素中文本的内容进行定位。

示例:

6.1 //li[text()="猫"]

选中文本内容为 的 li 元素对象。

6.2 //*[contains(text(),"喜欢")]

选中任意元素文本中包含 喜欢 两个字的元素,其中 * 表示所有元素是通配符,contains() 表示包含函数。

类似的有:starts-withends-with 函数,表示以什么字符开始和字符结尾的文本。

节点选择器

除了相对和绝对选择之外,下面这些选择器在处理较复杂的匹配场景可以发挥关键作用。

  • parent:::选中父级节点,/.. 也是选中父级,但是通常 parent:: 用于写在 [] 里面作为条件来判断
  • child:::选中子级节点,/ 也是选中子级,通常也是作为条件来使用
  • preceding-sibling:::选中同一层级的前面所有兄弟节点
  • following-sibling:::选中同一层级的后面所有兄弟节点
  • ancestor:::选中祖先节点,包括父级以及更上层的节点
  • descendant:::选中当前节点下面的所有节点,包括子级

举例:

//*[ancestor::div]

选中所有元素中,上级是 div 的元素,其实也就是选中了所有元素,来看看这个

//ancestor::div

只选中了一个元素。

两者的区别如下:

//*[ancestor::div] 选中的 * 表示所有元素,这些元素条件是 [ancestor::div] 父级及以上有 div。

//ancestor::div 选中的是作为别人父级及以上的 div,也就是选中的是 div,这个 div 的是别人的父级或者爷级等

两者是完全不同的概念

美团 APP 匹配示例

看了半天 HTML,我们来了解一下 APP 中的 XML,通常匹配 APP 比网页复杂太多,基本就那几个元素,而且属性名基本都一样,所以常用的手段还是使用各种条件来进行限制匹配,下面来看一个例子。

 <android.view.View index="5" class="android.view.View" text="" checked="false" clickable="true">
<android.widget.TextView index="1" class="android.widget.TextView" text="象山酥院(湛江印象汇店)" checked="false"/>
<android.widget.TextView index="2" class="android.widget.TextView" text="" checked="false" clickable="true"/>
<android.view.View index="3" class="android.view.View" text="" checked="false">
<android.widget.TextView index="0" class="android.widget.TextView" text="5.0" checked="false" />
</android.view.View>
<android.widget.TextView index="4" class="android.widget.TextView" text="周销量 872" checked="false" />
</android.view.View>
<android.view.View index="5" class="android.view.View" text="" checked="false" clickable="true">
<android.widget.TextView index="1" class="android.widget.TextView" text="蜜雪冰城" checked="false"/>
<android.widget.TextView index="2" class="android.widget.TextView" text="" checked="false" clickable="true"/>
<android.view.View index="3" class="android.view.View" text="" checked="false">
<android.widget.TextView index="0" class="android.widget.TextView" text="5.0" checked="false"/>
</android.view.View>
<android.widget.TextView index="4" class="android.widget.TextView" text="周销量 2322" checked="false"/>
</android.view.View>

上面代码为美团的城市列表页面的 UI XML 代码,其中每个元素都包含大量相同的属性和属性值,关键在于整个页面,任何地方基本就是 android.view.Viewandroid.widget.TextView ,像匹配 HTML 那样元素显然行不通。

示例:获取两个商品的评分

//*[@text and ancestor::*/following-sibling::*[contains(@text, '周销量')]]

规则解释:获取任何包含 text 属性的元素,它的父级的的兄弟元素必须是一个 text 值中包含 "周销量"的元素。

我这里没有使用 [1][2][3] 来定位,是因为不同商品的属性很多时候不一样。

通常还是根据想要的元素的位置,以及相邻元素的特征来定位,首先找到独特的文本,比如上面的周销量是固定会出现的,还有 ¥ 符号也可以,这些都是位置和文本值固定的,找到这个的位置,再去定位需要的元素的位置。

工具推荐

  1. 谷歌浏览器-审查元素- ctrl + f,可以直接输入 xpath 语句
  2. 谷歌浏览器-selectorshub 插件,文中使用的是这个插件

XPath从入门到精通:基础和高级用法完整指南,附美团APP匹配示例的更多相关文章

  1. Java入门到精通——基础篇之多线程实现简单的PV操作的进程同步

    Java入门到精通——基础篇之多线程实现简单的PV操作的进程同步 一.概述     PV操作是对信号量进行的操作.     进程同步是指在并发进程之间存在一种制约关系,一个进程的执行依赖另一个进程的消 ...

  2. Python爬虫入门之Urllib库的高级用法

    1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...

  3. shell中echo基础及高级用法详解-渐入佳境

    --作者:飞翔的小胖猪 --创建时间:2021年2月19日 1.1 基础用法 echo命令用来输出文本,在shell脚本中用来输出提示信息用的比较多. 单引号:原样输出所有的内容,不用转义就能输出特殊 ...

  4. Java入门到精通——基础篇之面向对象

    一.概述. Java属于面向对象的一种语言,因为Java是面向对象的语言所以这个语言的诞生需要有五个基本特性: 1)万物皆为对象. 2)程序是对象的集合. 3)每个对象都有自己的由其他对象所构成的存储 ...

  5. Java从入门到精通——基础篇之JSTL标签

    一.语言基础 EL(Expression Language)表达式,目的:为了使JSP写起来更加简单.提供了在 JSP 中简化表达式的方法. 二.分类 核心标签库:提供条件判断.属性访问.URL处理及 ...

  6. Java入门到精通——基础篇之static关键字

    一.概述        static 关键字是声明静态变量,静态方法用的.static的含义是属于类且不属于类对象的变量和函数. 二.static的产生.         在创建对象的时候除非用new ...

  7. Java从入门到精通——基础篇之Servlet与JSP的区别

    一.基本概念 1.1 Servlet Servlet是一种服务器端的Java应用程序,具有独立于平台和协议的特性,可以生成动态的Web页面.它担当客户请求(Web浏览器或其他HTTP客户程序)与服务器 ...

  8. Scala入门到精通——第二十二节 高级类型 (一)

    作者:摇摆少年梦 视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 this.type使用 类型投影 结构类型 复合类型 1. this.type使用 c ...

  9. 从入门到精通,Java学习路线导航(附学习资源)

    原文链接:https://blog.csdn.net/qq_42453117/article/details/100655512 引言 最近也有很多人来向我"请教",他们大都是一些 ...

  10. 【Vue】详解组件的基础与高级用法

    Vue.js 最核心的功能就是组件(Component),从组件的构建.注册到组件间通信,Vue 2.x 提供了更多方式,让我们更灵活地使用组件来实现不同需求. 一.构建组件 1.1 组件基础 一个组 ...

随机推荐

  1. Codeforces Round #617 (Div. 3) A~E

    比赛链接:Here 1296A. Array with Odd Sum 题意:给了 \(n\) 个数,现在就是说可以选择两个数让其中一个的值等于另一个的值. 这种操作无限次 问是不是能让这n个数操作后 ...

  2. AtCoder Educational DP Contest 刷题记录

    写在前面 深感自己 DP 很弱的 村人B 刷了点 DP 题,题集地址戳这里. 后记:刷完后感觉自己又行了 A - Frog 1 题意 给定 \(n\) 个石头,第 i 个石头的高度为 \(h_i\). ...

  3. Java内部类详解--成员内部类,局部内部类,匿名内部类,静态内部类

    一.内部类基础 在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的内部类一般来说包括这四种:成员内部类.局部内部类.匿名内部类和静态内部类.下面就先来了解一 ...

  4. vue 状态管理 四、Action用法

    系列导航 vue 状态管理 一.状态管理概念和基本结构 vue 状态管理 二.状态管理的基本使用 vue 状态管理 三.Mutations和Getters用法 vue 状态管理 四.Action用法 ...

  5. C#对象二进制序列化优化:位域技术实现极限压缩

    目录 1. 引言 2. 优化过程 2.1. 进程对象定义与初步分析 2.2. 排除Json序列化 2.3. 使用BinaryWriter进行二进制序列化 2.4. 数据类型调整 2.5. 再次数据类型 ...

  6. Nginx reload重新加载配置文件的异常情况

    Nginx reload重新加载配置文件的异常情况 背景 临近年底, 很多业务工作量都上来了. 今天同事告知, nginx的log 里面出现了大量的 too many openfiles 的提示信息. ...

  7. [转帖]linux下查看内存频率,内核函数,cpu频率

    https://www.cnblogs.com/lovesKey/p/10900501.html 查看CPU: cat /proc/cpuinfo # 总核数 = 物理CPU个数 X 每颗物理CPU的 ...

  8. 银河麒麟在线升级新版本docker

    银河麒麟在线升级新版本docker 卸载 学习来自: https://cloud.tencent.com/developer/article/1491742 yum remove docker \ d ...

  9. [转帖]Linux_Redhat8——常用命令:ls、ll、vim、ps、top、grep、tail

    Linux_Redhat8-常用命令: ls.ll.vim.ps.top.grep 一.ls(list):查看目录下的文件 ls:仅罗列出当前文件名或目录名. ll:罗列出当前文件或目录的详细信息,包 ...

  10. [转帖]等待事件 enq:TX - row lock contention分析与解决

    6月30日,数据库发生了大量锁表.大概持续1小时,并且越锁越多.后来通过业务人员停掉程序,并kill掉会话后解决. 几天后再EM上查看CPU占用: CPU发生了明显等待. 主要是由于enq:TX - ...