☰
Python闲鱼监控机器人:微信订阅推送实现与部署指南
2026/10/27 1:02:57 网站建设 项目流程

简介:这是一个基于Python开发的微信订阅机器人项目,面向闲鱼二手商品监控场景,适合希望学习微信接口自动化、爬虫与任务队列整合的Python开发者,对于搭建私域订阅提醒或无人值守监控工具尤为实用。项目以Celery为调度核心,完整搭建了四层功能模块:爬虫端实时抓取闲鱼商品并写入MySQL;交互式微信机器人可自动通过好友申请,并将订阅需求存入MongoDB;微信机器人Web接口为推送提供API;推送端轮询商品数据库,按MongoDB中的订阅分类请求接口,将匹配结果推送至订阅好友。整体架构清晰,从启动Web服务到启动Celery任务,一次性联动爬虫、机器人、数据更新与推送四类任务,目录按爬虫、机器人、接口、任务拆分,Python脚本职责单一,便于阅读与二次开发,具备可运行、易扩展的实战参考价值。资源共78个文件,其中75个为Python脚本,另附README说明、License与gitignore配置,压缩包仅99KB,小巧但模块完整。目前已有504人学习下载,适合用于课设、工具开发或二次改造。

1. 闲鱼监控 + 微信推送:这个微信订阅机器人到底帮你盯什么

先说结论:这不是一个「爬虫全家桶」,而是一个结构很克制的 Python 微信订阅机器人,核心场景就一句话——把闲鱼上的关键词搜索、新商品发布、价格变动,变成推送到你微信里的订阅消息。你可能在蹲一块显卡、一台微单,或者某个绝版配件,手动刷闲鱼一天能刷几十次,而这套机器人替你按固定间隔轮询,命中关键词就把商品标题、价格、链接推过来。

适合谁用?两类人最合适。一类是二手捡漏党,盯着特定型号、特定价格区间,机器比人眼刷得勤;另一类是倒卖或比价用户,需要同时盯多个关键词、多个卖家,手动切账号太累。整个项目用 Python 实现,核心依赖是 requests 和定时调度,不需要装浏览器内核,也不用搞什么无头浏览器,这对 Windows 和 Linux 服务器都友好。下文把资源包结构、监控逻辑、微信推送通道、部署踩坑一次讲透,照着改就能用起来。

2. 先看资源包结构:目录划分与三个核心模块

2.1 zip 里的文件都有什么

拿到 zip 解压后,目录大致分成源码、配置、依赖和说明四块。建议先按文件清单核对一遍,缺了任何一个配置文件,启动时都会翻车。

文件/目录作用关键点
main.py程序入口调度循环和异常兜底都在这
monitor/闲鱼监控模块搜索接口封装、响应解析、关键词过滤
pusher/微信推送模块pushplus / Server酱 通道封装
config.yaml全部可调参数关键词、价格区间、轮询间隔、推送 token
requirements.txtPython 依赖清单requests、pyyaml、apscheduler 等
README.md部署步骤环境要求、启动命令、常见报错

这个拆分思路值得直接沿用:监控、推送、配置三者分离。实际部署时你大概率要改的是config.yaml,最多动monitor/里的匹配规则,pusher/基本不用碰。

2.2 监控模块:搜索、解析、过滤三步拆开

很多初学写法是把「发请求 → 解析 → 筛选 → 推送」全塞到一个函数里,前两次能跑,跑几天就崩——因为一旦某个环节报错,整个循环就死了。这个资源包里把监控拆成三个独立步骤,分别对应monitor/下的search.py、parser.py、filter.py。

search.py只负责按关键词请求闲鱼搜索接口,返回原始 JSON 文本;parser.py从 JSON 里剥离出标题、价格、商品 ID、链接、发布时间、卖家昵称;filter.py做关键词二次过滤和价格区间校验,输出最终命中的商品列表。这样的好处是,接口字段变了你只改parser.py,过滤规则变了只改filter.py,不用牵一发动全身。

2.3 推送通道:为什么默认走 pushplus 而不是微信个人号

微信订阅机器人最容易卡住的环节就是把消息送进微信。直接用微信个人号接口属于灰产操作,账号风险极高,这个资源默认用的是 pushplus 的微信推送 API——扫码关注公众号后生成一个 token,通过 HTTP POST 就能把消息推到你微信上。

POST http://www.pushplus.plus/send

这种方式没有封号风险,token 就是你的身份标识,丢给config.yaml里的push_token字段即可。如果你更习惯 Server酱,把pusher/里的发送函数改一下 URL 和参数结构就能切换,两者都是半小时内能跑通的方案。

3. 把闲鱼关键词监控搭起来:请求、解析与关键词匹配

3.1 闲鱼搜索接口:参数怎么填

