浏览器控制台的调试方法:
$x('XPath表达式')
demo html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>常见的XPath表达式</title>
</head>
<body>
<h1>常见的XPath表达式</h1>
<div id="myDiv">
<button class="my-unique-button">class是独一无二的button</button>
<button id="myUniqueButton">id是独一无二的button</button>
<button>独一无二</button>
<button>button内容</button>
<p>p内容</p>
</div>
<button>1</button>
<button>2</button>
<button data-type="myType">button</button>
<button>button内容<span>还有其它内容呢</span></button>
<div class="myDiv">
<span>span内容</span>
<p>p内容</p>
</div>
<span>没有任何属性的span</span>
<span class="text">span内容</span>
<ul id="ul1">
<li>li内容1</li>
<li>li内容2</li>
<li>li内容3</li>
<li>li内容4</li>
<li>li内容5</li>
</ul>
<ul id="ul2">
<li>li内容1</li>
<li>li内容2</li>
<li>li内容3</li>
<li>li内容4</li>
<li>li内容5</li>
</ul>
<ul id="ul3">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<ul id="ul4">
<li>1.<span>haha</span></li>
<li>2.<span>haha</span></li>
<li>3.<span>haha</span></li>
<li>4.<span>haha</span></li>
<li>5.<span>haha</span></li>
<li>6.<span>haha</span></li>
<li>7.<span>haha</span></li>
<li>8.<span>haha</span></li>
<li>9.<span>haha</span></li>
<li>10.<span>haha</span></li>
</ul>
<div class="container1">container1</div>
<div id="container1" class="container1">container2</div>
<div class="container2">container2</div>
<div class="product-books">books</div>
<div class="product-movies">movies</div>
<div class="product-music">music</div>
<div id="myDiv2">test for string()<span>span内容</span></div>
</body>
</html>
常见的XPath表达式
找到页面的所有button元素
//button
有data-type这个属性就可以了,不在乎该属性的值是什么
//button[@data-type]
拥有任意属性的span元素
////span[@*]
//button[@class='my-unique-button']
//button[@id='myUniqueButton']
//button[text()='独一无二']
//div[@id='myDiv']/button
//div[@id='myDiv']/following-sibling :: button[1]
根据button里面的文本内容,text()只考虑它自己的文本内容,不考虑它的后代元素的内容
//button[text()="button内容"]
绝对路径与相对路径
绝对路径
html/body/div/p
相对路径
//div/p
*:所有元素
//div[@class="myDiv"]/*
.:当前节点
..:父节点
谓语(Predicates)筛选
位置索引
//ul[@id="ul1"]/li[1]
//ul[@id="ul1"]/li[position() < 4]
属性筛选(带@属性的)
//div[@id='myDiv']
文本筛选(有等号的)
//button[text()='独一无二']
//表达式(带括号的)
//div[not(@class="container1")]
//div[contains(@class, "product")]
//button[contains(text(), "独一无二")]
//button[starts-with(@class, "my")]
//button[starts-with(text(), "独一无二")]
位置索引和集合运算符,位置索引是从1开始计数的而不是0,最后一个的位置索引是last()而不是-1
选择第一个和最后一个
//ul[@id="ul1"]/li[1] | //ul[@id="ul1"]/li[last()]
position的使用
//ul[@id="ul1"]/li[position() < 4]
圆括号的使用:
//ul/li[2]和(//ul/li)[2]
//ul/li[2]会匹配任意的ul下面的第二个li,所以这会显示多个li,假如有多个ul
(//ul/li)[2]会把任意ul下面的li放入一个整体,例如放入一个列表,所以最终这只会显示一个li,因为所有符合条件的li已经放入一个整体了
运算
比较运算符:> = < !> != !<
//ul[@id="ul1"]/li[position() < 4]
算术运算:+ - * div mod
这里.用于表示li本身,实际上表示的的li本身的文本内容,最终选择的是li本身的文本内容%2=1的元素
取余
//ul[@id="ul3"]/li[. mod 2 = 1]
除法
//ul[@id="ul3"]/li[. div 3 = 1]
//ul[@id="ul4"]/li[number(substring-before(normalize-space(node()[1]), '.')) mod 2 = 0]
1. node()[1]:获取 li 下面的第一个子节点(即包含数字和点号的文本节点,如 "1."),避开后面的 <span> 标签。
2. normalize-space(...):去除可能存在的空格或换行符,确保文本干净。
3. substring-before(..., '.'):截取点号 . 字符之前的内容,得到纯数字的字符串(例如将 "2." 变成 "2")。
4. number(...):将字符串转换为数值类型。
5. mod 2 = 0:判断该数值是否为偶数。
逻辑运算符:and or not
//div[@id="container1" and @class="container1"]
not的用法(必须将要取反的内容用圆括号 () 包裹起来)
//ul[@id="ul1"]/li[not(position() < 4)]
//div[not(@class="container1")]
//div[@class="container1" or @class="container2"]
最终返回的是true或者false:
//div[@class="container1"] or //div[@class="container2"]
函数
text() 当前元素本身的文本,包括换行之类的空白符
//div[@id="myDiv2" and text()="test for string()"]
string() 既包含了元素本身的文本,也包含了后代所有元素的文本
//div[@id="myDiv2" and contains(string(), "span内容")]
//div[@id="myDiv2" and contains(string(), "test for string()")]
测试轴的html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>XPath 13 轴演示页面</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; line-height: 1.6; }
div { margin: 8px 0; padding: 10px; border: 1px dashed #aaa; border-radius: 4px; }
.target { background-color: #fff9c4; border: 2px solid #fbc02d; font-weight: bold; }
</style>
</head>
<body>
<!-- 爷爷节点 (ancestor) -->
<div id="grandparent">爷爷节点 (Grandparent)
<div id="uncle1">叔叔1节点 (Uncle1)</div>
<!-- 父亲节点 (ancestor / parent) -->
<div id="parent">父亲节点 (Parent)
<!-- 前面的同级节点 (preceding-sibling) -->
<p id="prev-sib">前面兄弟节点 (Preceding-sibling)</p>
<!-- 【当前节点 / Context Node】 -->
<div id="target" class="box" data-id="999">
当前节点 (Target / Context Node)
<span id="child-1">子节点 1 (Child / Descendant)</span>
<div id="child-div">
<span id="grandchild">孙子节点 (Descendant)</span>
</div>
</div>
<!-- 后面的同级节点 (following-sibling) -->
<p id="next-sib-1">后面兄弟节点 1 (Following-sibling)</p>
<div id="next-sib-2">后面兄弟节点 2 (Following-sibling)
<span id="niece">侄子节点 (Following)</span>
</div>
</div>
<div id="uncle2">叔叔2节点 (Uncle2)</div>
</div>
</body>
</html>
13种轴
1. 向下/子孙方向的轴
所有直接子节点,child::是默认的,所以可以省略不写
//*[@id="target"]/child::*
所有直接子节点和后续的任意后代节点
//*[@id="target"]/descendant::*
自身节点和所有直接子节点和后续的任意后代节点
//*[@id="target"]/descendant-or-self::*
2. 向上/祖先方向的轴
父节点
//*[@id="target"]/parent::*
可以通过给定元素类型进行过滤:
//a/parent::p
父节点和任意祖先节点(一直到html,如果有的话)
//*[@id="target"]/ancestor::*
自身节点和父节点和任意祖先节点(一直到html,如果有的话)
//*[@id="target"]/ancestor-or-self::*
3. 同级方向的轴(兄弟节点)
(后面的兄弟):同一层级中在当前节点之后的节点
//*[@id="target"]/following-sibling::*
(前面的兄弟):同一层级中在当前节点之前的节点
//*[@id="target"]/preceding-sibling::*
4. 文档顺序方向的轴
(文档中后续的所有节点):当前标签结束之后,整个 HTML 文档中所有的节点(不含祖先)
//*[@id="target"]/following::*
(文档中前面的所有节点):当前标签开始之前,整个 HTML 文档中所有的节点(不含祖先)
//*[@id="target"]/preceding::*
5. 其他特殊轴
self(自身节点)
//*[@id="target"]/self::*
attribute(属性节点):获取当前节点的所有属性(attribute::* 等于@)
//*[@id="target"]/attribute::*
//*[@id="target"]/@*
namespace(命名空间轴)
说明:用于 XML 命名空间(如带有 xmlns:prefix 的文档)。在普通的 HTML5 网页中几乎用不到,一般会返回空。
::后面可以加元素类型限制条件
//td[text()="颜色"]/preceding-sibling::*/input
//td[text()="颜色"]/following-sibling::td/input
// not、and
//div[@class="ant-card"]//ul/li[not(contains(@title, "一页")) and not(contains(string(), "跳至"))]
其它参考XPath表达式:
提取有title并且title是数字的li
//ul[@class="ant-pagination ant-table-pagination"]/li[@title and string(number(@title)) = @title]
关闭弹窗
//div[not(.//input[contains(@placeholder, "请输入")])]//*[contains(@class, "close")]
在 XPath 中,.//input 的核心意思是:在当前 div 的内部(所有后代中)查找 <input> 标签。
//*[contains(@class,"close")]/ancestor::div[not(contains(@placeholder,"请输入"))]/*[contains(@class,"close")]
//*[contains(@data-testid,"close")]/ancestor:: div[not(contains (@placeholder, "请输入"))]/*[contains(@data-testid,"close")]
处理SVG
先选择离这些svg比较近的div,然后通过local-name()或name()来选中该svg
//div[@class="gb_D"]//*[local-name()="svg"]
获取那些没有svg作为后代元素的li
//ul[@class="ant-pagination ant-table-pagination"]/li[@title and not(.//*[local-name()="svg"])]
获取属性和文本值
获取属性值
//*[@id="myDiv"]/button[1]/@class
获取button自身的文本,不包含它的后代元素的文本
/html/body/button[4]/text()
获取button自身的文本,包含它的后代元素的文本
string(/html/body/button[4])