☰
从零构建网页库存监控:用Python轮询、解析与推送提醒
2026/10/14 7:40:59 网站建设 项目流程

买一台PS5有多难,经历过的人都懂。各大平台页面上那个“有货”按钮,永远像在跟你的网速和手速捉迷藏。定时开售好几次,我都是人到屏幕前了,眼睁睁看着购物车从“可以结算”变成“暂时缺货”。反复被折腾之后,我决定换一种思路:不跟真人比手速,让程序替我盯着所有渠道。于是就有了 AnyPS5,一个帮我轮询商品页面、解析库存状态、第一时间推送提醒的小工具。

这篇东西不是广告,也不是什么“秒杀神器”教程。它是我从零搭建 AnyPS5 的真实过程记录,包括核心逻辑拆解、完整的配置和运行步骤,还有几段特别想让你避开的踩坑经历。如果你也想给自己写一套网页库存监控,或者只是对“程序怎么判断网页里有没有货”这件事好奇,这篇应该能帮到你。

1. 先搞清楚要解决什么问题:从“蹲点刷新”到“监控代劳”

1.1 当初是怎么被补货节奏折磨的

我在决定动手之前,试过不少“人工监控”的办法。最原始的方法是设闹钟,每隔一段时间就去刷新页面,看看有没有补货。听上去很合理,但实际执行起来问题很大。

首先是时间成本。每天工作已经够累了,还要记住每个平台大概的补货时间,根本没那么多精力。其次是网络抖动。即使页面真的显示有货,从你发现到打开购物车,可能已经过去几十秒,这时候库存早就被别的手快的用户拿走了。最崩溃的是那种“页面加载出来了,但是按钮还是灰色的”情况:你以为抢到了,实际上只是被缓存骗了。

我也看过别人做的提醒工具。但那些工具要么受众太大,消息发出来几秒钟就几十人转发,等看到消息再点进去已经晚了一步;要么需要各种复杂的浏览器插件配合,装完还得每天手动更新。考察了一圈之后,我确定自己需要的是这样一个项目:它能同时盯住多个商品链接,能对补货状态做判断,提醒足够及时且不烦人,配置要足够简单,换一个商品只需要改几行配置。这就是 AnyPS5 最初的需求清单。

1.2 AnyPS5 的定位:一个不知疲倦的守门员

如果你用过监控工具,可能会觉得这类东西都是“黄牛脚本”,其实不是。AnyPS5 的定位非常克制:它只做观测和提醒,不碰自动下单,不绕过排队机制,也不做任何高频率请求去冲击服务器。它就像一个帮你站在店门口盯“营业中”灯牌的守门员,灯一亮就打电话告诉你,你确定要买之后,自己走进去下单。

这种定位是我一开始就想清楚的。供应链和平台方都有一套自己的风控规则,做触发式、低频率的页面监控,本质上是普通消费者的正常访问行为,只是替代了人工刷新。反过来,如果加入自动下单、自动填地址这类功能,不仅容易跟平台规则产生冲突,还会把工具推到灰色地带。所以 AnyPS5 把边界画在“信息获取”这一侧:替你发现库存变化,把决策和购买动作留给真人。

1.3 用它之前,先想明白两件事

第一,监控工具不能保证你一定买到。库存情况瞬息万变,页面状态从“有货”变成“无货”可能就在一两分钟之内,网络传输、解析时间、通知延迟都会影响最后的结果。把它当成提醒器而不是保险箱,心态会好很多。

第二,使用频率要克制。更短的轮询间隔确实能让提醒更快,但也会让请求频率变得很高。任何网站都不会喜欢高频访问,被封了 IP,整个工具就废了。这两条边界想清楚之后,整个项目的实现思路就变得很清晰了。

2. 轮询、解析、决策:AnyPS5 的核心逻辑长什么样

2.1 数据源选择:公开页面就是最好的接口

