抓取商品价格时,class是 price_2f3a 每次刷新都变。
捕获按钮文字“加入购物车”,有的按钮写“立即购买”,有的写“加购”。
解决办法:XPath属性选择器 + 模糊匹配,专治各种“不稳定”。
场景一:动态class的精确捕获——用属性选择器
问题:class里包含固定前缀 price-,后面数字变化。
解法:用 ^=(开头匹配)或 *=(包含匹配)。
# 原始捕获(不稳定):
//span[@class='price-239847']
# 开头匹配:只要class以'price-'开头就行
//span[starts-with(@class, 'price-')]
# 包含匹配:class里有'price'就算
//span[contains(@class, 'price')]

# 影刀中使用:捕获一个相近元素,手动改成上述写法
操作步骤:
- 捕获一个价格元素(任意一个商品)。
- 在左侧元素库右键 → 编辑。
- 把元素路径改成
//span[contains(@class,'price')]。 - 点“验证”,看是否能高亮所有商品价格。
💡 如果
contains(@class,'price')匹配到太多无关元素(比如“price-tag”旁边的说明文字),加第二层筛选:
//span[contains(@class,'price') and not(contains(@class,'label'))]
场景二:按钮文本有多个写法——用or连接
问题:“加入购物车”有的写“加购”,有的写“加入购物车”。
解法:用 or 匹配任意一个。
# 捕获元素:购物车按钮(多种文本)
//button[text()='加入购物车' or text()='加购' or contains(text(),'购')]

# 更宽松:只要文本里有'购'字
//button[contains(text(),'购')]
影刀操作:
在元素路径里直接输入上述XPath,不需要重新捕获。
容易踩的坑:text() 只匹配直接文本,如果按钮里还有 span 标签,用 text() 会失败。
解决:改用 . 匹配所有子节点文本。
//button[contains(., '购')]
场景三:属性值包含空格——用contains拆开匹配
问题:class是 btn btn-primary large(多个类名),用 @class='btn btn-primary large' 要求顺序一致,容易失效。
解法:拆成多个 contains。
# 错误写法(要求完全匹配):
//button[@class='btn btn-primary large']
# 正确写法(只要包含btn和primary就行):
//button[contains(@class,'btn') and contains(@class,'primary')]
# 或者用属性选择器匹配空格分隔的某个类:
//button[@class~='btn'] # 注意:影刀部分版本不支持~=,建议用contains
实测:contains(@class,'btn') 最稳,因为class顺序变了也不影响。
场景四:定位没有唯一属性的元素——用父级+属性组合
问题:目标元素只有 <div>确认</div>,没有id、class。
解法:向上找到有稳定属性的父元素,再往下定位。
# 父级有稳定class='submit-area'
//div[@class='submit-area']/div[text()='确认']
# 父级是form表单
//form[@id='orderForm']//*[contains(text(),'确认')]
# 没有父级?找爷爷级
//div[@class='modal-content']//div[text()='确认']
影刀操作建议:
不要直接捕获那个裸div。先捕获它的父级,然后在元素路径里手动加
/div[text()='确认']。
场景五:表格/列表里按行内容定位(一行两列)
问题:表格第一列是“商品编号”,要点击同一行的“编辑”按钮。
解法:用 following-sibling 或 ../td[2]。
# 通过“商品编号”文本找到所在行,再定位同行第二个单元格的按钮
//td[contains(text(),'SPU-10086')]/following-sibling::td[1]//button
# 或者先找行(tr),再找该行下第2个td里的按钮
//tr[contains(.,'SPU-10086')]/td[2]//button
# 捕获元素:建议先捕获“商品编号”文本,然后手动改XPath
# 把文本值变成变量 {sku_code} 实现动态定位
常用XPath属性选择器速查
| 语法 | 含义 | 示例 |
|---|---|---|
@attr='value' | 完全等于 | @type='submit' |
contains(@attr,'val') | 包含子串 | contains(@class,'btn') |
starts-with(@attr,'val') | 以val开头 | starts-with(@id,'user_') |
@attr | 只要有该属性 | @disabled(无值属性) |
not(条件) | 取反 | not(contains(@class,'hide')) |
or / and | 逻辑组合 | @type='button' and @aria-label='关闭' |
影刀专属操作清单
| 操作 | 精确位置 | 说明 |
|---|---|---|
| 编辑已捕获元素 | 元素库 → 右键 → 编辑 | 手动修改XPath |
| 验证XPath | 编辑窗口 → 验证按钮 | 检查语法和匹配数量 |
| 动态属性 | 元素路径中写 {变量名} | 配合属性选择器用 |
| 魔法指令 | 对话框输入自然语言 | 3.0版本可直接生成模糊匹配XPath |
版本差异:影刀社区版3.0以上支持魔法指令自动生成XPath,但自己写更可控。
魔法指令适合新手,但复杂场景建议手写。
常见问题速查
| 问题 | 原因 | 解决方法 |
|---|---|---|
contains(@class,'price') 匹配到0个 | class里确实没有'price' | 打开开发者工具看真实class值 |
| 匹配到20个元素,我只想要第1个 | 缺索引 | 加[1]:(//span[contains(@class,'price')])[1] |
text() 匹配不到 | 目标元素里有子标签(如<i>) | 改用 contains(.,'文本')(点代表所有子节点文本) |
| 属性选择器里变量不生效 | 变量名写错或未定义 | 用“输出日志”打印变量值,检查拼接后的XPath |
推荐资源
- 影刀官方帮助中心:《XPath高级篇——属性选择器与函数》视频(约15分钟)
- 在线工具:XPath Helper(上篇已讲) → 用
//tag[@attr]格式实时测试 - 影刀模板市场:搜索“商品价格采集”,下载后看里面XPath的写法
最后一句:属性选择器和模糊匹配是XPath的灵魂,学会这5个场景,80%的元素定位问题都能自己搞定。
作者:林焱
本文为《影刀RPA学习手册》系列文章之一,内容源于实操经验的整理与分享。
