影刀RPA进阶教程:XPath父子与兄弟元素定位实战技巧

之前那篇XPath完全手册讲了属性选择器、模糊匹配、参照物定位这些基础,但电商页面远比理想情况复杂。很多时候你要找的元素自己没有任何特征,但它老爹(父元素)有固定ID,或者它大哥(前一个兄弟元素)有固定文本。这时候就得用轴定位,通过父子、兄弟关系来绕道找到目标。

picture.image 我最早接触“轴”这个概念的时候觉得有点绕,什么parent::following-sibling::,听着就头大。后来在抓天猫商品详情页的规格参数时,被逼着用了一次,发现掌握了三四个常用轴,90%的难定位元素都能迎刃而解。这篇文章就专讲XPath轴在影刀RPA电商数据采集里的实战用法,每个轴都配了抓取场景和代码注释,看完你就能写出“铁打的关系定位”,不再怕元素没特征。

为什么需要轴定位:当目标元素一穷二白的时候

picture.image

理想情况下,目标元素有唯一的id或data属性,一个//*[@id='xxx']就搞定了。但很多时候,你要抓的字段是光秃秃的<span>129</span>,没有任何属性,页面上还有几十个同样的span。这时候唯一能区分它的,就是它在DOM树里的位置——它上面是哪个div,旁边是哪个标签。

轴定位就是干这个的:以某个稳定元素为锚点,通过“亲属关系”找到目标元素。 常用的轴有这几种:

picture.image

轴名称关系比喻
parent::*直接父元素老爸

picture.image | child::* | 直接子元素 | 儿子 | | ancestor::* | 所有祖先元素 | 爷爷及以上 | | following-sibling::* | 后面的同级元素 | 弟弟 | | preceding-sibling::* | 前面的同级元素 | 哥哥 |

picture.image | following::* | 文档中后面的所有元素(不限于同级) | 后面所有人 | | preceding::* | 文档中前面的所有元素 | 前面所有人 |

在电商采集里,最实用的是parent、following-sibling、preceding-sibling这三个。我们一个一个看实战案例。

picture.image

场景一:通过父元素定位——当目标藏在某个固定容器里

抓取场景: 天猫商品详情页,每个规格参数的值都在 <dd> 标签里,而前面的参数名在 <dt> 标签里。你想要抓“品牌名称”对应的值,但值本身只有一个光秃秃的 <dd>Apple</dd>,没有任何属性。而参数名 <dt>品牌</dt> 是固定的文本,可以定位。

picture.image

思路: 先定位到包含“品牌”文字的 <dt> 元素,然后找到它所在的父容器 <dl>,再在父容器里找到 <dd>


