影刀RPA新手教程:XPath实战手册——六种写法在真实采集项目中的选择与避坑
XPath是RPA元素定位的核心技能。上一篇文章讲了元素定位的基础,这篇专攻XPath,把六种写法讲透。
我第一次写XPath的时候,照着教程抄,结果一个字符都不对。后来踩了几十个坑,才总结出这套实战经验。
这篇不讲理论,直接给用法、给代码、给坑点。
一、XPath六种写法详解
1.1 写法一:属性选择器(适合稳定元素)
语法格式:
//标签[@属性="属性值"]什么时候用:
- 元素的属性值是稳定的(不会随机变化)
- 知道准确的属性名和属性值
真实项目代码(百度搜索框):
//input[@id="kw"]代码解释:
//input:找页面上所有的input标签[@id="kw"]:筛选出id等于"kw"的那个input
另一个真实案例(淘宝商品搜索框):
//input[@id="q"]为什么用这个写法:百度搜索框的id是"kw",淘宝商品搜索框的id是"q",这些都是前端开发固定的,不会变。用属性选择器定位,准确率接近100%。
踩坑点1:属性值写错一个字符就匹配不到。
我第一次做的时候,把[@id="kw"]写成了[@id="kw "](多了个空格),排查了一下午,心态直接崩。
解决方案:F12打开开发者工具,直接复制属性值,不要手敲。
踩坑点2:属性值包含引号。
比如value="他说:"你好"",这种字符串里有引号,XPath会报错。
解决方案:用单引号包裹,比如[@value='他说:"你好"']。
踩坑点3:属性值很长,不想写全。
比如class="submit-btn btn-primary btn-large btn-hover",写全了太麻烦。
解决方案:用contains函数(见写法二)。
1.2 写法二:模糊匹配contains(适合只确定部分元素)
语法格式:
//标签[contains(@属性,"部分值")]什么时候用:
- 只知道属性值的一部分
- 属性值太长,不想写全
- 属性值包含多个值(比如class有多个类名)
真实项目代码(小红书号):
//span[contains(text(),"小红书号:")]代码解释:
//span:找页面上所有的span标签[contains(text(),"小红书号:")]:筛选出文本内容包含"小红书号:"的span
为什么用这个写法:小红书号的格式是"小红书号:xxxx",冒号后面的数字是变化的,但"小红书号:"这五个字是固定的。用contains可以匹配到整个span,然后再用正则表达式提取冒号后面的数字。
具体操作流程(在影刀中实现):
- 用【获取元素对象(web)】指令,定位方式选择"XPath选择器"
- XPath填写
//span[contains(text(),"小红书号:")] - 用【获取元素文本内容(web)】指令获取整个文本(比如"小红书号:abc123")
- 用【从文本中提取内容】指令,正则表达式填写
小红书号:(.*),提取出"abc123"
另一个真实案例(匹配class包含多个值的情况):
//div[contains(@class,"note-title")]这样可以匹配:
class="note-title"class="note-title hot"class="hot note-title"
如果用属性选择器[@class="note-title"],只能匹配第一种情况,后两种都匹配不到。
踩坑点1:contains是模糊匹配,可能匹配到多个元素。
解决方案:加上位置限制。
(//div[contains(@class,"note-title")])[1]意思是取第一个匹配项。
踩坑点2:text()包含空白字符。
比如页面源码是<span>小红书号: abc123 </span>(前后有空格),用contains(text(),"小红书号:")可能匹配不到。
解决方案:用normalize-space函数去掉空白字符。
//span[contains(normalize-space(text()),"小红书号:")]1.3 写法三:参照物定位preceding-sibling/following-sibling(适合通过上下文定位)
语法格式:
//标签[@属性="属性值"]/preceding-sibling::标签 # 找前面的兄弟节点 //标签[@属性="属性值"]/following-sibling::标签 # 找后面的兄弟节点什么时候用:
- 要定位的元素没有明确属性,但它旁边有个有明确属性的元素
- 元素之间的相对位置是固定的
真实项目代码(小红书笔记的点赞数):
//span[text()="点赞"]/following-sibling::span代码解释:
//span[text()="点赞"]:找到文本是"点赞"的span(这就是参照物)/following-sibling::span:找它后面的兄弟span(就是点赞数)
为什么用这个写法:小红书笔记详情页,点赞数、收藏数、评论数这三个数据,结构是一样的:
<span>点赞</span><span>1234</span><span>收藏</span><span>567</span><span>评论</span><span>89</span>点赞数在"点赞"这个span的后面,所以用following-sibling::span就能定位到。
如果要定位收藏数:
//span[text()="收藏"]/following-sibling::span如果要定位评论数:
//span[text()="评论"]/following-sibling::span另一个真实案例(找前面的兄弟节点):
//span[text()="收藏数:100"]/preceding-sibling::span这样可以找到"收藏数:100"前面的span,比如可能是收藏的图标。
踩坑点1:preceding-sibling和following-sibling只能找同级节点。
如果要找父节点,用parent::。
//span[text()="点赞"]/parent::div如果要找子节点,用child::(见写法四)。
踩坑点2:兄弟节点有多个,需要调整下标。
比如"点赞"后面的第一个span是点赞数,第二个span可能是其他内容。
解决方案:用下标精确控制。
//span[text()="点赞"]/following-sibling::span[1] # 第一个兄弟节点 //span[text()="点赞"]/following-sibling::span[2] # 第二个兄弟节点1.4 写法四:层级定位child::(适合逐层精确提取)
语法格式:
//父标签/child::子标签简化写法(child::可以省略):
//父标签/子标签什么时候用:
- 要精确定位某个父节点下的第几个子节点
- 页面结构规律,层级关系明确
真实项目代码(小红书笔记的点赞数、收藏数、评论数):
//div[@class="interact-container"]/div[1] # 点赞数 //div[@class="interact-container"]/div[2] # 收藏数 //div[@class="interact-container"]/div[3] # 评论数代码解释:
//div[@class="interact-container"]:找到class是"interact-container"的div(这是点赞、收藏、评论的容器)/div[1]:找它的第一个子div(点赞数)
为什么用这个写法:小红书笔记的互动数据(点赞、收藏、评论)都放在interact-container这个div里面,而且是按顺序排列的。用child::可以精确控制取第几个。
踩坑点1:如果页面结构变了(比如插入了一个新的div),下标可能错位。
我第一次做的时候,写完流程测试没问题。过了一个星期再用,发现采集到的点赞数和收藏数反了。排查了半天,发现页面改版,插入了一个新的div,导致下标全乱了。
解决方案:
- 优先用contains或preceding-sibling/following-sibling,减少对下标的依赖
- 如果必须用下标,定期维护,页面改版后及时更新XPath
踩坑点2:下标从1开始,不是从0开始。
这是XPath的语法规则,和Python的列表索引不同。
错误写法:
//div[@class="interact-container"]/div[0] # 这样写会报错正确写法:
//div[@class="interact-container"]/div[1] # 第一个子div1.5 写法五:starts-with/ends-with(适合属性值有规律的情况)
语法格式:
//标签[starts-with(@属性,"开头部分")] # 匹配以"开头部分"开始的属性值 //标签[ends-with(@属性,"结尾部分")] # 匹配以"结尾部分"结束的属性值什么时候用:
- 属性值有规律,比如都是以某个前缀开头,或以某个后缀结尾
- 属性值的前缀或后缀是固定的,但中间部分会变化
真实项目代码(电商商品列表的商品ID):
//div[starts-with(@id,"product-")]代码解释:
starts-with(@id,"product-"):匹配id以"product-"开头的div- 比如
id="product-12345"、id="product-67890"都能匹配到
另一个真实案例(匹配以".jpg"结尾的图片):
//img[ends-with(@src,".jpg")]这样可以匹配所有jpg格式的图片。
踩坑点1:ends-with在XPath 1.0中不支持。
Chrome浏览器使用的是XPath 1.0,所以ends-with会报错。
解决方案:用contains代替,或者升级到支持XPath 2.0的浏览器。
用contains代替ends-with:
//img[contains(@src,".jpg")]注意:这样也会匹配到".jpg.png"这种文件名,不够精确。
更精确的解决方案:用正则表达式。
//img[matches(@src,"\.jpg$")]但matches函数也是XPath 2.0才支持的,Chrome不支持。
最终方案:在影刀中用【从文本中提取内容】指令,用正则表达式\.jpg$过滤。
1.6 写法六:组合写法(实战中最灵活)
语法格式:把前面的写法组合起来,实现复杂定位。
真实项目代码(小红书笔记详情页,提取发布时间):
//div[@class="note-meta"]/span[contains(text(),"发布于")]/following-sibling::span[1]代码解释(从右往左读):
following-sibling::span[1]:找前面的span节点的后面第一个兄弟span/span[contains(text(),"发布于")]:这个span的文本内容包含"发布于"//div[@class="note-meta"]:这个span在class是"note-meta"的div里面
为什么用组合写法:真实项目的页面结构通常很复杂,单一写法可能搞不定。组合写法可以应对各种复杂情况。
另一个真实案例(电商商品列表,提取商品名称,但只限前两行):
//div[@class="product-list"]/div[position()<=2]/div[@class="product-name"]代码解释:
position()<=2:限制只取前两个商品- 这样可以实现"只采集前两行数据"的需求
position()函数:返回当前节点的位置。
更多组合写法的例子:
例1:匹配class包含"note-title"且文本包含"教程"的div。
//div[contains(@class,"note-title") and contains(text(),"教程")]例2:匹配class包含"note-title"或class包含"article-title"的div。
//div[contains(@class,"note-title") or contains(@class,"article-title")]例3:匹配class包含"note-title"但文本不包含"广告"的div。
//div[contains(@class,"note-title") and not(contains(text(),"广告"))]二、XPath校验工具和常见语法报错
2.1 XPath校验工具
写好的XPath,怎么验证对不对?下面两个工具可以帮到你。
工具一:Chrome开发者工具(最常用)
操作步骤:
- F12打开开发者工具
- 按Ctrl+F(Mac是Cmd+F),打开搜索框
- 输入XPath,回车
- 如果XPath正确,匹配到的元素会高亮显示,并且显示匹配数量
我第一次做的时候,不知道有这个功能的,写完XPath就直接往影刀里填,结果报错。现在都是先在开发者工具里验证,确认无误再填到影刀里。
工具二:XPath Helper(Chrome插件)
这是一个Chrome插件,可以实时验证XPath。
安装方法:
- 在Chrome应用商店搜索"XPath Helper"
- 点击"添加至Chrome"
使用方法:
- 打开需要采集的网页
- 点击浏览器右上角的XPath Helper图标
- 在XPath输入框中填写XPath
- 实时显示匹配结果
踩坑:XPath Helper在某些网站上无法使用(比如有反爬机制的网站)。
解决方案:用Chrome开发者工具代替。
2.2 常见语法报错及解决方案
报错一:SyntaxError: Invalid XPath expression
原因:XPath语法写错了。
常见错误:
引号不匹配。
- 错误:
//div[@class="note-title] - 正确:
//div[@class="note-title"]
- 错误:
括号不匹配。
- 错误:
//div[contains(@class,"note-title" - 正确:
//div[contains(@class,"note-title")]
- 错误:
使用了不存在的函数。
- 错误:
//div[matches(@class,"note-title")](matches函数XPath 1.0不支持) - 正确:
//div[contains(@class,"note-title")]
- 错误:
解决方案:仔细检查XPath语法,确保引号、括号都匹配。
报错二:Unable to locate element with XPath
原因:XPath没有问题,但页面上没有匹配的元素。
常见原因:
- 页面还没加载完成。
- 元素在iframe里面。
- 元素在动态加载的内容里面。
解决方案:
- 在【获取元素对象(web)】指令中,设置"等待元素存在(s)"为5秒或更长。
- 如果元素在iframe里面,先用【切换到iframe(web)】指令切换到iframe,再定位元素。
- 如果元素是动态加载的,用【循环相似元素(web)】指令等待元素出现。
报错三:XPath returned multiple elements, expected one
原因:XPath匹配到了多个元素,但指令只接受一个元素。
解决方案:
- 用下标限制,只取第一个匹配项。
(//div[@class="note-title"])[1] - 改用【获取相似元素列表(web)】指令,获取所有匹配的元素。
三、案例实战:采集电商商品列表页
下面用一个完整案例,把XPath六种写法串起来。
需求:采集电商商品列表页的以下数据(每个商品):
- 商品标题
- 商品价格
- 商品销量
- 评价数
实现方案:对每个数据用不同的XPath写法(为了演示不同写法在实际项目中的选择)。
3.1 采集商品标题(用属性选择器)
商品标题通常有稳定的class或id,用属性选择器最合适。
XPath写法:
//div[@class="product-title"]指令配置:
- 用【获取相似元素列表(web)】指令,定位方式选择"XPath选择器"
- XPath填写上面的代码
- 元素操作选择"获取元素文本内容"
- 保存到变量
商品标题列表
3.2 采集商品价格(用contains模糊匹配)
商品价格的class可能包含多个值(比如class="price current-price"),用contains更稳妥。
XPath写法:
//span[contains(@class,"price")]指令配置:
- 用【获取相似元素列表(web)】指令,定位方式选择"XPath选择器"
- XPath填写上面的代码
- 元素操作选择"获取元素文本内容"
- 保存到变量
商品价格列表
3.3 采集商品销量(用preceding-sibling/following-sibling参照物定位)
商品销量通常在"销量"这个文本后面,用following-sibling定位。
XPath写法:
//span[text()="销量"]/following-sibling::span指令配置:
- 用【获取相似元素列表(web)】指令,定位方式选择"XPath选择器"
- XPath填写上面的代码
- 元素操作选择"获取元素文本内容"
- 保存到变量
商品销量列表
3.4 采集评价数(用child::层级定位)
评价数在商品信息的某个子div里面,用child::定位。
XPath写法:
//div[@class="product-info"]/div[@class="review"]指令配置:
- 用【获取相似元素列表(web)】指令,定位方式选择"XPath选择器"
- XPath填写上面的代码
- 元素操作选择"获取元素文本内容"
- 保存到变量
评价数列表
3.5 数据整合
用【ForEach列表循环】指令,遍历商品标题列表,同时取出对应位置的价格、销量、评价数,写入Excel。
具体操作流程:
- 用【获取相似元素列表(web)】指令,分别获取商品标题列表、价格列表、销量列表、评价数列表
- 用【ForEach列表循环】指令,循环商品标题列表
- 在循环体内,用【获取列表项】指令,按当前循环的下标取出对应位置的价格、销量、评价数
- 用【写入Excel行】指令,将标题、价格、销量、评价数写入Excel的一行
四、总结
XPath六种写法,每种都有适用场景:
- 属性选择器:适合稳定元素,准确率最高
- contains模糊匹配:适合只确定部分元素,最常用
- preceding-sibling/following-sibling:适合通过上下文定位,灵活性强
- child::层级定位:适合逐层精确提取,下标控制要小心
- starts-with/ends-with:适合属性值有规律的情况,注意浏览器兼容性
- 组合写法:实战中最灵活,可以应对各种复杂情况
新手最重要的是:多练。找个项目实战,把六种写法都试一遍,自然就掌握了。
踩坑经验总结:
- 属性值写错一个字符就匹配不到,用F12直接复制
- contains可能匹配到多个元素,用下标限制
- child::的下标从1开始,不是从0开始
- ends-with在Chrome中不支持,用contains代替
- 写好的XPath先在Chrome开发者工具中验证,再填到影刀里
更多案例在影刀RPA学习主页 home.linyan.cloud
#影刀RPA #XPath #元素定位 #网页自动化 #新手入门
作者:林焱