闲鱼的搜索接口走的是m.goofish.com的移动端 API,返回 JSON 格式数据,比渲染完再解析 HTML 稳得多。核心请求参数如下:

参数取值说明必填
q搜索关键词,需要 URL 编码是
page页码,从 1 开始是
t时间戳,防缓存建议填
n每页条数,一般 10~20建议填

具体请求代码写法如下:

import requests import time from urllib.parse import quote def search_goofish(keyword: str, page: int = 1) -> str: base_url = "https://m.goofish.com/api/h5/mtop.taobao.idle.search.api/1.0/" params = { "q": quote(keyword), "page": page, "t": int(time.time() * 1000), "n": 20, } headers = { "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)", "Referer": "https://m.goofish.com/", } resp = requests.get(base_url, params=params, headers=headers, timeout=5) resp.raise_for_status() return resp.text

这段代码的关键在于quote(keyword)——关键词里有空格或中文时,不编码会直接 400。t参数用毫秒级时间戳,是为了避免闲鱼缓存旧结果,实测加上之后新商品出现的延迟明显短一些。请求头里的User-Agent必须模拟手机浏览器,闲鱼对 PC 端 UA 的请求限制更严,这一步没做对,后面全是滑块。

3.2 解析响应:从嵌套 JSON 里抠出商品字段

闲鱼搜索接口返回的是一个嵌套很深的 JSON,商品列表藏在data下的items数组里。直接下标索引很容易因为字段缺失报 KeyError,所以解析函数要加.get()兜底。下面是核心解析逻辑:

import json def parse_items(raw_text: str) -> list: data = json.loads(raw_text) items = data.get("data", {}).get("items", []) result = [] for item in items: title = item.get("title", "").strip() price = item.get("price", "0") item_id = item.get("itemId") or item.get("item_id") publish_time = item.get("pubTime") or item.get("publishTime") seller = item.get("seller", {}).get("nick", "未知卖家") link = f"https://m.goofish.com/item?id={item_id}" result.append({ "title": title, "price": float(price) if price else 0.0, "item_id": item_id, "publish_time": publish_time, "seller": seller, "link": link, }) return result

注意price的来源可能是字符串也可能是数字,而且闲鱼有些商品是「面议」价格,接口这时返回的可能是空串,最好统一转成 float 并默认 0,方便后面做价格区间过滤。itemId的字段名在不同端有细微差异,老接口叫item_id,新接口叫itemId,所以这里用or做了双保险。链接拼接时id参数是必需的,有些人只拿到标题没拿到 ID,推送出来一堆无链接消息,问题就出在这。

3.3 关键词二次过滤:全局匹配不如「标题 + 卖家」双维度

很多新手以为搜索接口返回的商品一定都带关键词,实际并非如此。闲鱼的模糊搜索会把「相近品类」也带进来,比如你搜「索尼 A7M3」,出来一堆「A7R3」「A7C」的配件。所以拿到parse_items的结果后,还得再做一层本地过滤:

def filter_items(items: list, include_keywords: list, exclude_keywords: list, max_price: float) -> list: matched = [] for item in items: text = item["title"] + " " + item["seller"] if exclude_keywords and any(k in text for k in exclude_keywords): continue if not all(k in text for k in include_keywords): continue if 0 < max_price < item["price"]: continue matched.append(item) return matched

这里include_keywords与exclude_keywords都是列表,用any做排除、all做包含,语义很直观。把卖家昵称拼进匹配文本是为了支持「指定卖家监控」——在include_keywords里填卖家昵称的一部分,等于只看那个人的上新,这个功能在蹲个人卖家时尤其好用。max_price默认 0 表示不限制,这个判断逻辑是「先转 float 再比较」,如果你直接在配置里写字符串,会导致每次比较都报 TypeError。

4. 对接微信订阅推送:模板消息、轮询与去重

4.1 推送通道配置:pushplus token 和模板消息结构

微信订阅机器人的最后一公里是 pushplus 推送。在config.yaml里要配三个字段:push_token、pub_topic、template。push_token是从 pushplus 公众号绑定后拿到的令牌,pub_topic是群组编号(不填则推给个人),template一般用html,内容支持简单的标题加链接排版。配置示例:

pushplus: token: "你的token" topic: "" template: "html"

推送函数封装在pusher/里,发送逻辑是一个标准的 POST 请求:

import requests def send_wechat(token: str, title: str, content: str, template: str = "html") -> bool: url = "http://www.pushplus.plus/send" payload = { "token": token, "title": title, "content": content, "template": template, } resp = requests.post(url, json=payload, timeout=5) result = resp.json() if result.get("code") != 200: print(f"推送失败: {result.get('msg')}") return False return True