![picture.image](https://p3-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/0fd351e941a64867b81e9d1bd97c306b~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783656941&x-signature=SKfE9IlIdr0BntkYtkxgkDVvWfI%3D)
# 捕获元素:品牌名称
# 第一步:定位“品牌”的dt,然后回到父级dl,再找dl下的dd
//dt[contains(text(),'品牌')]/../dd
# 或者用parent轴

![picture.image](https://p3-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/fd1e477d24944110a5f8a3b2e7d4e875~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783656941&x-signature=9fFHW7XKT08ZiQkDcwMbTbs1sNQ%3D)
//dt[contains(text(),'品牌')]/parent::*/dd

/..parent::* 的简写,效果一样。这行XPath的意思是:在整个页面找到包含“品牌”文字的 <dt>,然后去它的父元素里面,找 <dd> 标签。因为每个参数是一个 <dl>,里面一个 <dt> 一个 <dd>,所以这样定位是唯一的。

在影刀里使用: 打开元素编辑窗口,把这行XPath粘贴进去,点“验证”,应该能匹配到唯一的品牌值元素。然后就可以用“提取元素文本”拿到“Apple”。

扩展到更多参数: 如果你要取“型号”的值,只需要把 contains(text(),'品牌') 改成 contains(text(),'型号')。其他的都一样。这种模式可以批量提取所有规格参数,用循环遍历参数名列表即可。

场景二:通过前一个兄弟元素定位——电商列表里的价格、销量

抓取场景: 拼多多商品列表里,每个商品的价格数字在一个 <span> 里,紧跟在另一个 <span>¥</span> 的后面。¥符号有固定文本,但价格数字本身没有任何特征。你要定位价格数字。

思路: 先定位到文本为“¥”的 <span>,然后找它后面紧挨着的第一个兄弟元素。

# 捕获元素:拼多多商品价格数字(跟在¥符号后面)
//span[text()='¥']/following-sibling::span[1]

following-sibling::span[1] 表示当前元素后面第一个同级的 <span> 标签。如果¥符号和数字之间还有别的标签(比如一个空格),可以用 following-sibling::*[1] 不限定标签类型,或者 following-sibling::span[1] 就是第一个span。

同样的逻辑,反过来用前面的兄弟元素: 有时候你要定位“销量”标签,但它前面的“已拼”文本是固定的。你想通过“已拼”定位到它后面的数字。

picture.image

# 捕获元素:拼多多销量数字(如“1.2万件”里的“1.2万”)
# 思路:定位“已拼”这个文本,然后找它后面第一个兄弟元素
//*[contains(text(),'已拼')]/following-sibling::*[1]

在影刀里验证: 在拼多多搜索结果页按F12,用Ctrl+F搜索这行XPath,看看是否高亮到唯一的数字元素。如果页面上有多个商品,//*[contains(text(),'已拼')] 会匹配到所有“已拼”,但结合后面的兄弟轴,每个都会对应各自的价格,所以如果是在“获取相似元素列表”后的循环内使用相对定位,需要加上.

相对定位下的写法: 在ForEach循环体内,如果当前循环项是一个商品卡片容器,XPath要以.开头:

.//*[contains(text(),'已拼')]/following-sibling::*[1]

不加点的话,// 会从页面根节点搜索,会找到页面上所有的“已拼”,然后取第一个,导致所有商品的价格都重复。这个点就是相对定位的关键,之前文章强调过多次,这里再提醒一次。

场景三:通过后面的兄弟元素反推前面——通过结果找原因

抓取场景: 小红书笔记列表里,点赞数显示为“128赞”,你想单独提取数字128。数字本身没有固定特征,但后面的“赞”字是固定的。你想通过“赞”字反推到前面的数字。

思路: 定位到包含“赞”字的元素,然后用 preceding-sibling::*[1] 拿到它前面的兄弟元素(数字)。

# 捕获元素:小红书点赞数字
//*[contains(text(),'赞')]/preceding-sibling::*[1]

如果点赞数后面没有直接跟“赞”字(比如点赞数是一个span,赞字是另一个span),而是“赞”字紧跟在数字后面,或者数字和“赞”是同一个span,那就不需要兄弟轴。但如果像某些页面,数字是一个span,“赞”是另一个span,这个写法就好使。

一种更灵活的方式: 如果“赞”字可能是元素文本的一部分(比如<span>128赞</span>),那就不用轴,直接用 //span[contains(text(),'赞')] 提取文本,拿到“128赞”,然后用正则清洗掉“赞”字。但有时数字和赞字在不同标签里,就非得用轴不可。

场景四:多层级父子关系——找祖宗再找孙子

抓取场景: 某个复杂的商品列表,每个商品卡片的最外层有一个唯一的 data-id 属性,你想要卡片内的标题,但标题本身没特征。

思路: 先定位到有 data-id 的那个容器,再在它内部找标题标签。

# 捕获元素:商品标题
//div[@data-id='123456']//h3

注意这里用了 // 而不是 /// 表示在容器内的任意层级查找h3,/ 表示只在直接子元素里找。因为标题可能在多层div嵌套里面,用 //h3 更稳妥。

如果你想限定只找直接子元素里的h3,而不管更深层的:

//div[@data-id='123456']/div/h3

这根据实际DOM结构来。

结合“获取相似元素列表”的注意事项

在ForEach循环内使用轴定位时,如果当前循环项已经是商品卡片,那么相对定位的轴要基于当前卡片。

假设每个卡片的结构是:

<div class="card">
  <span class="label">价格</span>
  <span class="value">29.9</span>
</div>

在ForEach循环内,当前循环项是 div.card。你想通过“价格”标签定位到后面的29.9,XPath应该写:

.//span[contains(text(),'价格')]/following-sibling::span[1]

注意前面的点,表示从当前循环项开始找。如果不加点,// 会从整个文档根找,那就会找到第一个卡片的“价格”,然后所有循环都输出第一个的29.9。这是最容易犯的错误,也是无报错排查法里说的典型问题。

轴定位的性能与可读性

轴定位写起来可能比直接属性定位长一点,但稳定性通常更好,因为页面结构相对文本来说没那么容易变。而且XPath的意图很明确:“找那个后面跟着‘价格’的数字”。这比 //div[3]/span[2] 这种序号定位好懂得多。

如果对性能有极致要求,可以用更短的路径,但对于一般数据采集,这点性能差异可以忽略。优先保证稳定性和可维护性。


父子兄弟轴速查表

需求XPath写法说明
找父元素//目标/..//目标/parent::*回到上一级
找所有子元素中的某个标签//父元素/child::div直接子div
找紧跟在后面的兄弟//锚点/following-sibling::*[1]下一个同级
找紧跟在后面的特定标签兄弟//锚点/following-sibling::span[1]下一个同级span
找紧挨着前面的兄弟//锚点/preceding-sibling::*[1]上一个同级
在祖先容器内找孙子//容器//目标标签不限层级

推荐练习: 打开淘宝或天猫的商品详情页,按F12,尝试用轴定位写出以下字段的XPath,并逐个验证:品牌、型号、价格、销量、商品参数中的任何一个值。只准用轴,不准用属性的精确匹配(除非必要)。做完这个练习,你对页面结构的理解会上一个台阶。


#影刀RPA #RPA自动化 #XPath #元素定位 #电商自动化

作者:林焱

本文为《影刀RPA学习手册》系列文章之一,内容源于实操经验的整理与分享。

0
0
0
0
评论
未登录
暂无评论