做网页监控,第一个要回答的问题是“数据从哪里来”。平台方通常不会为普通消费者提供实时库存 API,所以我选择直接从商品页面获取数据。具体来说有两种路径。

第一种是抓取商品页的 HTML 正文,然后用选择器定位关键节点。这种方式实现起来简单,只要页面结构稳定,就能稳定工作。第二种是拼接平台内部的 Web 接口。很多商品页面在加载时,会向后端请求一个 JSON 数据接口,里面包含库存状态、价格、SKU 信息等完整字段。直接在程序里模拟这个请求,就能拿到更结构化的数据。

我自己在 AnyPS5 里主要用第二种方式,因为 JSON 接口返回的字段稳定且明确,不需要解析 HTML。需要注意的是,有些网页是“前端渲染”的,页面核心内容是 JavaScript 加载后才出现,直接用 requests 抓到的 HTML 里根本没有商品信息。遇到这种情况,要么用无头浏览器,要么就直接调背后的数据接口。无头浏览器成本高、内存占用大,所以我优先选择找接口。

2.2 判断“有货”的三种手段

拿到页面数据之后,最关键的一步是判断当前是不是真的能买。我试过三种方式,各有优劣。

判断方式实现思路优点缺点适用场景
JSON 字段法读取接口返回的库存状态字段(如stockStatus: "in_stock")准确、结构化需要对接口请求做反向分析能找到内部接口的平台
文本关键词法在 HTML 或接口文本里搜索“加入购物车”“立即抢购”等词语实现最快易受页面文案变化影响没有明确字段,只能抓页面时
状态与价格组合法库存字段和价格字段同时满足条件才判定有货误报率低判断逻辑复杂平台会显示“有货”但实际无法下单的场景

我实际用下来,文本关键词法最容易踩坑。比如某个页面在无货时也会展示“缺货登记”按钮,关键词里恰好包含“缺货”两个字,程序就可能把“登记按钮”误判成“有货”。后来我所有判断都改成“状态字段 + 关键词”双重确认,大大减少了误报。

2.3 轮询策略:不是请求越频繁就越快

轮询间隔是整个监控系统里最需要克制的地方。很多朋友一开始会觉得:我每 5 秒刷一次,肯定比每 30 秒刷一次先看到补货。理论上确实如此,但实际运行时会带来两个问题。

第一个问题是容易被平台的风控系统识别为异常流量。同一个 IP 以固定频率高频访问商品页,很快就会触发频率限制,轻则要求验证码,重则直接封禁。第二个问题是资源浪费。补货本身是低频事件,频繁轮询大多数时候只是在反复请求同一个“无货”结果,白白消耗带宽和服务器资源。

AnyPS5 的做法是“固定间隔 + 随机抖动 + 重试退避”。基础间隔设置成 10 到 15 秒,每次请求之前生成一个 0 到 5 秒的随机延迟,把请求时间打散,让访问模式更像真实用户。如果某次请求超时或返回异常,下一次重试的间隔要拉长到原来的两倍,而不是立刻重试。这三个机制叠加之后,监控的稳定性远好于一根筋的固定频率轮询。

2.4 登录态和 Cookie 的取舍

监控不是所有平台都要求登录,但确实有一部分页面,只有登录后才能看到真实库存信息。AnyPS5 支持把用户手动登录后获得的 Cookie 复制到配置里,在请求时带上。

这里要提醒一句:Cookie 是有有效期的。有的平台几小时就失效,有的可能撑几天。用 Cookie 做监控,就要接受“定期手动更新”这个现实。我会把 Cookie 失效导致的异常(比如频繁跳转登录页)当作监控系统要处理的一种状态,而不是简单归因于网络问题。在实际配置里,每次启动程序都会先做一次“可用性探测”,如果请求结果明显异常,就直接在日志里提示用户去更新 Cookie,免得白白轮询一整天。

3. 从检测到提醒:让消息自己找上门

3.1 通知渠道怎么选:延迟和稳定性的权衡