这里判断成功别只看 HTTP 状态码,pushplus 接口响应体里有个code字段,200 才是成功。很多人用resp.status_code判断,返回 200 但实际推送失败,就是因为code不是 200。如果你有多个监控关键词,建议一个消息里打包多条商品,而不是一条商品推一次——否则微信会刷屏,而且 pushplus 免费版每天有推送条数上限,打包发能省额度。

4.2 轮询间隔与调度:太短封号,太长漏商品

轮询间隔是一个典型的玄学参数。太短,闲鱼的反爬策略分分钟给你上滑块验证;太长,热门关键词下的秒拍商品根本来不及推送。这个资源的默认配置是 60 秒一轮,实测在单个关键词下比较安全。调度的核心循环可参考下面这段伪代码框架:

import time from monitor import search_goofish, parse_items, filter_items from pusher import send_wechat def run_once(config): keyword = config["monitor"]["keyword"] raw = search_goofish(keyword) items = parse_items(raw) hits = filter_items(items, config["monitor"]["include"], config["monitor"]["exclude"], config["monitor"]["max_price"]) if hits: send_wechat(config["pushplus"]["token"], f"闲鱼新上 {len(hits)} 件", format_content(hits)) return len(hits) while True: try: run_once(config) except Exception as e: print(f"本轮异常: {e}") time.sleep(config["monitor"]["interval_seconds"])

while True加sleep是最直观的写法,适合小规模监控;如果你要同时盯几十个关键词,建议换成 APScheduler 的BackgroundScheduler,因为单个循环无法并发请求多个关键词,一个关键词请求慢了会拖累后面所有关键词。异常兜底必须放在try-except里,否则某次网络抖动导致请求超时,整个程序就退出——这是部署后跑一段时间挂掉的头号原因。

4.3 去重机制:只推新链接,不重复轰炸

如果不做去重,同一件商品每次轮询命中都会推一次,一小时内你会被同一条链接轰炸几十遍。常见的做法是「已推送商品 ID 集合 + 固定窗口」:

seen_ids = set() WINDOW_SIZE = 200 def is_new(item_id: str) -> bool: if item_id in seen_ids: return False seen_ids.add(item_id) if len(seen_ids) > WINDOW_SIZE: seen_ids.pop() return True

为什么不用数据库存储而是用一个 Python 集合?因为监控商品量级在百级到千级,内存集合足够,程序重启后会重新学习和推送一次,对你「看新商品」的目标影响不大。WINDOW_SIZE设为 200 的意思是保留最近 200 个商品 ID,超过之后弹出最早那个——这保证热门商品下架重挂(ID 不变)时能被重新识别,又不会无限膨胀内存。注意,闲鱼商品重挂后item_id不变,所以这个去重也能过滤掉「同链接改价再上架」的打扰。

5. 部署避坑:闲鱼反爬、推送丢失与 cookie 失效的常见排查

5.1 频繁请求触发滑块验证码

现象:跑了一段时间后,search_goofish返回值不再是正常 JSON,而是<html>...验证码...</html>或者直接抛 415 错误。

原因:请求频率超过闲鱼的阈值。闲鱼对同 IP 短时间高频请求非常敏感,尤其是在多个关键词并发轮询时,每个关键词都请求一遍,单位时间请求数可能翻了几倍。

解决:把轮询间隔从 60 秒调大到 120~180 秒,并给每个关键词的请求加随机抖动,避免整点齐射。比如:

time.sleep(interval + random.uniform(0, 5))

5.2 微信推送偶尔收不到,返回 code 200 但无消息

现象:pushplus 接口返回{"code":200},但微信里没收到推送,或者延迟十几分钟。

原因:免费版 pushplus 对单日推送条数有上限,超出后接口仍然返回 200,实际会被静默丢弃。另外,一条消息里 content 字段太长也会被截断。

解决:在推送函数里每次发送后记录当天累计条数,超过 190 条就不再发送并输出日志。同时限制format_content里只保留前 5 条商品信息和链接,超出的部分用「等 N 件更多商品」代替。

5.3 闲鱼接口提示用户未登录或返回 4200 错误

现象:部署几天后,parse_items解析出的items列表变成空数组,抓包发现接口响应体里有subCode: 4200。

原因:闲鱼移动端接口要求登录态,首次可以匿名访问,但高频请求后服务端会要求补充 Cookie。没有 Cookie 的程序此时就失效了。

解决:从手机浏览器登录闲鱼网页版,抓包获取Cookie字段,填到config.yaml的request_headers中。注意 Cookie 有效期一般 3~7 天,这属于正常运行周期,不是 bug。

request_headers: Cookie: "你的cookie" User-Agent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)"

5.4 关键词误报:搜「A7M3」却推送「A7R3」的配件

现象:推送列表里出现大量不相关的商品,标题里明明没有核心关键词,但搜索结果把它带出来了。

