文档备案控制台
免费开始使用

影刀RPA进阶教程:XPath定位的7个高级函数——not、normalize-space、tran

写XPath时,contains()text()能解决80%的问题。但剩下的20%,比如要排除某个元素、忽略多余空格、大小写不敏感、截取部分属性,就需要用到高级函数。

这7个函数,每个都能在特定场景下救你一命。

picture.image

我也是遇到“文本里有换行符匹配不上”、“属性值大小写不一致”这种坑之后,才一个一个学过来的。

一、not() —— 排除法定位

picture.image

场景:页面上有多个按钮,你要点“提交”,但有一个禁用的“提交”按钮和一个可用的。或者你想排除某个特定class。

语法not(条件)

picture.image

代码示例

# 点击可用的提交按钮(排除disabled的)

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/c386c82aa34741a59497a980df94175c~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1782610707&x-signature=ShLbayTwlUpGAMvq0R3WXGjYPGo%3D)
//button[contains(text(),'提交') and not(@disabled)]

# 排除包含特定class的元素
//div[contains(@class,'item') and not(contains(@class,'ad'))]


![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/3b540cc7a73d4f4e892bf22c32628a9d~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1782610707&x-signature=NhyrNMY2EAGQ4EGTjm%2B0gdk3%2BpM%3D)
# 排除文本为“查看更多”的链接
//a[not(text()='查看更多')]

# 排除隐藏元素
//span[not(contains(@style,'display:none'))]

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/95575f7a6dc04cffbc240a712b90966f~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1782610707&x-signature=HN00XHHjLblAk%2F5DKg4xkd8DLjs%3D)

实战案例:商品列表里混入了广告卡片,广告卡片的class包含sponsor,你要采集所有非广告商品。

//div[contains(@class,'product-card') and not(contains(@class,'sponsor'))]

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/a5b1e1a4aa544d0386ee71e04ea28a6e~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1782610707&x-signature=gDFNL3Y0Z12TrtjvHDsQfVMfxJo%3D)

二、normalize-space() —— 去掉首尾空格和中间多余空格

场景:元素的文本是“ 连衣裙 ”“连衣裙 女装”(多个空格),用text()='连衣裙'匹配不上。

picture.image

语法normalize-space(字符串) — 去掉首尾空格,并将中间连续空格压缩成一个空格。

代码示例

# 匹配忽略首尾空格
//button[normalize-space(text())='提交']

# 匹配中间有多个空格的文本
//span[normalize-space()='连衣裙 女装']  # 实际文本“连衣裙  女装”也能匹配

# 结合contains使用
//div[contains(normalize-space(), '热门推荐')]

注意normalize-space()不加参数时,默认处理当前节点的文本内容。在影刀XPath引擎中,写成normalize-space(.)更稳妥。

三、translate() —— 大小写不敏感匹配

场景:页面上按钮文本可能是“Login”、“LOGIN”、“login”,你想统一匹配。

语法translate(字符串, '大写字母', '小写字母') — 将大写转成小写(或反过来)。

代码示例

# 将文本转为小写后再比较
//button[translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='login']

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/3e056cffc7ac4278a70a6201a64a9700~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1782610707&x-signature=qiJFA1Gb7wHtzk1xhB4FnqZF54o%3D)

# 更简洁的写法(只转换需要的字母)
//button[translate(text(), 'LOGI', 'logi')='login']

封装成可复用的模式(针对英文):

# 忽略大小写匹配“确定”按钮(英文OK)
//button[translate(text(), 'OK', 'ok')='ok']

# 匹配属性值大小写不敏感
//input[translate(@type, 'TEXT', 'text')='text']

注意:中文没有大小写问题,这个函数主要处理英文和数字。如果你只需要处理少量字符,可以只列出可能出现的字母。

四、substring() —— 截取部分文本或属性

场景:元素的ID是btn_submit_123456,你想匹配btn_submit_开头的,但starts-with某些版本不支持。或者你想取文本的前几个字符。

语法substring(字符串, 起始位置, 长度) — 位置从1开始。

代码示例

# 匹配ID的前10个字符
//div[substring(@id, 1, 10)='btn_submit']

# 取文本的后4位(假设文本是“商品编号1234”)

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/520f0196e9234385b4d342dcb87bc871~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1782610707&x-signature=%2BxcdoHg2%2F6U7OwN%2FpqfwJelT8Y0%3D)
//span[substring(text(), string-length(text())-3)='1234']

