声明
本文章中所有内容仅供学习交流使用,不得用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关。
若擅自使用本文讲解的技术而导致的任何意外,作者均不负责。文中若有侵权内容,请立即联系作者删除!
〇:友情提示
- 本文章已在微信公众号【逆向学习日记】发布,若在【优快云】中无法全部查看,可以直接点击【一篇文章彻底搞懂【XPath】的使用方法】查看。
- 在微信公众号【逆向学习日记】也可以阅读更多文章。
- 感谢关注微信公众号和阅读文章的朋友!
Ⅰ:背景
- 作为爬虫工程师或者数据采集工程师,我们在采集数据的时候,难免会碰到数据接口返回的格式并不是JSON格式,而是XML或者是HTML格式的数据,我们要解析其中需要的数据就很困难,当然可以用正则表达式来提取。
- 作为软件测试工程师,我们在进行web自动化的时候需要进行元素的定位,可能存在其他的方式定位不准确的情况,就需要使用XPath方式来进行元素定位。
Ⅱ:数据准备
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>游戏商店</title>
</head>
<body>
<div class="gamestore">
<div class="game" category="ACTION">
<h2>游戏名称:动作冒险</h2>
<p>开发者:游戏开发公司A</p>
<p>发行年份:2020</p>
<p>价格:¥120</p>
</div>
<div class="game" category="RPG">
<h2>游戏名称:角色扮演</h2>
<p>开发者:游戏开发公司B</p>
<p>发行年份:2019</p>
<p>价格:¥80</p>
</div>
<div class="game" category="SIMULATION">
<h2>游戏名称:模拟人生</h2>
<p>开发者:游戏开发公司C</p>
<p>发行年份:2021</p>
<p>价格:¥60</p>
</div>
<div class="game" category="SPORTS">
<h2>游戏名称:运动竞技</h2>
<p>开发者:游戏开发公司D</p>
<p>发行年份:2022</p>
<p>价格:¥50</p>
</div>
</div>
</body>
</html>
Ⅲ:知识探讨
-
XPath 【XML Path Language】是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。它是XML 路径语言。同样的也支持HTML格式查找。
-
XPath 使用的是路径表达式来选取 XML/HTML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。【绝对路径、相对路径】
-
XPath 中含有很多内置函数,可以对字符串、数字、日期等做处理。
-
XPath 相关术语
a. 节点(Node):在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。
b.基本值(或称原子值,Atomic value):基本值是无父或无子的节点。eg:game、商店游戏
c.节点关系
1.父【Parent】:每个元素以及属性都有一个父。
2.子【Children】:元素节点可有零个、一个或多个子。
3.同胞【Sibling】:拥有相同的父的节点。
4.先辈(Ancestor):某节点的父、父的父,等等。
5.后代(Descendant):某个节点的子,子的子,等等。 -
XPath语法:【 测试节点[谓语] 】
a.最常用的路径表达式
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
… | 选取当前节点的父节点。 |
@ | 选取属性。 |
b.示例
路径表达式 | 结果/说明 |
---|---|
/html | 选取根元素html;所有的绝对路径都是以[ / ] 开头 |
/html/body | 选取根元素html下面的子元素的body元素 |
//h2 | 选取所有的h2子元素,不管它们是在文档的位置 |
./html//h2 | 选取根元素html下面的后代元素的所有h2元素,不管它们位于html之后的位置 |
//@class | 选取名为class的所有属性 |
//div[1]/h2 | 选取第一个h2元素 |
//div[last()]/h2 | 选取最后一个h2元素 |
//div[last()-1]/h2 | 选取倒数第二个h2元素 |
//div[position()<4]/h2 | 选取前面3个的h2元素 |
//div[@category] | 选取所有拥有名为category的属性的div元素 |
//div[@category=‘RPG’] | 选取所有的div元素,并且这些元素拥有值为 RPG的category属性 |
//div[@class=‘game’][number(substring-after(last()/p/text(), ‘¥’)) > 60] | 选取所有的div元素,并且其中最后的p元素的值大于60 |
//div[@class=‘game’][number(substring-after(last()/p/text(), ‘¥’)) > 60]/h2 | 选取所有的div元素下的h2元素,并且其中最后的p元素的值大于60 |
注意:可能xpath函数在某些浏览器上不支持,需要酌情使用!!!
c.通配符
通配符 | 描述 |
---|---|
* | 匹配任何元素节点 |
@* | 匹配任何属性节点 |
node() | 匹配任何类型节点 |
d.示例
路径表达式 | 结果/说明 |
---|---|
//div[@class=‘game’][1]/* | 匹配属性名为game的第一个div元素的所有子元素 |
//* | 匹配文档的所有元素 |
//div[@*] | 匹配所有带属性的div |
e.选取多个类型元素【|】
f.示例
路径表达式 | 结果/说明 |
---|---|
//div/h2 |//div/p[1] | 选取div元素的所有h2和第一个p元素 |
//div/h2 | //div/p[last()] | 选取文档的所有h2元素和最后一个p元素 |
- XPath的轴运算【Axes】 语法【 轴名称::节点测试[谓语] 】
a.轴名称与结果描述
轴名称 | 结果 |
---|---|
ancestor | 结果 |
ancestor-or-self | 选取当前节点的所有先辈(父、祖父等)以及当前节点本身 |
attribute | 选取当前节点的所有先辈(父、祖父等)以及当前节点本身 |
child | 选取当前节点的所有子元素 |
descendant | 选取当前节点的所有后代元素(子、孙等) |
descendant-or-self | 选取当前节点的所有后代元素(子、孙等)以及当前节点本身 |
following | 选取文档中当前节点的结束标签之后的所有节点 |
following-sibling | 选取当前节点之后的所有同级节点 |
namespace | 选取当前节点的所有命名空间节点 |
parent | 选取当前节点的父节点 |
preceding | 选取文档中当前节点的开始标签之前的所有节点 |
preceding-sibling | 选取当前节点之前的所有同级节点 |
self | 选取当前节点 |
b.示例
表达式 | 结果/描述 |
---|---|
(//div[@class=‘game’])[1]/h2[1]/ancestor:: * | 获取第一个class='game’下的第一个h2元素的先辈元素 |
(//div[@class=‘game’])[1]/h2[1]/ancestor-or-self:: * | 获取第一个class='game’下的第一个h2元素的先辈以及当前节点 |
//div[@class=‘gamestore’]/div[1]/attribute:: * | 获取class='gamestore’的div下的第一个div节点的所有属性 |
//div[@class=‘gamestore’]/div[1]/child:: * | 获取class='gamestore’的div下的第一个div节点的所有子元素 |
//div[@class=‘gamestore’]/descendant:: * | 获取class='gamestore’的div元素的所有后代节点 |
//div[@class=‘gamestore’]/descendant-or-self:: * | 获取class='gamestore’的div元素的所有后代节点以及当前节点 |
//div[@class=‘game’][1]/p[2]/following:: * | 获取第一个class=‘game’ 的div元素第2个p标签之后的所有节点 |
//div[@class=‘game’][1]/p[2]/following-sibling:: * | 获取第一个class=‘game’ 的div元素第2个p标签之后的所有同级(兄弟)节点 |
//div[@class=‘game’][1]/parent:: * | 获取第一个class=‘game’ 的div元素的父级元素 |
//div[@class=‘game’][1]/p[2]/preceding:: * | 获取第一个class=‘game’ 的div元素第2个p标签之前的所有节点 |
//div[@class=‘game’][1]/p[2]/preceding-sibling:: * | 获取第一个class=‘game’ 的div元素第2个p标签之前的所有同级(兄弟)节点 |
//div[@class=‘game’][1]/p[2]/self:: * | 获取第一个class=‘game’ 的div元素第2个p标签 |
Ⅳ:总结
目前为止,就把XPath的大部分查找方式就分析完毕,当然其中还有很多没有说到位的方式方法,各位大佬也可以在评论区留下其他的方式,供大家一起学习探讨。还有其中的XPath函数就不讲了,在实际过程中暂时还用不到,需要使用的时候可以自行查询使用方法。喜欢的朋友可以关注微信公众号【逆向学习日记】阅读更多的文章。