拼多多商品页的价格藏得深——列表页显示“券后价”,点进去又有“拼单价”“单独购买价”,还经常带“起”字。采集回来不处理,表格里全是“¥9.9起”这种文本,排序求和全废。
这篇讲影刀RPA采拼多多商品数据的完整流程:从搜索页翻页、提取字段、到价格清洗入库。都是实际跑过店铺数据采集的写法。
先说结论:拼多多采集的关键不是定位,是价格字段的分层处理和翻页节奏控制。
第一步:搜索页结构与翻页逻辑
拼多多网页版搜索页的商品卡片,class名带动态哈希,但商品链接有固定特征——都带goods.html或goods_id。
定位用链接特征,别用class:
# 捕获元素:所有商品卡片的链接(goods.html是拼多多商品页固定路径)
//a[contains(@href,'goods.html')]
# 捕获元素:商品卡片容器(含价格和销量的区域)

//div[contains(@class,'goods-item')]
翻页方面,拼多多网页版是滚动加载而非点击翻页。操作方式:用“执行JavaScript”指令滚动到底部,等新商品出现,循环3~5次。

// 滚动到页面底部触发加载
window.scrollTo(0, document.body.scrollHeight);
这里有个坑:拼多多滚动后新加载的商品,DOM是追加的,不是替换的。 每滚一次,相似元素列表会变长。所以先滚够次数,再一次性获取相似元素列表,别边滚边取,否则会重复采集。
| 操作顺序 | 正确做法 | 错误做法 |
|---------|---------|---------|
| 滚动 | 循环滚5次 | 滚一次就采 |
| 获取列表 | 滚完后一次性获取 | 每滚一次获取一次 |
第二步:商品字段提取与相对定位
拿到商品卡片列表后,用For Each遍历,每个卡片内提取标题、价格、销量、店铺名。
字段定位用相对XPath,以当前卡片为起点:
# 捕获元素:商品标题(相对当前卡片)
.//*[contains(@class,'goods-name')]

