ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python+Playwright实现闲鱼自动监控与秒拍实战

Python+Playwright实现闲鱼自动监控与秒拍实战 闲鱼上真正想买的东西基本等不到你手动刷新。我为了蹲一块某品牌的限量色键盘连续三天设了闹钟每次点进链接都是“已拍下”。后来我把这套流程直接交给了 Python轮询关键词、解析新上架商品、自动创建订单。这套闲鱼监控自动秒拍的项目前后改了四个版本踩过验证码、吃过风控甚至误拍过不想要的商品。今天把能说的细节都写下来包括技术选型、核心代码、部署方式和避坑经验。这篇文章适合有 Python 基础、想做一个真实可用自动化项目的人。不需要你会爬虫逆向只要懂基本语法、会装第三方库跟着思路走完就能跑起来。1. 闲鱼监控到底是什么需求抢东西这件事拼的不是手速1.1 我为什么要写这个项目我平时喜欢淘二手相机、键盘、游戏机都在闲鱼上买过。时间久了就发现一个规律真正极品的闲置价格低、成色好、描述实在的商品通常发布后几分钟内就被拍走。想靠人工一遍遍刷新页面去蹲基本等于赌博赢面很小。试过几个第三方监控工具要么只能盯某几个固定链接要么收费高得离谱还有一些打着“闲鱼助手”旗号的小工具你把它登录信息交出去了账号出了问题根本没地方说理。既然平时就在写 Python我干脆自己做了一个通过关键词搜索闲鱼上的新商品发现符合条件的就推送通知再进一步实现自动下单。整个过程从零开始写了差不多两周改到第四个版本才稳定。做完回头再看这个项目真正有意思的地方不在于代码量有多大而在于它把“信息获取—条件过滤—自动操作”这条链路走通了。这套思路换个平台、换个业务场景稍微改改就能复用。1.2 监控和秒拍是两件完全不同的事很多人一听“监控秒拍”以为是一个循环里顺便把两件事都干了。实际上监控是监控秒拍是秒拍两者的难度、代码、风险完全不在一个量级。监控的核心是定时获取数据 去重 通知。你要解决的是“我如何第一时间知道某个关键词下出现了新商品”。秒拍的核心是快速走完下单流程你要解决的是“我如何代替人工点击把订单创建出来”。我把这两个功能拆分成了两个模块。监控模块可以独立运行只做提醒秒拍模块只在特定条件下触发。这样做的好处是即使秒拍出了问题监控链路不会跟着崩。你也可以先只跑监控跑一段时间觉得稳定了再开启自动下单。1.3 想复现这篇文章你需要哪些基础先说结论基础门槛不高不需要系统学过爬虫也不需要会逆向分析接口签名。你需要具备以下几点本地能跑 Python 3.10 以上版本会用 pip 安装第三方库至少了解 HTML 选择器的大致概念比如 class、text 这类定位方式愿意自己抓几次页面因为闲鱼的页面结构会变选择器也需要跟着微调如果你之前只写过一些简单的处理脚本没有做过自动化项目这正好是练手的好机会。下面的内容我会把每个模块拆开讲连“为什么这么做”都解释清楚不是只丢一堆代码让你自己看。2. 开工前的边界与选型先别急着写代码2.1 合规底线与账号风险在写第一行代码之前必须把风险和边界说清楚。闲鱼是阿里旗下的交易平台规则明确反对使用机器人、脚本恶意抢购、批量下单等行为。一旦被平台的风控系统判定异常轻则限制搜索和下单重则封禁账号。这个代价是你自己承担的跟脚本没关系。所以我把这个项目定位成“个人学习 减少重复操作”并且有三条红线坚决不碰不用脚本去批量囤货、恶意抢货不对正常交易秩序造成干扰不用脚本骚扰卖家比如自动给同一个卖家发大量消息支付环节不做自动化订单提交后一定由人工确认再付款尤其是最后一条。自动下单到“提交订单”这步本质上是帮你省去了重复点击的操作但钱怎么花、买不买必须留给人来决策。我在项目里特意做了限制自动化流程止步于支付前这也是整篇文章最想强调的底线。2.2 技术路线requests直连还是浏览器自动化项目最开始的版本用的是 requests 直接请求闲鱼的 H5 搜索接口。这种方式速度快一轮请求不到一秒看起来特别适合“秒拍”的场景。但实际跑起来问题很多。闲鱼的接口请求头里带了签名参数比如常见的 x-sign、x-mini-wua 这类字段它们由前端 JS 动态生成且规则不定期更换。我第一版刚写完跑了不到一星期接口返回的数据就开始异常紧接着出现了滑块验证。去逆向签名算法等于陷入一场无边无际的猫鼠游戏而且这种对抗行为本身就不值得推荐。后来换了思路用 Playwright 驱动一个真实的浏览器。好处非常直接对比维度requests 直连Playwright 浏览器自动化请求速度快中等接口签名问题经常遇到需逆向基本绕开登录态维护Cookie 易失效可保存完整登录状态实现难度高低风控触发概率较高相对较低浏览器自动化走的是一条“更接近真人操作”的技术路线虽然单次请求速度比 requests 慢但胜在稳定。对于闲鱼这种强风控平台稳比快重要得多。2.3 环境准备与目录结构操作系统方面Windows、macOS、Linux 都可以我是在 macOS 上开发但代码没有平台绑定。安装依赖的步骤很简单pip install playwright playwright install chromium需要说明的是Playwright 只是封装了浏览器操作它还需要真正下载一个 Chromium 内核所以第二条命令必不可少。网络环境正常的情况下下载大概需要一两分钟。项目目录我设计得比较简洁xianyu-monitor/ ├── config.json # 所有配置集中管理 ├── main.py # 入口文件负责调度 ├── monitor.py # 监控模块搜索、解析、过滤、去重 ├── buyer.py # 秒拍模块自动下单 ├── notify.py # 通知模块推送新商品消息 ├── logger.py # 日志配置 ├── state.json # Playwright 保存的登录态 ├── seen.json # 已经处理过的商品 ID └── logs/这个结构遵循一个原则每个模块只干一件事模块之间通过函数调用串联而不是把所有逻辑塞进一个文件里。后面扩展、排查问题都会轻松很多。3. 监控链路实现搜索、解析、过滤与去重监控是整个项目的核心。秒拍可以不做但监控必须稳定可靠。3.1 登录态处理闲鱼网页版的大部分搜索功能不登录也能看但登录之后搜索结果更完整也能看到更多卖家的真实信息。更重要的一点是如果后续要做自动下单没有登录态完全走不通。用 Playwright 处理登录态非常简单它会帮我们把登录后的 cookie、localStorage 等数据保存到一个文件里下次启动时直接加载。# save_state.py from playwright.sync_api import sync_playwright def save_login_state(): with sync_playwright() as p: browser p.chromium.launch(headlessFalse) context browser.new_context() page context.new_page() page.goto(https://www.goofish.com/) input(请在浏览器中完成登录然后按回车键保存登录态...) context.storage_state(pathstate.json) browser.close() if __name__ __main__: save_login_state()注意我特意把headlessFalse也就是让浏览器窗口弹出来。因为扫码登录这个动作只有真人能看到二维码才能完成。登录之后按回车脚本就会把当前上下文的所有状态数据打包保存到state.json。这个文件相当于你的“数字身份”千万不能泄露给别人。3.2 用 Playwright 打开搜索结果页监控模块的第一步就是打开闲鱼的搜索页面。# monitor.py from playwright.sync_api import sync_playwright def open_search(page, keyword): url fhttps://www.goofish.com/search?q{keyword} page.goto(url, wait_untildomcontentloaded) page.wait_for_timeout(3000)这里有一个容易踩的坑wait_untildomcontentloaded只表示页面 HTML 结构加载完了闲鱼的搜索结果内容是异步请求回来后再渲染的所以必须再加一个固定等待时间让商品卡片生成出来。等多久合适我实际测试下来 3 秒左右比较够用。如果你觉得固定等待不优雅可以改成显式等待某个卡片选择器出现page.wait_for_selector(div[class*card], timeout10000)这样更可靠页面还没加载完就一直等不会白跑。3.3 商品卡片解析搜索结果页里每个商品是一张卡片。Playwright 可以把这些卡片批量取出来遍历。def parse_cards(page): cards page.locator(div[class*card]).all() items [] for card in cards: try: title card.locator(span[class*title]).inner_text() price card.locator(span[class*price]).inner_text() link card.locator(a).first.get_attribute(href) item_id parse_item_id(link) items.append({ id: item_id, title: title, price: price, link: fhttps://www.goofish.com{link} if link.startswith(/) else link }) except Exception: continue return items这里的div[class*card]和span[class*title]是我项目当时的页面匹配规则。闲鱼前端改版比较频繁类名经常会换所以这段代码在你的机器上有很大概率需要调整。调整方法没有捷径先用浏览器打开搜索页按 F12 打开开发者工具用元素选择器点一下商品卡片看清楚当前页面实际的类名再把代码里的选择器改掉。我给parse_cards包了一层异常处理单个卡片解析失败就跳过。因为卡片列表里偶尔会混入推荐位、广告位这些元素结构跟普通商品不一样不处理异常会让整个循环中止。3.4 过滤规则解析出原始商品列表后下一步是过滤器。我配置了这样几个维度标题必须包含监控的关键词。比如你搜的是“RTX 4060”那标题里出现“4060”才进入候选价格不能超过预设上限。这个是硬性条件避免监控到一堆上万的整机地区白名单。如果你的交易范围只在同城可以只保留指定城市的商品卖家信用等级。闲鱼的商品卡片上会显示卖家芝麻信用或是否实名这一项可作为加分项不做硬性判断def filter_items(items, config): result [] for item in items: if config[max_price] and float(item[price]) config[max_price]: continue if config[keywords] and not any(kw in item[title] for kw in config[keywords]): continue if config[cities] and not any(city in item.get(region, ) for city in config[cities]): continue result.append(item) return result这里有个小细节我用的any(kw in item[title] for kw in config[keywords])意思是只要标题命中任意一个关键词就保留。如果你想严格匹配多个词就改成all(...)。到底用any还是all取决于你的实际场景。像显卡这种型号区分很细的商品用all更精确像“键盘”“轴体”这种宽泛词用any更容易捞到东西。3.5 去重与持久化同一件商品每一轮搜索都会出现在结果里。如果不去重脚本跑半小时你的通知通道就会被同一个商品轰炸几十遍。我的方案是维护一个seen.json文件import json import os def load_seen(): if os.path.exists(seen.json): with open(seen.json, r, encodingutf-8) as f: return set(json.load(f)) return set() def save_seen(seen): with open(seen.json, w, encodingutf-8) as f: json.dump(list(seen), f, ensure_asciiFalse)每次搜索完把这一轮拿到的商品 ID 跟seen集合做对比新出现的才进入后续处理流程存入集合并且立刻写回文件。为什么强调立刻写回因为脚本可能随时会崩。如果只在退出时保存崩溃一次就意味着重启后所有商品都会当成新的再通知一遍。数据持久化这件事越快做越好。4. 自动秒拍执行细节从列表页到支付前的最后一公里监控没问题之后就可以加秒拍模块了。4.1 什么叫“秒拍”先明确一点我这里说的“秒拍”指的是浏览器自动化下的快速下单从发现新商品到提交订单大概需要几秒钟。这个速度对于大部分个人闲置类商品足够用了。市面上还有一种“毫秒级抢购”通过逆向 App 接口获取签名、预置参数、提前建立长连接真正意义上的零点几秒下单。那种方案的技术门槛和账号风险都高得多也不符合本文的合规前提我不建议普通用户去碰。4.2 点击“我想要”并提交订单闲鱼商品详情页的核心交互按钮是“我想要”。点击之后如果商品有规格、数量选择会弹出确认面板再点击“提交订单”就能创建订单。# buyer.py from playwright.sync_api import TimeoutError as PlaywrightTimeoutError def auto_buy(page, item_url): page.goto(item_url, wait_untildomcontentloaded) page.wait_for_timeout(1500) try: buy_btn page.locator(text我想要).first buy_btn.click(timeout3000) except PlaywrightTimeoutError: print(f【跳过】商品可能已售出或下架: {item_url}) return False try: page.wait_for_selector(text提交订单, timeout5000) page.click(text提交订单) except PlaywrightTimeoutError: print(f【警告】未找到提交订单按钮: {item_url}) return False page.wait_for_selector(text去支付, timeout5000) print(f【成功】订单创建完成等待人工支付: {item_url}) return True整个流程分三段进页面、点我想要、提交订单。每一步都做了超时保护不会因为页面加载慢就卡死。这里有几个失败场景要注意商品已经被人拍下“我想要”按钮直接就不存在了商品已被卖家删除页面会弹出“宝贝不存在”的提示页面还在加载按钮还没渲染出来这时候点击会定位失败针对这些情况我统一用异常捕获兜底打印出跳过原因后退出当前商品不影响下一轮监控。4.3 为什么只做到支付前很多朋友可能不理解都自动化了为什么不顺便把支付也点了原因有三点。第一支付涉及真实的资金操作一旦自动执行遇到商品与描述不符、卖家临时改价、批量下单多付钱等纠纷你会非常被动。第二闲鱼对支付环节的监控更严格自动跳转支付的风险远高于前几步。第三从技术上说创建订单已经锁定了商品你手动点一下支付最多再花十秒钟根本没有必要在自动化里冒这个险。所以我在设计时把auto_buy的终点严格控制在“去支付”页面。只要你看到订单创建成功的提示这单基本已经稳了剩下的动作交给手指完成。4.4 异常处理清单把这几个最常见的异常场景整理成表方便你排查异常场景表现处理方式商品已售出“我想要”按钮不存在记录 URL 并跳过商品已下架页面提示“宝贝不存在”记录 URL 并跳过滑块验证页面出现滑动拼图停止自动化等待人工介入网络超时页面加载失败重试 3 次仍失败则放弃重复点击同一商品产生两条订单加入已拍集合设置冷却时间最后一种情况我单独说一下。脚本在点击“提交订单”之后页面不会立刻跳转如果这个间隙里你又触发了新一轮操作同一条订单可能被提交两次。我的解决办法是一旦auto_buy返回成功立刻把这个商品 ID 写入一个ordered集合并且在当次运行中做检查遇到重复就直接跳过。5. 从脚本到服务调度、配置、日志与告警写好的模块不能只手动跑一次这个项目能长期用靠的是让它自己按节奏跑下去。5.1 用 schedule 驱动循环我选择了最简单直接的schedule库来做定时任务。# main.py import schedule import time from monitor import XianyuMonitor from logger import get_logger logger get_logger() def run_once(): monitor XianyuMonitor() monitor.execute() schedule.every(30).seconds.do(run_once) while True: schedule.run_pending() time.sleep(1)轮询间隔我强烈建议不要低于 15 秒推荐设置为 30 秒以上。理由后面踩坑章节会细讲这里先记住结论你是在帮自己逛闲鱼不是在帮服务器做压测。5.2 配置管理不同商品、不同时间段监控逻辑经常要改。配置文件我用 JSON 格式{ keywords: [RTX 4060, B760主板], max_price: 2500, cities: [], interval: 30, auto_buy: false, notify: { server_chan_key: , dingtalk_webhook: } }auto_buy这个开关很关键。我默认把它设为false也就是说脚本只监控和通知不下单。想开启秒拍需要手动改成true。这样设计是有意为之。监控和秒拍是两个风险等级完全不同的功能用配置开关隔离能防止你某天手一滑把不该自动下单的商品给拍了。5.3 日志与状态持久化日志我用的是 Python 标准库的logging没有引入额外框架。# logger.py import logging def get_logger(): logger logging.getLogger(xianyu_monitor) logger.setLevel(logging.INFO) fh logging.FileHandler(logs/run.log, encodingutf-8) fh.setFormatter(logging.Formatter(%(asctime)s | %(levelname)s | %(message)s)) logger.addHandler(fh) return logger日志的价值在于事后排查。比如你早上起来发现某件商品没被监控到翻一下日志就能看到那一轮是网络超时、页面没加载出来还是被验证码拦截了。没有日志的项目出了问题基本只能靠猜。5.4 通知渠道监控到新商品后最舒服的接收方式是推到手机上。我用过两种方式第一种是 Server 酱。注册之后拿到一个 key请求它的接口就能推送消息到微信import requests def notify_serverchan(key, title, content): url fhttps://sctapi.ftqq.com/{key}.send requests.post(url, data{title: title, desp: content}, timeout5)第二种是钉钉群机器人。创建一个自定义机器人拿到 webhook 地址用 POST 发一条文本消息即可。实际体感Server 酱推送延迟低个人用方便钉钉机器人适合你不止一个人盯同类型商品的情况拉个小群大家一起看。通知模板我会带上价格、链接、地区点进通知就能直接判断要不要去下单。6. 实测踩坑清单风控、验证码、接口变化这部分是拿真金白银的时间换来的。6.1 高频轮询触发滑块第一个版本我为了追求“快”把轮询间隔调成了 5 秒。跑了大概二十分钟页面出现了滑块验证而且是那种拖拽拼图必须人工手动完成。为什么闲鱼这么敏感因为正常用户不会每 5 秒就刷新一次搜索结果页这个频率已经明显偏离人类行为。后来我把间隔调到了 30 秒并且每次请求之间加了一段随机延时情况才好转。代码里加随机等待可以用一行import random import time time.sleep(random.uniform(2, 5))注意随机等待要加在“每轮搜索完成之后、下一轮搜索开始之前”让整体的节奏看起来更像一个在犹豫、翻看的人而不是一个节拍器。6.2 Cookie 失效与多设备踢下线Playwright 保存的state.json不是永久有效的。我的测试中有的账号几个小时后 token 就失效了有的能撑一整天规律不确定。失效的表现是页面跳转到登录页搜索结果为空或者“我想要”按钮点击后提示未登录。解决思路是每次运行前先检查当前 URL 或页面元素判断是否处于登录状态。如果检测到跳转到了登录页立刻停止轮询并推送一条告警提醒你重新执行登录脚本更新state.json。还有一个小细节闲鱼同一个账号在手机和脚本浏览器上同时登录有时会把脚本端踢下线。所以我让脚本用的浏览器上下文尽量独立不要跟日常使用的账号 session 冲突。6.3 发布与索引的时间差你以为卖家刚发布商品搜索接口立刻就能抓到实测下来闲鱼搜索有索引延迟一件商品从上架到能被关键词搜到短则几十秒长则几分钟。这意味着什么意味着“监控到商品马上去秒拍”的链路其实天然存在一个时间窗口差你看到商品的时候它可能已经发布了有一阵子了。对于真正的超热门商品这个时间差足够别人手速快的用户抢先拍走。所以你别指望脚本能监控到每一件“刚发布”的极品。它能做的是帮你持续盯住某个关键词不错过那些上架后还有余量的商品。这个心态很重要期望值放对了用起来才不会焦虑。6.4 秒拍冲突与重复下单重复下单的问题是某天下午发现的。当时脚本检测到一台价格合适的相机自动走了下单流程但页面网络卡顿提交订单后没有马上跳转。第二轮轮询又扫到了同样的商品又执行了一次下单。结果就是同一个订单产生了两个待付款订单手动取消一个另外一个也因为重复操作被限制。后来我在监控模块里同时维护seen和ordered两个集合只有auto_buy开关开启时成功下单的商品 ID 才会进入ordered并设置为当天不再处理。再补充一个经验自动下单前建议加一个“冷静时间”判断。比如从发现新商品到执行下单之间间隔 3 到 5 秒。这既不会大幅影响成功概率也给你留出一点点反应时间来确认“这东西我到底要不要”。7. 最后想说的话这个项目能走多远这套项目做下来我的体会是闲鱼监控自动秒拍的真正价值不在于帮你抢到某一件商品而在于让你把一套完整的自动化流程跑通。从页面解析到数据持久化从异常处理到定时调度这些能力在很多工作场景里都能复用。但也要给想复刻的同学泼一盆冷水。闲鱼的风控一直在动态变化今天能用的选择器明天可能就失效今天能跑通的流程后天可能就会触发滑块。这个项目本质上需要你持续维护不是写完就能一劳永逸的。我对它的定位是一个“学习型实战项目”而不是一个可以无限压榨的抢货工具。我个人的使用习惯是监控功能常开秒拍功能只在真正想买、且已经提前确认过商品信息时才临时打开用完立刻关掉。同时每天看一眼日志发现异常就及时处理。这样折腾了几个月账号一直安然无恙想要的东西也成功拍到过几次。如果你也想复现建议从最小闭环开始先只做关键词监控和通知跑一个星期确认稳定再考虑加自动下单。步子迈小一点踩坑的概率也小一点。
返回列表