影刀RPA新手教程:拼多多采集全流程实战——商品数据翻页与反爬处理
我第一次做拼多多采集的时候,以为和淘宝一样直接上 XPath 就能搞定,结果流程跑了 3 页就被封了 session。后来才知道,拼多多的反爬逻辑和淘宝完全不是一个量级,class 名会定期混淆,价格字段用了"万+"之类的展示文本,直接存进去根本没法算。
这篇把拼多多采集全流程梳理一遍——从安装影刀、定位元素到翻页判断、价格清洗、写入 Excel,每个环节我都踩过坑,全写进来了。案例主线:搜索"无线耳机",采集前 10 页商品的标题、价格、销量、店铺名,存 Excel。
一、安装影刀,先把浏览器插件装好
影刀官网下载安装包,安装后第一步别急着建流程——先装浏览器插件。很多新手跳过这步,结果"获取已打开的网页对象"一直报错,以为是代码问题,折腾一下午。
插件安装路径:影刀主界面右上角→扩展→安装 Chrome 扩展。装完以后在 Chrome 扩展管理页确认一下插件已启用。如果用的是影刀自带浏览器,不需要手动装,但要注意影刀浏览器和系统 Chrome 不能同时运行,否则会冲突报错。
界面结构记三个区域就够用了:左边是指令面板(拖拽式操作),中间是流程画布,右边是变量和日志面板。编码模式才是长期用的模式,新手先熟悉拖拽,有感觉之后切编码,效率高两倍。
二、元素定位:拼多多 CSS 选择器和 XPath 的真实区别
拼多多搜索结果页,先 F12 开 DevTools,找商品列表容器。
我的实际定位路径:
商品卡片容器:
div[data-area-id="searchResultListContainer"] li.goods-list-container商品标题(每个 li 里面):
.//div[contains(@class,'goods-title-text')]价格(注意拼多多价格分两段:整数部分 + 小数部分):
.//span[contains(@class,'price-int')] .//span[contains(@class,'price-decimal')]销量文本:
span.goods-sales店铺名:
.//span[contains(@class,'store-name')]XPath vs CSS 怎么选?拼多多页面 class 名字有些会混淆(a1b2c3这种随机串),XPath 的contains(@class,'goods-title-text')比 CSS 的精确类名更稳。但是获取相似元素列表这种批量操作,CSS 写法更简洁,用browser.find_all_by_css()传 CSS 字符串,比 XPath 少写不少代码。
正则的使用场景:价格文本采集回来是¥59.90,要提取数字用:
importre price_str="¥59.90"price=re.sub(r'[^\d.]','',price_str)# 结果 '59.90'销量文本是"1.2万+",转成数字:
sales_text="1.2万+"if'万'insales_text:num=float(re.sub(r'[^\d.]','',sales_text.split('万')[0]))*10000else:num=float(re.sub(r'[^\d]','',sales_text))三、变量规划:提前想好存什么
采集类流程,变量设计先于写流程。这次用到:
page_num:整数,当前第几页,初始值 1all_data:列表,存所有商品数据(每条是字典)goods_list:相似元素列表,当前页的商品卡片has_next:布尔,是否有下一页item:字典,单条商品数据,key:title/price/sales/shop
字典取值要注意:采集到的字段如果某个商品没有销量显示,直接item['sales']会报 KeyError。用item.get('sales', '0')更安全,给个默认值兜底。
四、流程控制:翻页判断是关键
翻页判断——不确定总页数怎么处理?
拼多多搜索结果页,总页数显示在页面底部,但更可靠的判断方式是看"下一页"按钮有没有disabled属性。
fromxbotimportweb,sleepimportrandomdefmain(args):browser=web.get_active()page_num=1all_data=[]whileTrue:# 等待商品列表加载完成browser.wait_appear('商品列表容器')# 采集当前页(见下一节)page_data=collect_current_page(browser)all_data.extend(page_data)# 判断是否有下一页next_btn_class=browser.find_by_css('button.page-next, a[aria-label="下一页"]').get_attribute('class')if'disabled'innext_btn_classorpage_num>=10:break# 点击下一页 + 随机延时(反爬核心)browser.find_by_css('button.page-next').click()sleep(random.uniform(2.5,5.0))page_num+=1returnall_datawhile True+ 手动break是处理"不知道总页数"场景的标准写法。比for i in range(10)灵活,因为有时候搜索词结果不足 10 页,提前 break 比循环到找不到元素报错强多了。
五、网页自动化:采集逻辑 + 等待策略
当前页采集函数:
defcollect_current_page(browser):page_result=[]# 获取所有商品卡片goods_elements=browser.find_all_by_css('li.goods-list-container')foritemingoods_elements:try:title=item.find_by_xpath('.//div[contains(@class,"goods-title-text")]').get_text()price_int=item.find_by_xpath('.//span[contains(@class,"price-int")]').get_text()price_dec=item.find_by_xpath('.//span[contains(@class,"price-decimal")]').get_text()price=float(price_int+'.'+price_dec.strip('.'))sales=item.find_by_xpath('.//span[contains(@class,"goods-sales")]').get_text()shop=item.find_by_xpath('.//span[contains(@class,"store-name")]').get_text()page_result.append({'title':title,'price':price,'sales':sales,'shop':shop})exceptExceptionase:# 单条失败不中断整个流程continuereturnpage_result等待策略,三种情况用三种方式:
- 等固定时间:
sleep(3)——最简单,但不精准,浪费时间 - 等元素出现:
browser.wait_appear('商品列表容器', timeout=15)——推荐,元素出现就继续 - 等页面加载完:
browser.wait_load_completed()——适合跳转之后,但有些 SPA 页面这个返回 True 但 DOM 还没渲染,拼多多就是这样,用法 2 更可靠
弹窗处理:拼多多时不时弹出"领券"弹窗,在采集循环开始前加:
ifbrowser.wait_appear('关闭弹窗按钮',timeout=3):browser.find('关闭弹窗按钮').click()timeout=3够了,超时自动返回 False,不影响主流程。
六、反爬策略:真正有效的做法
先说没用的:固定 sleep(1) 这种规律性停顿,爬虫检测系统一眼就识别出来。
真正有效的四个组合:
随机延时:每次操作后
sleep(random.uniform(1.5, 4.0)),范围要够大,别用(0.5, 1.5)这种太规律的开启模拟真人操作:影刀有内置指令"开启模拟真人操作",勾选"仿真移动鼠标"、“仿真操作停顿”,设置最小停顿 1 秒、最大停顿 3 秒。这个指令包裹住采集区间,拼多多检测鼠标轨迹的概率会降低不少
滚动模拟:每翻一页先滚动到底部再滚回来,模拟人看内容的行为
browser.scroll_to(location='bottom',behavior='smooth')sleep(random.uniform(0.8,1.5))browser.scroll_to(location='top',behavior='smooth')- 监听网络请求:拼多多价格有时候走 XHR 接口,不在 DOM 里。用
browser.start_monitor_network()监听,browser.get_responses(url='api/search', resource_type='XHR')拿接口数据,比解析 DOM 更稳
browser.start_monitor_network()browser.reload()responses=browser.get_responses(url='api/search',resource_type='XHR')browser.stop_monitor_network()七、数据处理:价格清洗和销量处理
这是拼多多采集最容易忽视的环节。采回来的数据存 Excel 之前必须清洗。
价格清洗:
¥59.90→ 去掉 ¥ 和非数字字符,转 float- 部分商品价格显示"59" 没有小数,用
price_int + '.00'补全再转 float - 拼多多有些商品是区间价
59~89,按最低价取:price_text.split('~')[0]
销量清洗:
defparse_sales(sales_text):ifnotsales_text:return0sales_text=sales_text.replace('件','').replace('已购','').strip()if'万'insales_text:returnint(float(sales_text.replace('万+','').replace('万',''))*10000)returnint(re.sub(r'\D','',sales_text)or0)写入 Excel 注意的坑:
写入
(123)这种带全角括号的数字,Excel 会自动变成-123。解决:把数字转成字符串类型再写入,或者写入前加一个空格前缀' 123'写入 datetime 类型时间戳,用 office 模式会少 8 小时(UTC 时区问题)。解决:转成字符串
str(datetime_obj)再写入,或者用 openpyxl 模式写入大量数据(5000行+)之前先确认 Excel 进程没被占用。频繁读写 Excel 容易触发"被呼叫方拒绝接收呼叫"报错,原因是 office 进程处于编辑状态。在读写操作前后加
sleep(1)能缓解,但根本方法是确保 Excel 文件是关闭状态
八、鼠标键盘辅助操作
有些拼多多搜索框需要先点击激活再输入,直接用"输入文字"指令有时会失败,因为焦点不在搜索框里。
# 先点击搜索框,再清空再输入search_input=browser.find_by_css('input[data-placeholder="搜索商品/品牌"]')search_input.click()search_input.clear()search_input.input('无线耳机')# 按 Enter 搜索fromxbotimportkeyboard keyboard.press_and_release('enter')滚动加载商品列表(有些分类页不是翻页而是滚动加载更多):
# 每次滚动 600px,等加载for_inrange(5):browser.scroll_to(location='point',behavior='smooth',top=600*_,left=0)sleep(random.uniform(1.2,2.0))九、平台实战:拼多多 vs 淘宝的真实区别
做过淘宝采集再做拼多多,几个地方会踩坑:
价格字段结构不一样:淘宝价格在一个完整的 span 里,class 名
price;拼多多拆成整数和小数两个 span,要拼起来。直接复制淘宝的 XPath 来用,采回来全是空或者乱码登录检测更激进:淘宝未登录也能搜索采集,拼多多搜索一定要登录,否则几页之后跳验证码。每次开流程先检测登录状态:
ifnotbrowser.wait_appear('用户头像',timeout=5):raiseException('未登录,请先手动登录拼多多')详情页结构:拼多多商品详情页用了大量 JS 动态渲染,进入详情页后要等待主图加载完才能采集。等待方式:
browser.wait_appear('商品主图', timeout=20)比wait_load_completed()可靠搜索结果 URL 参数:拼多多搜索 URL 里
search_key=无线耳机&page=2可以直接构造翻页 URL,不用点击"下一页"按钮。减少点击动作也是降低被检测概率的方式:
forpageinrange(1,11):url=f'https://www.pinduoduo.com/search_result/?search_key=无线耳机&page={page}'browser.navigate(url)browser.wait_appear('商品列表容器',timeout=20)sleep(random.uniform(2.0,4.0))十、系统联动:采集结果发飞书通知
采集完成后自动发飞书消息,告诉自己"今天跑完了,共采了 XXX 条"。
importxbotdefnotify_feishu(count,file_path):msg=f'拼多多采集完成,共{count}条数据,文件:{file_path}'xbot.web.open('https://open.feishu.cn/open-apis/bot/v2/hook/你的webhook_token',load_timeout=10)# 或者用 HTTP 请求指令发送用"发送 HTTP 请求"指令更稳定:POST 到飞书机器人 webhook,body 里放 JSON 格式的消息内容。这个是系统联动里最常用的通知方式,配一次以后所有流程都能复用。
十一、工程化:子流程封装和调试
流程超过 30 步就要拆子流程,否则排查问题极其痛苦。
这次拼多多采集的子流程划分:
init_browser:打开浏览器、登录检测、搜索关键词collect_page:采集当前页数据(返回列表)turn_page:翻页 + 随机等待(返回 has_next 布尔值)clean_data:价格清洗 + 销量清洗(输入原始列表,返回清洗后列表)write_excel:写入 Excel(输入数据列表 + 文件路径)
子流程参数传递:影刀子流程支持传入参数和返回值,用列表或字典传多个值。比如collect_page传入browser对象,返回page_data列表。
调试技巧:流程出问题不要靠猜,在疑似出错的地方加"打印日志"指令,打印当前变量值。拼多多采集常见的排查点:打印goods_elements的长度(看是不是 0),打印每条price_str的原始值(看清洗前是什么格式)。
命名规范:变量名用下划线小写goods_list,子流程名用动词开头collect_page、write_excel,别用 “子流程1”、“流程A” 这种,回头看根本不知道是干什么的。
十二、常见报错速查
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
UIAError: 等待元素超时 | 元素没加载出来 / XPath 写错了 | 先手动验证 XPath 在 DevTools 里能选中;增大 timeout 到 30s |
Array to String Conversion | 把列表直接写入 Excel 单元格 | 用str()转字符串或','.join(list)再写入 |
被呼叫方拒绝接收呼叫 | Excel 被占用或处于编辑状态 | 确认 Excel 关闭;写入操作前后加 sleep(1) |
find_all 返回空列表 | class 名被混淆 / 页面未加载完 | 用contains(@class,'...)做模糊匹配;检查是否有 iframe |
price 转 float 报错 | 价格文本含特殊字符或为空 | 用 try-except 捕获,跳过异常条目;检查原始文本格式 |
| 翻页后数据和上一页相同 | 翻页点击没生效,页面没刷新 | 翻页后用wait_disappear等旧内容消失再采集 |
常见踩坑合集再过一遍:
- 拼多多每次搜索完都要滚动几下,否则懒加载的商品不出来,采集到的只有前几条
- 价格区间商品(
59~89)要做 split 处理,否则转 float 直接报错 - 不要用固定 sleep,用
random.uniform(min, max),范围至少 1.5 秒差值 - 详情页和列表页的 XPath 完全不同,要分别捕获,别混用
- 流程跑完之后检查 Excel 行数是否和预期匹配,有时候 try-except 静默跳过了很多条
更多采集技巧可以参考 home.linyan.cloud,里面有完整的拼多多/淘宝/小红书实战案例和代码模板。
#影刀RPA #新手教程 #拼多多采集 #网页自动化 #RPA教程 #数据采集
作者:林焱