# 截取中间部分(从第3位开始取5位)
//span[substring(@data-code, 3, 5)='12345']

实战案例:商品SKU编码格式固定为SKU_ABC_123,你要匹配以SKU_开头且第5-7位是ABC的。

//div[starts-with(@data-sku, 'SKU_') and substring(@data-sku, 5, 3)='ABC']

五、string-length() —— 判断文本长度

场景:你要找评论内容长度大于10个字符的,或者排除空评论。

语法string-length(字符串)

代码示例

# 找长度大于0的评论(排除空评论)
//div[contains(@class,'comment') and string-length(text())>0]

# 找长度刚好为11的手机号
//span[string-length(text())=11 and string(number(text()))=text()]

# 排除短标题
//a[string-length(@title)>5]

六、last() 和 position() —— 动态索引

场景:你要取最后一个商品、倒数第二个、或者前三个中的第二个。

语法last()表示最后一个的位置,position()表示当前节点在列表中的序号。

代码示例

# 取最后一个商品卡片
(//div[@class='product-card'])[last()]

# 取倒数第二个按钮
(//button)[last()-1]

# 取前三项中的第二项
(//li)[position()=2 and position()<=3]

# 每隔一项取一个(奇数位置)
//tr[position() mod 2 = 1]

注意last()position()必须在方括号内使用,且通常配合()包裹整个路径。

七、count() —— 统计子元素数量

场景:你要找那些有至少3个规格选项的商品,或者找图片数量大于5的图集。

语法count(节点集)

代码示例

# 找规格选项数量大于3的商品
//div[contains(@class,'product') and count(.//button[contains(@class,'size')]) > 3]

# 找评论数大于10的帖子
//div[contains(@class,'post') and count(.//div[@class='comment']) > 10]

# 找有图片的商品(至少一张图)
//div[count(.//img) > 0]

八、实战:组合使用高级函数

场景:采集商品列表,要求:

  • 排除广告卡片(class不含ad
  • 标题文本去掉首尾空格后长度大于5
  • 价格标签转为小写后包含usd
  • 取前10个商品中的偶数位置

XPath

(//div[contains(@class,'product-card') and not(contains(@class,'ad')) and string-length(normalize-space(.//h3))>5 and contains(translate(.//span[@class='price'], 'USD', 'usd'), 'usd')])[position() mod 2 = 0 and position() <= 10]

拆解

  1. //div[contains(@class,'product-card') — 基础卡片
  2. and not(contains(@class,'ad')) — 排除广告
  3. and string-length(normalize-space(.//h3))>5 — 标题长度
  4. and contains(translate(.//span[@class='price'], 'USD', 'usd'), 'usd') — 价格含usd(大小写不敏感)
  5. 外层用()包起来,加[position() mod 2 = 0 and position() <= 10] — 取前10个中的偶数位置

九、函数支持度速查(影刀环境)

函数支持度备注
not()✅ 完全支持常用
normalize-space()✅ 支持推荐用normalize-space(.)
translate()⚠️ 部分支持简单转换可用,复杂可能失效
substring()✅ 支持位置从1开始
string-length()✅ 支持-
last()✅ 支持-
position()✅ 支持-
count()✅ 支持-
matches()(正则)❌ 不支持contains代替

常见问题/易错速查

问题原因解决方法
normalize-space()没效果没加参数写成normalize-space(.)
translate()报错转换字符串太长只转换必要的字母,如translate(., 'US', 'us')
substring索引从0开始?记混了XPath索引从1开始,不是0
count()返回数字但没法直接比较数值比较用><直接写count(...) > 3即可
组合函数后XPath验证超时XPath太复杂拆成多步,或用影刀指令代替

推荐资源

XPath函数参考:搜索“XPath 1.0 Functions”,W3Schools有完整列表。

影刀测试:在捕获元素界面,用“验证”功能测试复杂XPath,报错会提示具体位置。


最后说一句
这些高级函数我并不是每次都用,但遇到特殊需求时能救命。建议先把normalize-space()not()记住,这两个最常用。translate()的大小写转换其实可以用影刀指令做——先获取文本,用Python转小写再比较,比写复杂XPath更直观。优先保证可读性,太长的XPath拆成多步指令也不丢人。

作者:林焱

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

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