# 捕获元素:价格文本(相对当前卡片)
.//*[contains(@class,'price')]
# 捕获元素:销量文本(相对当前卡片)
.//*[contains(text(),'已拼')]
# 捕获元素:店铺名(相对当前卡片)
.//*[contains(@class,'shop-name')]
注意开头那个.,没有它就会从整个页面找,拿到的是第一个商品的数据,所有卡片都变成一样的内容。 这是新手最常犯的错,建议每加一个字段就单独校验一次。
提取出来的原始数据长这样:
标题:夏季纯棉短袖T恤男宽松百搭
价格:¥29.9起
销量:已拼10万+件
店铺:XX服饰旗舰店
第三步:价格清洗——拼多多最麻烦的一步
拼多多的价格文本有四种常见格式,不统一处理没法用:
| 原始格式 | 含义 | 处理后 |
|---|---|---|
| ¥29.9起 | 最低价 | 29.9 |
| ¥29.9-39.9 | 区间价 | 29.9 / 39.9 |
| 券后¥19.9 | 券后价 | 19.9 |
| ¥29.9 | 单一价 | 29.9 |
用Python模块统一清洗,提取所有数字,第一个当主价格,区间价另存最低价和最高价:
# 影刀RPA Python模块:拼多多价格清洗
# 输入:price_str 原始价格文本
# 输出:dict 含min_price、max_price、is_range
import re
def parse_pdd_price(price_str):
if not price_str:
return {'min_price': None, 'max_price': None, 'is_range': False}
# 提取所有数字(含小数)
nums = re.findall(r'\d+\.?\d*', price_str)
if not nums:
return {'min_price': None, 'max_price': None, 'is_range': False}
prices = [float(n) for n in nums]
return {
'min_price': min(prices),
'max_price': max(prices),
'is_range': len(prices) > 1 # 有多个数字说明是区间价
}
关键点:“券后¥19.9”里“券后”两个字不含数字,re.findall只抓数字,天然过滤掉了。 但“满99减10”这种促销描述也会被误抓,所以优先只取价格区域内的文本,别拿整张卡片去匹配数字。
销量同理,“已拼10万+件”要转成数字100000。转换逻辑和价格类似,遇到“万”乘10000。
第四步:翻页采集与去重
滚动加载的模式下,同一个商品可能被加载两次(滚动回弹时)。采完必须按商品ID去重。
商品ID从链接里提取:
# 影刀RPA Python模块:从链接提取商品ID
# 输入:url 商品链接
# 输出:goods_id 字符串
import re
def extract_goods_id(url):
# 拼多多链接形如 goods.html?goods_id=123456
m = re.search(r'goods_id=(\d+)', url)
return m.group(1) if m else ''
翻页循环的结构:
计次循环(5次滚动)
滚动到底部 → 等待2秒
循环结束
获取相似元素列表 → 卡片列表
For Each 卡片 in 卡片列表
提取标题、价格、销量、链接
提取goods_id → 判断是否已采过
未采过 → 清洗价格 → 追加到结果列表
循环结束
写入行数据到表格
去重用集合比遍历列表快。 开头建一个空集合已采ID,循环里判断goods_id not in 已采ID,采过的跳过。
第五步:店铺数据汇总
采集多个商品后,按店铺名分组,统计每个店铺的商品数和最低价。
# 影刀RPA Python模块:按店铺汇总
# 输入:df 含shop_name、min_price两列的DataFrame
# 输出:汇总后的DataFrame
import pandas as pd
def summarize_by_shop(df):
result = df.groupby('shop_name').agg(
goods_count=('min_price', 'count'), # 商品数
lowest_price=('min_price', 'min'), # 最低价
avg_price=('min_price', 'mean') # 均价
).reset_index()
return result.sort_values('goods_count', ascending=False)
注意:min_price为None的行要先过滤掉,否则mean结果全是NaN。用df.dropna(subset=['min_price'])先清一遍。
汇总结果可以直接写飞书多维表格或本地Excel,看团队用哪个。写飞书的细节在飞书联动那篇讲过,Token缓存和字段映射照着来就行。
第六步:防限流与节奏控制
拼多多对高频访问有检测,滚太快、采太密都可能触发验证码或临时封IP。
三条实战经验:
- 滚动间隔至少2秒,别写500毫秒连续滚
- 每采完一页(5次滚动),随机等待3~8秒再滚下一页
- 单次流程别超过50页,分批次跑
判断是否被限流: 页面出现“验证”字样或商品列表突然为空。Catch块里判断元素是否存在//*[contains(text(),'验证')],存在就暂停流程、记录日志、发通知。
| 控制项 | 建议值 | 说明 |
|---|---|---|
| 滚动间隔 | 2秒 | 别低于1.5秒 |
| 页间等待 | 3~8秒随机 | 别用固定值 |
| 单次页数 | ≤50页 | 分批跑 |
易错速查表
| 现象 | 原因 | 修复 |
|---|---|---|
| 所有商品数据一样 | 相对XPath没加. | 开头加. |
| 采到重复商品 | 滚动回弹重复加载 | 按goods_id去重 |
| 价格为文本 | 带“¥”“起”没清洗 | Python正则提取数字 |
| 采到一半列表空了 | 触发限流 | 加随机等待+分批 |
| 销量“10万+”变文本 | 没做单位转换 | 遇“万”乘10000 |
最容易踩的坑:拼多多页面结构改版频繁,XPath要定期校验。 建议每次跑之前点“校验”看高亮数量,明显变少就说明选择器失效。影刀RPA社区版每天30分钟额度,调采集流程够用;店铺数据每天定时采就上创业版,跑满不中断。手机端拼多多采集要ADB模块单独授权,网页版不涉及。
推荐资源
- 影刀官方学院“电商数据采集”专题,翻页和相似元素有演示
- 拼多多商家后台的“商品管理”页结构比前台稳定,有权限的话优先从后台采
#影刀RPA #RPA自动化 #拼多多 #批量采集 #数据清洗
作者:林焱
本文为《影刀RPA学习手册》系列文章之一,内容源于实操经验的整理与分享。
