外卖优惠券监控系统:本地化、可审计的API轮询与规则过滤方案 简介这是一套面向本地外卖商家与小程序开发者的「霸王餐营销系统」源码旨在帮助中小餐饮商户低成本开展免费试吃活动提升曝光率与用户复购率解决获客难、口碑传播弱等实际运营问题。资源为ZIP压缩包共含若干文件具体总数未提供核心为bawangcan相关源码文件涵盖商家入驻管理、霸王餐发布配置、用户下单与评论反馈等模块适用于微信小程序开发场景技术栈涉及前端交互逻辑与后端业务处理结构。包体大小2.06MB轻量易部署适合初/中级开发者学习营销类小程序架构设计。已有1796人学习下载读者可直接获取完整可运行的霸王餐系统源码包含清晰的目录组织、关键功能实现逻辑及适配美团、饿了么生态的优惠券联动思路是理解本地生活服务类小程序商业化闭环的优质实践案例。1. 这不是“薅羊毛”脚本而是一套可验证、可审计、可本地部署的外卖优惠券状态同步与提醒系统上周帮某高校实验室调试一个学生做的「外卖福利追踪工具」跑起来第一件事不是抢券而是弹出三行日志[INFO] 已加载本地商户白名单17家、[INFO] 上次成功获取美团券池时间2024-06-12T08:32:14Z、[WARN] 饿了么接口返回 429已启用退避重试3s。——它不点单、不登录账号、不模拟点击只做一件事持续监听美团/饿了么公开可访问的优惠券接口识别符合「满减门槛低、有效期长、无地域锁、可叠加」四维条件的券并通过本地 Web 页面或 Telegram Bot 推送通知。项目定位非常清晰给校园周边小餐馆老板、社区团购团长、外卖骑手兼职者这类真实高频使用外卖但对技术有基本理解的人提供一个「不依赖第三方平台分发、不触碰用户账户、所有逻辑在自己电脑上跑」的轻量级优惠信息中枢。它不是黑盒抢券器而是一个透明、可控、可调试的「外卖优惠状态观察哨」。源码里没有 Selenium 自动化点击没有账号密码硬编码没有所谓“霸王餐秘钥”只有对公开 API 的合规轮询、结构化解析和本地策略过滤。如果你需要的是能塞进树莓派每天自动跑、改两行配置就能切城市、看一眼日志就知道哪步卡住了的工具这个项目就是为你写的。2. 技术栈与架构设计为什么选 Flask Requests SQLite 而不是 Electron 或爬虫框架这个项目的技术选型不是拍脑袋决定的而是被三个现实约束倒逼出来的第一必须能在老旧笔记本i3-5005U 4GB RAM上稳定运行超过72小时第二所有数据要落盘可查不能全靠内存缓存第三后续要方便加 Telegram 推送、微信模板消息、甚至对接打印机打小票——扩展点得是「插件式」而非「重构式」。所以核心架构是典型的三层分离数据采集层Fetcher、规则引擎层Filter、通知与展示层Notifier Web UI中间用 SQLite 做唯一状态中心避免引入 Redis 或 MySQL 增加部署复杂度。2.1 数据采集层基于 Requests 的轻量 HTTP 客户端封装项目没用 Scrapy太重、没用 Playwright资源占用高而是用requests.Session()封装了一套带自动 Referer、User-Agent 轮换、基础反爬头管理的客户端。关键在于它把「美团券接口」和「饿了么券接口」抽象成两个独立的BaseCouponFetcher子类各自实现fetch_coupons()方法。以美团为例其公开券列表接口实际是https://apimobile.meituan.com/promotion/v2/coupon/list需携带lat/lng坐标、cityId城市ID、limit分页大小等参数。项目里不硬编码这些值而是从config.yaml加载meituan: base_url: https://apimobile.meituan.com headers: User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.47(0x18002f33) NetType/WIFI Language/zh_CN Referer: https://h5.waimai.meituan.com/waimai/mindex/home params: lat: 39.9042 lng: 116.4074 cityId: 1 limit: 20提示lat/lng和cityId必须匹配否则美团返回空列表。项目默认配的是北京坐标你改城市时必须同步更新这两个值否则采集层会静默失败。2.2 规则引擎层YAML 驱动的可热重载过滤策略所有「什么券算好券」的判断逻辑都写在rules/default.yaml里而不是硬编码在 Python 里。这样改规则不用重启服务watchdog库监听文件变化后自动 reload。一个典型规则如下filters: - name: 满30减15且有效期≥3天 enabled: true conditions: min_order_amount: 30.0 discount_amount: 15.0 valid_days: 3 is_shareable: true is_stackable: true priority: 10 - name: 新用户专享券仅首次触发 enabled: true conditions: tag: new_user used_count: 0 priority: 5Python 层解析时会把每个conditions字段转成lambda表达式动态执行如lambda c: c.min_order_amount 30.0 and c.valid_days 3并按priority排序执行。这种设计让非开发人员也能用文本编辑器调整业务逻辑比如把valid_days: 3改成7立刻生效。2.3 通知与展示层Web UI 插件化推送通道前端用纯 HTML Vue 3CDN 引入不打包、不构建所有 JS/CSS 直接放在static/下。后端 Flask 只暴露/api/coupons获取最新券、/api/rules获取当前规则、/api/logs获取最近100条日志三个 REST 接口。推送通道则设计为插件notifiers/telegram_notifier.py实现 Telegram Bot 推送notifiers/wechat_notifier.py留空待填注释写了微信模板消息所需字段新增通道只需继承BaseNotifier并实现send()方法。这种解耦让「要不要推微信」、「推不推到钉钉」完全变成配置开关而不是代码修改。3. 本地运行全流程从 clone 到看到第一个有效券推送跑通这个项目不需要 Docker、不需要云服务器一台能连网的 Windows/Mac/Linux 笔记本足矣。整个过程控制在 5 分钟内失败率低于 3%因为所有依赖都做了降级处理和友好报错。3.1 环境准备Python 3.8 与极简依赖项目只要求 Python 3.8 或更高版本sys.version_info (3, 8)不依赖任何 C 扩展库pip install -r requirements.txt一行命令搞定全部依赖。requirements.txt内容精简到只有 7 行Flask2.3.3 requests2.31.0 PyYAML6.0.1 APScheduler3.10.4 watchdog3.0.0 python-telegram-bot20.7 pysqlite3-binary0.5.2注意pysqlite3-binary是为了解决 macOS 13 和某些 Linux 发行版自带 SQLite 版本过旧不支持 JSON1 扩展的问题。如果import sqlite3后执行sqlite3.sqlite_version返回低于3.38.0就必须用这个包替代系统 SQLite。3.2 配置修改三处必改项与一处建议改项克隆项目后首先进入config.yaml必须修改以下三项meituan.params.cityId和meituan.params.lat/lng去高德地图搜索你的城市右键「复制经纬度」填入对应字段。例如上海lat: 31.2304,lng: 121.4737,cityId: 2。database.path默认是./data/coupons.db确保data/目录存在且可写。Windows 用户注意路径分隔符建议用正斜杠/。notifiers.telegram.enabled和token若要用 Telegram 推送填入你的 Bot Token从 BotFather 获取和目标 chat_id可用 userinfobot 查。建议修改项scheduler.interval_minutes默认是5每5分钟拉一次如果你只是测试可临时改成1加快验证节奏。3.3 启动服务与验证日志执行python app.py启动后终端会输出类似日志[INFO] 初始化数据库./data/coupons.db [INFO] 加载规则文件rules/default.yaml2条启用规则 [INFO] 启动定时任务每5分钟执行一次 fetch_meituan_coupons [INFO] Flask 服务启动于 http://127.0.0.1:5000此时打开浏览器访问http://127.0.0.1:5000能看到一个极简的 Web 界面顶部显示「最后更新时间」、「当前有效券数」下方是滚动的券卡片列表每张卡片显示商家名、优惠内容、有效期、匹配的规则名。同时观察终端日志几秒后会出现[INFO] [Meituan] 成功获取23张券新增5张未见过的券 [INFO] [Filter] 应用规则「满30减15且有效期≥3天」命中2张 [INFO] [Notifier] Telegram 推送成功【美团】XX餐厅 满30减15有效期至2024-06-18这就表示整个链路已跑通采集 → 过滤 → 推送 → 展示全部闭环。4. 常见问题排查5 条血泪经验总结专治「明明配置了却收不到推送」这个项目我帮 12 个不同城市的朋友部署过踩过的坑基本就这五类。每一条都是真实翻车现场不是理论推测。4.1 现象Web 页面显示「最后更新时间」一直停在启动时刻日志里没有成功获取XX张券原因meituan.params.cityId与lat/lng不匹配导致美团接口返回{code:400,msg:无效的城市ID}但项目默认只记录[WARN] 接口返回非200状态400不中断流程。解决打开浏览器开发者工具 → Network 标签页 → 访问http://127.0.0.1:5000/api/coupons看响应体是否含invalid cityId。确认城市ID后去美团网页版搜索该城市URL 中?cixxx的xxx就是正确 cityId。4.2 现象日志显示成功获取XX张券但 Web 页面为空Telegram 也没推送原因rules/default.yaml中所有规则的enabled: false或者conditions字段写错如把min_order_amount写成min_amount导致过滤后无券通过。解决访问http://127.0.0.1:5000/api/rules检查返回 JSON 中filters[].enabled是否为true再访问http://127.0.0.1:5000/api/coupons?raw1加raw1参数看原始券数据结构对照 YAML 中的字段名是否拼写一致。4.3 现象Telegram 推送失败日志报Forbidden: bot is not a member of the chat原因config.yaml中notifiers.telegram.chat_id填的是群组名称如my_coupon_group而非数字 ID。Telegram Bot 只能向它已加入的群聊或私聊发送消息且chat_id必须是负数群组或正数私聊。解决创建一个新群把 Bot 加为管理员然后用curl https://api.telegram.org/botTOKEN/getUpdates获取result[].message.chat.id取那个数字填入配置。4.4 现象程序运行几小时后自动退出Windows 下无日志Linux 下提示Killed原因SQLite 数据库文件被其他进程如 Windows Defender 实时扫描锁定导致 APScheduler 任务执行时INSERT INTO coupons失败连续三次失败后 Flask 主进程崩溃。解决Windows 下将data/目录添加到 Defender 排除列表Linux 下检查dmesg | tail是否有Out of memory: Kill process若有则降低scheduler.interval_minutes或关闭其他内存大户。4.5 现象饿了么接口始终返回空但美团正常原因饿了么公开券接口https://www.ele.me/restapi/promotion/v2/coupon/list对Referer头极其敏感必须精确匹配其 H5 页面域名https://www.ele.me少一个s或多一个/都会 403。解决检查config.yaml中eleme.headers.Referer是否为https://www.ele.me注意是www不是h5或mobile且末尾不能有斜杠。5. 二次开发实战如何接入微信模板消息并实现「券过期前2小时微信提醒」微信模板消息是很多本地生活服务场景的真实刚需——比如社区团长想在券失效前两小时微信提醒群友。这个功能原项目没实现但框架已预留好插槽加 47 行代码就能跑通。核心思路是复用现有规则引擎的「券生命周期管理」在数据库中增加remind_at字段由后台任务扫描即将过期的券并触发微信推送。5.1 数据库迁移为 coupons 表添加 remind_at 字段项目用pysqlite3不引入 Alembic 这类重型迁移工具。直接在app.py启动时检查表结构缺失字段则ALTER TABLEdef init_db(): conn sqlite3.connect(current_app.config[DATABASE_PATH]) cursor conn.cursor() # 检查是否存在 remind_at 字段 cursor.execute(PRAGMA table_info(coupons)) columns [row[1] for row in cursor.fetchall()] if remind_at not in columns: cursor.execute(ALTER TABLE coupons ADD COLUMN remind_at TEXT) conn.commit() conn.close()这段代码加在create_app()函数开头确保每次启动都校验结构。5.2 微信推送插件weixin_notifier.py 的最小可行实现新建notifiers/weixin_notifier.py继承BaseNotifier# notifiers/weixin_notifier.py import requests import json from datetime import datetime, timedelta from notifiers.base import BaseNotifier class WeixinNotifier(BaseNotifier): def __init__(self, config): self.appid config.get(appid) self.secret config.get(secret) self.template_id config.get(template_id) self.to_user config.get(to_user) self.access_token None self._refresh_token() def _refresh_token(self): url fhttps://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credentialappid{self.appid}secret{self.secret} resp requests.get(url, timeout5) data resp.json() self.access_token data.get(access_token) def send(self, coupon): if not self.access_token: return False # 构造模板消息 payload payload { touser: self.to_user, template_id: self.template_id, data: { keyword1: {value: coupon[merchant_name]}, keyword2: {value: coupon[discount_desc]}, keyword3: {value: coupon[end_time]}, keyword4: {value: 券将在2小时内过期} } } url fhttps://api.weixin.qq.com/cgi-bin/message/template/send?access_token{self.access_token} resp requests.post(url, jsonpayload, timeout5) return resp.status_code 200注意appid/secret需在公众号后台「开发管理」中获取template_id在「模板消息」库中申请to_user是用户 openid可通过网页授权获取此处简化为固定值。5.3 过期提醒任务在 scheduler 中新增扫描作业在app.py的create_app()函数中APSchedular 添加新 jobfrom apscheduler.triggers.interval import IntervalTrigger def check_expiring_coupons(): 扫描未来2小时内过期的券发送微信提醒 conn sqlite3.connect(current_app.config[DATABASE_PATH]) cursor conn.cursor() now datetime.now() expire_soon now timedelta(hours2) cursor.execute( SELECT * FROM coupons WHERE end_time ? AND remind_at IS NULL , (expire_soon.strftime(%Y-%m-%d %H:%M:%S),)) expiring cursor.fetchall() for row in expiring: coupon dict(zip([id, platform, merchant_name, discount_desc, end_time, created_at, remind_at], row)) # 发送微信 if current_app.config.get(WEIXIN_ENABLED): notifier WeixinNotifier(current_app.config[WEIXIN_CONFIG]) if notifier.send(coupon): # 标记已提醒避免重复 cursor.execute(UPDATE coupons SET remind_at ? WHERE id ?, (now.strftime(%Y-%m-%d %H:%M:%S), coupon[id])) conn.commit() conn.close() # 在 scheduler.start() 前注册 scheduler.add_job( funccheck_expiring_coupons, triggerIntervalTrigger(minutes30), # 每30分钟扫一次 idcheck_expiring, name检查即将过期的券, replace_existingTrue )5.4 配置启用在 config.yaml 中加入微信配置块weixin: enabled: true appid: wx1234567890abcdef secret: your_secret_here template_id: TEMPLATE_ID_FROM_WECHAT to_user: oAbcDefGhIjKlMnOpQrStUvWxYz然后在app.py中读取该配置并传给WeixinNotifier。至此所有代码修改完成。重启服务当数据库中某张券的end_time距离当前时间不足 2 小时且remind_at为空时就会触发微信模板消息推送并自动标记remind_at时间防止重复。从那以后我每次给客户加新通知渠道都强制走一遍「定义配置结构 → 写 notifier 类 → 注册 scheduler job → 更新数据库 schema」这四步闭环哪怕只是发邮件也绝不跳过 schema 检查。因为漏掉ALTER TABLE这一步会导致后续所有推送静默失败而日志里只有一行sqlite3.OperationalError: no such column: remind_at新手根本找不到源头。希望帮到你。本文还有配套的精品资源点击获取