1. 项目背景与需求分析
最近两年外卖平台的优惠活动越来越复杂,作为一名经常点外卖的程序员,我发现手动领取优惠券不仅耗时,还经常错过限时活动。上个月统计发现,仅因为忘记领券就多花了近300元。于是决定用Python写个自动化脚本解决这个问题。
这个爬虫的核心功能需要实现:
- 模拟登录外卖平台账号
- 自动抓取当前可领取的优惠券列表
- 智能筛选最优优惠组合(满减券、折扣券等)
- 自动完成领取操作
- 将优惠信息持久化存储
重要提示:开发此类脚本需特别注意反爬机制,建议控制请求频率在10秒/次以上,避免对平台服务器造成压力。
2. 技术选型与环境准备
2.1 核心工具链选择
经过对比测试,最终技术栈如下:
- 请求库:Requests + Session(保持登录状态)
- 解析库:BeautifulSoup4(HTML解析)/ 直接处理JSON接口
- 自动化:Selenium(处理复杂验证场景)
- 存储:SQLite(轻量级本地存储)
- 调度:APScheduler(定时任务)
bash复制# 环境安装命令
pip install requests beautifulsoup4 selenium apscheduler
2.2 关键配置要点
- User-Agent轮换池:准备10个主流浏览器UA字符串
- 代理IP设置:建议使用本机IP,频繁更换IP易触发风控
- Cookie管理:使用
requests.Session()自动维护会话 - 超时控制:设置connect_timeout=5, read_timeout=15
python复制# UA配置示例
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)..."
]
3. 核心功能实现详解
3.1 登录模块实现
主流外卖平台登录方式分析:
| 平台 | 登录方式 | 难点 |
|---|---|---|
| 美团 | 手机号+验证码 | 滑动验证码 |
| 饿了么 | 账号密码+图形验证 | Token有效期短 |
以美团为例的模拟登录代码框架:
python复制def login_meituan(username, password):
session = requests.Session()
# 1. 获取登录页CSRF Token
login_page = session.get('https://passport.meituan.com/account/login')
token = parse_csrf(login_page.text)
# 2. 处理滑动验证码
captcha = handle_slider_captcha(session)
# 3. 提交登录请求
data = {
'username': username,
'password': encrypt_password(password),
'csrf': token,
'captcha': captcha
}
resp = session.post('https://passport.meituan.com/account/login', data=data)
return session if '登录成功' in resp.text else None
实战经验:美团新版登录需要先获取
riskChannel参数,可通过抓包分析获取生成逻辑。
3.2 优惠券抓取策略
3.2.1 页面解析方案
通过浏览器开发者工具分析,发现优惠券数据实际通过接口返回:
code复制GET /api/coupon/list?page=1&limit=20
Response: {
"data": [{
"id": "123456",
"name": "满30减5",
"condition": "满30元可用",
"value": 5,
"startTime": "2024-03-01",
"expireTime": "2024-03-31"
}]
}
解析代码示例:
python复制def fetch_coupons(session):
headers = {'Referer': 'https://www.meituan.com/coupon/'}
url = 'https://www.meituan.com/api/coupon/list'
params = {'page': 1, 'limit': 50}
try:
resp = session.get(url, params=params, headers=headers)
data = resp.json().get('data', [])
return [{
'id': item['id'],
'name': item['name'],
'threshold': float(item['condition'][1:-1]),
'discount': item['value'],
'expire': item['expireTime']
} for item in data]
except Exception as e:
print(f"抓取失败: {str(e)}")
return []
3.2.2 智能筛选算法
实现优惠券价值评估模型:
python复制def evaluate_coupons(coupons, history_orders):
"""基于历史订单数据评估优惠券实用价值"""
avg_order = sum(o['amount'] for o in history_orders)/len(history_orders)
scored = []
for c in coupons:
# 基础分 = 折扣金额/使用门槛
base_score = c['discount'] / c['threshold']
# 使用概率 = 历史订单中超过门槛的比例
use_prob = len([o for o in history_orders
if o['amount'] >= c['threshold']])/len(history_orders)
# 有效期权重 (剩余天数占比)
remain_days = (datetime.strptime(c['expire'], '%Y-%m-%d') - datetime.now()).days
time_weight = min(1, remain_days/7) # 按周计算
final_score = base_score * use_prob * time_weight
scored.append((c, final_score))
return sorted(scored, key=lambda x: -x[1])
4. 自动化领取与存储
4.1 领取接口逆向分析
通过抓包发现领取请求需要以下参数:
code复制POST /api/coupon/claim
Form Data:
- couponId: 123456
- location: 121.4737,31.2304
- deviceId: xxxxxxx
- token: xxxxxxx
关键实现代码:
python复制def claim_coupon(session, coupon_id):
url = 'https://www.meituan.com/api/coupon/claim'
data = {
'couponId': coupon_id,
'location': get_current_location(),
'deviceId': generate_device_id(),
'token': session.cookies.get('token')
}
resp = session.post(url, data=data)
if resp.json().get('success'):
print(f"成功领取优惠券: {coupon_id}")
return True
else:
print(f"领取失败: {resp.json().get('message')}")
return False
4.2 数据存储设计
使用SQLite实现本地存储:
python复制import sqlite3
from datetime import datetime
def init_db():
conn = sqlite3.connect('coupons.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS coupons
(id TEXT PRIMARY KEY,
name TEXT,
threshold REAL,
discount REAL,
expire TEXT,
claimed INTEGER DEFAULT 0,
claim_time TEXT)''')
conn.commit()
conn.close()
def save_coupon(coupon):
conn = sqlite3.connect('coupons.db')
c = conn.cursor()
c.execute("INSERT OR REPLACE INTO coupons VALUES (?,?,?,?,?,?,?)",
(coupon['id'], coupon['name'], coupon['threshold'],
coupon['discount'], coupon['expire'], 1,
datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
conn.commit()
conn.close()
5. 反爬对抗与优化策略
5.1 常见风控措施应对
| 风控类型 | 解决方案 | 实现示例 |
|---|---|---|
| IP频率限制 | 随机延迟(5-15秒) | time.sleep(random.uniform(5,15)) |
| 行为验证码 | Selenium模拟人工操作 | 使用ActionChains模拟移动 |
| 参数签名 | 逆向JS获取加密逻辑 | 使用PyExecJS执行关键JS代码 |
| 设备指纹 | 维护固定deviceId | 从本地存储读取/生成固定ID |
5.2 请求参数动态生成
关键参数加密示例:
python复制def generate_sign(params, secret_key):
"""模拟美团签名算法"""
sorted_params = sorted(params.items(), key=lambda x: x[0])
query_str = '&'.join([f'{k}={v}' for k,v in sorted_params])
sign = hashlib.md5((query_str + secret_key).encode()).hexdigest()
return sign
# 使用示例
params = {
'page': 1,
'limit': 20,
'timestamp': int(time.time())
}
params['sign'] = generate_sign(params, 'SECRET_KEY')
6. 完整脚本架构与调度
6.1 项目目录结构
code复制/coupon_bot
│── config.py # 配置文件
│── main.py # 主程序
│── spiders/ # 爬虫模块
│ │── meituan.py
│ │── eleme.py
│── utils/ # 工具函数
│ │── auth.py
│ │── storage.py
│── requirements.txt
6.2 定时任务配置
使用APScheduler实现智能调度:
python复制from apscheduler.schedulers.blocking import BlockingScheduler
def job():
print("开始执行优惠券抓取任务...")
session = login(username, password)
coupons = fetch_coupons(session)
scored = evaluate_coupons(coupons, load_history())
for c, score in scored[:5]: # 只领取前5个高价值券
if score > 0.5: # 阈值控制
claim_coupon(session, c['id'])
save_coupon(c)
scheduler = BlockingScheduler()
# 每天9点、12点、18点各执行一次
scheduler.add_job(job, 'cron', hour='9,12,18')
scheduler.start()
7. 实际效果与注意事项
经过一个月实测,该脚本平均每周能自动领取12-15张优惠券,其中约60%能在点外卖时实际使用,月均节省约150元。但需要注意:
- 法律合规:仅限个人使用,禁止商业用途
- 频率控制:设置合理间隔,我的配置是:
- 列表查询:每2小时1次
- 领取操作:每次间隔≥30秒
- 异常处理:对以下情况要做特别处理:
- 验证码触发时自动切换人工验证模式
- 遇到403错误立即暂停1小时
- 同一优惠券重复领取提示
python复制# 异常处理示例
try:
claim_coupon(session, coupon_id)
except Exception as e:
if '频繁操作' in str(e):
print("触发频率限制,暂停1小时")
time.sleep(3600)
elif '验证码' in str(e):
handle_captcha_manually()
这个项目最让我意外的是,通过分析历史订单数据优化领取策略后,优惠券的实际使用率从最初的35%提升到了62%。建议定期(如每周)运行analyze_usage.py脚本评估策略效果,持续优化算法参数。