原因:闲鱼搜索接口的推荐逻辑不是严格包含匹配,它会把相关品类、同品牌商品混进来。本地filter_items里的include_keywords用all判断时,如果只填一个词「A7M3」,这个判断实际上拦不住不相关内容(因为标题可能写「索尼A73」或包含其他子型号)。

解决:用「双词约束」兜底:把机型和必须出现的价格词分开填,例如include_keywords: ["A7M3", "索尼"]。实测多加一个品牌词,误报率能降一半以上,同时把「A7R3」「A7C」放进exclude_keywords做排除。

5.5 程序跑一段时间内存暴涨

现象:seen_ids集合无限增长,运行一周后进程占用内存超过 1GB。

原因:我以为WINDOW_SIZE控制住了去重集合大小,但如果filter_items返回的商品 ID 全部不重复,seen_ids.pop()弹出的是任意元素,不是最早插入的——Python 的 set 是无序的,pop 弹出哪个元素是不可预测的。

解决:改用collections.deque辅助记录顺序,同时保留 set 做查重:

from collections import deque seen_ids = set() seen_order = deque() def is_new(item_id: str) -> bool: if item_id in seen_ids: return False seen_ids.add(item_id) seen_order.append(item_id) if len(seen_order) > WINDOW_SIZE: old = seen_order.popleft() seen_ids.discard(old) return True

这样弹出的就是最早加入的那个 ID,内存占用能稳定在几十 MB 以内。如果你已经用 set 裸跑了好几天,修复后重启一次,内存会立刻回落。

6. 把监控调得更聪明:发布时间过滤、白名单与改价收敛

6.1 用发布时间戳过滤老链接

闲鱼搜索默认按综合排序时,稳定货源会一直排在前面,每次轮询都命中同一个商品。如果想只看「新发布的」,可以在filter_items里加一个min_publish_time参数——把轮询当天的零点时间戳传进去,只保留publish_time在当天的商品。蹲秒拍商品时,这个过滤能有效减少无意义推送。

import datetime def is_today(publish_time_str: str) -> bool: if not publish_time_str: return False dt = datetime.datetime.strptime(publish_time_str, "%Y-%m-%d %H:%M:%S") return dt.date() == datetime.date.today()

注意,闲鱼接口的pubTime有两种格式:老版本是"2025-06-12 10:30:00",新版本可能出现 ISO 格式带T和时区,解析前做一次字符串归一化,否则strptime会抛 ValueError。

6.2 卖家白名单与黑名单

如果你要盯的不是某个关键词下所有商品,而是特定几个卖家的上新,把卖家昵称拆出来单放一组配置。还有一种是黑名单,专门屏蔽倒卖商——闲鱼上有些卖家是「上架瞬间改价」的类型,你在exclude_keywords里填他们的昵称特征即可。

whitelist_sellers: ["数码老张", "二手小王子"] blacklist_sellers: ["批量回收商"]

过滤逻辑上,白名单优先级高于关键词匹配:只要命中了白名单卖家,就算标题没完全匹配也推送;命中黑名单则无条件丢弃。这个配置在config.yaml单独成段,改起来不用动代码。

6.3 高频改价场景下的重复提醒收敛

闲鱼卖家经常会「微降价」刺激下单,比如从 3500 改到 3450。如果你只按商品 ID 去重,改价后的同 ID 商品被判定为重复不推送,这其实错过了低价机会。但又不能每次都推,否则就是骚扰。折中方案是「价格变化超过阈值才提醒」:

price_cache = {} def should_notify_price(item_id: str, new_price: float, threshold: float = 50.0) -> bool: old_price = price_cache.get(item_id) if old_price is None: price_cache[item_id] = new_price return False if abs(new_price - old_price) >= threshold: price_cache[item_id] = new_price return True return False

首次发现不推送,价格变化超过 50 元才提醒,低于阈值的变化静默更新缓存。这比「每次都推」少很多噪音,比「完全忽略」更能抓到真正的大幅降价。我会把threshold设为关键词价格的 2% 左右,单价高的商品,降价 50 元可能不算事,但在单价 400 元的镜头盖场景,50 元就是大事了。

回头说说我这套习惯的来历:最早跑这个项目时,我只设了 30 秒的轮询间隔,跑了不到一个小时就触发了滑块,整个程序直接停摆。从那以后我每次部署微信订阅机器人,都强制走一遍「请求频率 → 异常兜底 → 去重机制 → 推送额度」四步检查,宁可慢一点,也不要让监控程序在半夜悄悄死掉。这套闲鱼监控的资源包整体设计偏务实,没有花哨的架构,核心就是「轮询 + 解析 + 过滤 + 推送」四个环节,你把上面的踩坑点对着检查一遍,大概率能稳定跑几周不翻车。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询