检测到库存变化只是第一步,如果通知不及时,一切都白搭。我对比了常用的几种通知方式,列成一个表:

通知方式延迟稳定性成本适合场景
本地桌面通知最低差(电脑休眠就失效)零成本人一直坐在电脑前
邮件提醒中好零成本不要求秒开,看看记录即可
聊天软件机器人低好零成本适合大多数人,手机实时接收
短信最低最好按条收费重要到愿意付费提醒

我最后选的是机器人推送。原因有两个:一是手机端能实时收到消息,延迟基本在几秒内;二是机器人消息可以附带商品链接和库存详情,点一下就能跳转到下单页面。邮件虽然稳定,但邮件客户端推送普遍有延迟,等看到邮件再打开页面,黄花菜都凉了。本地通知则有一个不便之处:即使电脑开着,消息也只会在桌面弹出,人不在电脑前就完全失效。

3.2 去重与防轰炸:只在真正需要时打扰用户

监控跑起来之后,会遇到一个新的问题:页面库存状态可能在“有货”和“无货”之间反复横跳,如果不做去重,一天可能会收到几十条通知。这比不通知还烦人。

AnyPS5 在通知模块里加了两个机制。第一个是状态变化检测:程序每次检查后会把当前状态存下来,只有状态从“无货”变成“有货”时才触发通知,连续多次有货只通知一次。第二个是冷却时间:即便状态发生变化,也要等冷却时间结束才能再次发送通知。我一般把冷却设成 30 分钟,目的是过滤掉那种“页面状态闪断”造成的假提醒。在这 30 分钟里,如果用户已经抢到货了,也不会被后续重复消息打扰。

3.3 部署位置的延迟差异:放家里和放云上的区别

监控程序可以跑在本地电脑上,也可以部署到云服务器上。如果放本地,优点是网络延迟低,缺点是电脑一关、网络一断,监控就停了。如果放云上,最大的好处是 24 小时不间断,缺点是云服务器和平台之间可能存在更高的网络延迟,且要额外承担一台服务器的成本。

我建议先在家里电脑上把工具调试稳定,再部署到低配云主机上长期运行。云主机配置也不需要多高,单核 1G 内存跑这种轮询程序绰绰有余。本地主要负责日常调试,云主机负责持续监控。这样的分工,既省钱又可靠。

4. 实际操作:把 AnyPS5 从零跑起来

4.1 环境准备

我选 Python 来实现,主要是生态里处理请求和解析的库都很成熟,写起来快。环境要求很简单:

  • Python 3.9 以上
  • requests:发 HTTP 请求
  • lxml:解析 HTML 和 JSON 数据
  • pyyaml:加载配置文件

在终端里执行安装命令:

pip install requests lxml pyyaml

然后把程序代码放在一个独立目录下,比如anyps5/。建议用虚拟环境装依赖,免得跟系统环境冲突:

python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install requests lxml pyyaml

4.2 写配置文件

AnyPS5 的监控对象完全由配置文件驱动。我维护了一份config.yaml,里面的结构大致如下:

monitor: base_interval: 10 # 基础轮询间隔,单位秒 jitter: 5 # 随机抖动上限,单位秒 retry_backoff: 2 # 请求失败后重试的倍数 targets: - name: "store_a_ps5_disc" url: "https://store-a.example.com/item/12345" api: "https://store-a.example.com/api/item/12345/stock" stock_field: "stockStatus" in_stock_values: ["in_stock", "available"] confirm_keywords: ["加入购物车", "立即购买"] cookie_file: "cookies_store_a.txt" notify: type: "bot_push" bot_token: "这里填机器人的 Token" chat_id: "这里填接收消息的会话 ID" cooldown_minutes: 30

monitor部分控制轮询策略。targets部分是最核心的,每个监控目标都要指明请求地址、库存状态字段和确认关键词。notify部分配置通知渠道。整个设计的理念是:要监控新商品,只需要往targets里加一段,不用改任何程序逻辑。

