小规模单机监控用 schedule 足够,但需任务持久化、失败重试、并发控制或防重复执行时必须用 APScheduler;后者支持 SQLite/SQLAlchemy 存储、job 互斥、异常恢复,推荐首次部署即采用。

用 schedule 还是 APScheduler?别选错调度器
直接上结论:小规模单机监控用 schedule 足够,但只要涉及任务持久化、失败重试、多任务并发或需避免重复执行(比如爬虫被中断后重启不重复抓),必须换 APScheduler。前者轻量但无状态管理,后者自带 SQLite 存储和 job 互斥机制,能防止价格比对逻辑被同一时间触发两次。
常见错误是把 schedule.every().hour.do(check_price) 放在 while 循环里就以为“稳了”,其实一旦脚本异常退出,任务就彻底丢失;而 APScheduler 配合 BackgroundScheduler + SQLAlchemyJobStore 可以自动恢复未完成的 job。
实操建议:
- 首次部署优先用
APScheduler,哪怕只监控 1 个 SKU —— 后续加量不用改架构 - 禁用
coalesce=True以外的默认参数,否则网络抖动导致某次检查跳过,下次会堆积多个 pending job - 在
add_job()里显式传入max_instances=1,避免同一商品被并发请求两次
解析竞品页面时 requests + BeautifulSoup 总超时或被封?
不是代码写得不对,是没模拟真实访问链路。京东/淘宝等平台会校验 User-Agent、Referer、Cookie 甚至 TLS 指纹。单纯加个随机 UA 几乎无效。
立即学习“Python免费学习笔记(深入)”;
真正有效的做法是复用浏览器真实请求头,并控制请求频率:
- 用 Chrome DevTools 的 Network 面板复制一个成功加载价格的
curl命令,再用curl2requests工具转成 Python 代码 - 把
requests.Session()实例全局复用,避免每次新建 TCP 连接 - 强制设置
timeout=(3.05, 27)(连接 3.05s,读取 27s),避开某些 CDN 的慢响应陷阱 - 在每次请求后加
time.sleep(random.uniform(1.2, 2.8)),别信“1 秒足够”,实际要留出 JS 渲染+反爬验证时间
如果目标站用 React/Vue 渲染价格,BeautifulSoup 必然拿不到数据 —— 此时必须切到 playwright 或 selenium,但代价是内存占用翻倍,得用 --headless=new 和 slow_mo=500 参数压降资源消耗。
price_changed 判断逻辑为什么总误报?
核心问题在于没剥离干扰信息。竞品页面常混有促销价、划线价、会员价、满减后预估价,直接用 re.search(r'¥(\d+\.\d+)', html) 极易匹配错。
正确做法是定位 DOM 节点后再提取文本,并做数值归一化:
- 用 CSS 选择器锁定价格容器,例如
soup.select('span.price-normal')[0].get_text(),而不是全文正则 - 清洗时保留小数点后两位,但比较前统一转为
Decimal类型,避免浮点误差(float('199.9') == float('199.90')是 False) - 加入最小变动阈值判断,比如
abs(new_price - old_price) > Decimal('0.5')才算有效变动,过滤掉四舍五入抖动 - 记录原始 HTML 片段到本地文件(按日期+SKU 命名),方便后续人工核对误报原因
报警发微信/钉钉总收不到?关键在 webhook 签名校验
钉钉机器人必须带 timestamp 和 sign 参数,微信则要求 JSON body 中 msgtype 和 text.content 结构严格匹配文档。漏掉任意一项都会返回 400 或静默丢弃。
实操要点:
- 钉钉签名生成必须用当前毫秒级时间戳,且
sign = base64.b64encode(hmac.new(...).digest()),不能用hashlib直接 hexdigest - 微信发送前先用
requests.post(url, json=payload, timeout=5),别用data=json.dumps(...),否则 Content-Type 不对 - 报警内容里必须包含跳转链接,例如
f"https://item.jd.com/{sku}.html",否则运营人员无法快速定位 - 加一层本地日志兜底:每次调用 webhook 前,把完整 payload 写入
alarm_log_{date}.json,便于排查网络超时或鉴权失败
最常被忽略的是报警频率限制 —— 钉钉默认每分钟最多 20 条,微信每秒最多 20 条。如果 5 个 SKU 同时变价,得用队列+去重+合并发送,而不是循环发 5 次请求。


