4.3 核心监控逻辑代码

核心程序不复杂,我拆成了三个函数:加载配置、检查库存、发送通知。检查库存的主循环如下:

import requests import yaml import json import random import time def check_stock(target, session): headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} resp = session.get(target["api"], headers=headers, timeout=10) resp.raise_for_status() data = resp.json() status = data.get(target["stock_field"], "") page_text = json.dumps(data, ensure_ascii=False) if status in target["in_stock_values"]: for kw in target["confirm_keywords"]: if kw in page_text: return True return False def main(): with open("config.yaml", "r", encoding="utf-8") as f: config = yaml.safe_load(f) session = requests.Session() # 加载 Cookie try: with open(config["targets"][0]["cookie_file"], "r") as cf: session.headers.update({"Cookie": cf.read().strip()}) except FileNotFoundError: pass last_notify_time = 0 while True: for target in config["targets"]: try: stock = check_stock(target, session) if stock: now = time.time() if now - last_notify_time >= config["notify"]["cooldown_minutes"] * 60: # 这里调用通知模块 print(f"[{target['name']}] 有货了!") last_notify_time = now except Exception as e: print(f"检查 {target['name']} 失败: {e}") base = config["monitor"]["base_interval"] jitter = random.uniform(0, config["monitor"]["jitter"]) time.sleep(base + jitter) if __name__ == "__main__": main()

真实项目中,你还需要把“发送通知”的逻辑接进check_stock返回True的分支里,同时把状态变化记录到日志文件。上面的代码是一个最小可运行版本,重点展示了轮询节奏、状态判定和冷却控制这三个核心环节。

4.4 运行、验证和日志观察

启动命令很简单:

python monitor.py

启动后不要急着等补货,先做两步验证。第一步是看启动日志里有没有输出“检查 xxx 失败”之类的异常,如果有,说明 URL、Cookie 或网络配置有问题。第二步是故意临时改一个错误的confirm_keywords,让程序即使检测到有货也会走“正常通知”流程,从而验证通知链路是否通。等确认消息能收到,再把关键词改回正确值。

我在实际运行过程中,还会把每次检查的原始状态写入一个本地日志文件,方便回头排查。比如某天突然发现某平台一直显示“有货”,但点进去根本买不了,通过日志能很清楚地看到判定结果来自哪个字段,从而定位问题。

4.5 真实运行结果和调优

跑了一周之后,我的体会是:监控工具的“有效命中率”比“反应速度”重要得多。有一次确实检测到某个平台有货,从检测到消息推送只用了大概 5 秒,但我点进去时已经被抢完了。反过来,有些看起来“触发了”的情况,其实是平台端把页面状态改成“可预约”,并非真实库存。

经过几轮调优,我把基础轮询间隔从 10 秒调到了 15 秒。原因是要监控的目标比较多,10 秒已经足够触发平台的访问频率限制,15 秒反而稳定。对于真正想要的结果来说,慢几秒不重要,重要的是别被封。

5. 踩坑实录:连续几天没睡好换来的一手经验

5.1 页面改版后选择器全部失效

运行到第三天,我突然在日志里看到大量“解析失败”的记录。查了一下,原来某个平台的前端页面结构改了,原本能定位到“加入购物车”按钮的 CSS 选择器全部失效。如果当初用的是无头浏览器,可能还能靠渲染后的页面兜底,但我是用请求接口的方式,接口路径也变了,所以只能重新分析。

这个坑的教训很直接:页面前端每隔一段时间就会改版,依赖 DOM 结构的方案天然不稳定。我更推荐优先找数据接口,因为接口字段的变更频率远低于页面 UI 的变更频率。改完之后,我还额外加了一层“页面结构变化检测”:每次解析失败超过一定次数,程序会在日志里给出明确提示,而不是默默吞掉异常继续空转。

5.2 轮询太猛,突然被“风控”

有段时间,我把轮询间隔调得非常激进,想看看最快能多快拿到补货提醒。结果运行不到半天,原本一切正常的请求开始频繁返回 403 状态码,再过一会儿,连首页都访问不了了。这就是典型的频率过高触发了平台的访问控制。

排查过程其实很无聊:换网络、换 User-Agent、删 Cookie,试了一堆方法,最终发现是请求频率问题。把间隔调回 15 秒,再开启随机抖动之后,服务很快就恢复正常。这个坑想分享的关键是:不要追求极致的轮询速度。平台有流量监控,个人工具更应该抱着“细水长流”的心态运行,而不是一次性把它打到不可用。

5.3 假补货:“显示有货”但加不进购物车

最让人崩溃的还不是漏报,而是误报。有一次工具提醒我“某平台有货了”,我兴冲冲地点进去,页面确实显示有货,但“加入购物车”按钮是灰的,点了没有任何反应。这种假补货往往因为页面上的库存文案是静态模板,库存字段并没有真实更新。

我把这个场景记录成“需要人工二次确认”的状态,在通知消息里直接注明“页面出现可购买字样,但可能无法直接下单”,避免看到消息后空欢喜。同时,我把判定逻辑改成“状态字段 + 确认关键词”双验证:只有接口里的库存状态明确为“可购买”,且页面文本里包含真实可点击的按钮文案,才判定为真补货。

5.4 时区和时间戳的坑

这个坑是很小,但特别容易在排查日志时把人搞晕。记录日志时,我一开始用的是系统默认的 UTC 时间,导致晚上看到日志里的“00:30”以为是深夜,实际上对应的是本地时间早上 8 点半。补货事件发生的时间点对后市分析很重要,建议所有日志统一用本地时区输出,并且在日志格式里加上%Y-%m-%d %H:%M:%S %Z,一眼就能看懂。

6. 更进一步的玩法:把监控能力迁移到别的场景

6.1 从 PS5 到其他商品:配置化才是核心

AnyPS5 的核心价值并不只在“监控 PS5”这一件事上。因为所有目标商品都是配置驱动的,把config.yaml里的 URL 和关键词换掉,就能监控其他商品。我试过用它盯显卡、限量手办、演唱会门票,工作原理完全一致。关键就是配置文件里每一行都理解清楚,换目标时就不会出错。

如果你想把它改成监控其他东西,只需要记住两个原则:第一,优先找数据接口而不是解析页面;第二,把确认方式设计得尽量严格,宁可漏报一次,也不要误报十次把你烦到崩溃。

6.2 把监控日志变成时间序列数据

在运行一段时间后,我决定把每次检查的结果都存储到本地 SQLite 数据库,而不只是写在日志里。这样就能画出补货状态随时间变化的趋势图,观察补货大概发生在什么时间段、什么频率出现。虽然单靠这些数据很难精确预测下一次补货,但至少可以帮你决定什么时候该真正坐在电脑前准备下单。

存储结构很简单:一张表存时间戳、商品名称、库存状态、价格,每次检查插入一行。等到积累几百条记录之后,用最简单的统计就能看出很多有意思的模式。比如某个平台经常在周末补货,某个平台在工作日凌晨有集中放货。有了这些信息,监控策略可以做得更有针对性。

6.3 少一点贪心,多一点克制

项目做到最后,我最大的体会其实不是技术。AnyPS5 本质上是个提醒工具,它省最多的是人的注意力,而不是替你锁定一台主机。每次补货之后,能不能抢到,还是取决于你的网络、手速和运气。想通了这一点,你对这类工具的心态就会变得很健康:它帮你在正确的时间出现在正确的地方,剩下的努力还得自己做。

如果你准备照着这套思路搭一个自己的监控工具,先从最简单的单目标开始,把通知链路打通,再慢慢加配置。不要一上来就追求监控几十个平台,那样只会让自己陷入无尽的调参和误报处理中。运行一段时间,根据日志调整策略,这个工具最终会变得非常顺手。

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

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

立即咨询