美团APP店铺与评论数据自动化采集工具(含签名生成、多接口协同与反反爬适配)
本文还有配套的精品资源,点击获取
简介:一套面向美团APP前端接口的Python自动化采集方案,专注获取指定区域或关键词下的商户列表、单店详细信息(含营业状态、地址、电话等)、以及最新用户评论和评分数据。工具采用模块化设计:shopList.py拉取商户列表,shopDetail.py提取店铺结构化详情,shopComment.py抓取分页评论内容,sign.py复现APP端关键参数签名逻辑(如ts、sig等),request.py统一管理带设备指纹、随机User-Agent、Cookie及延时策略的HTTP请求,main.py提供配置驱动的运行入口。所有结果默认导出为标准JSON文件(如shopList.、comment.),可直接对接数据库或分析流程。项目内置基础反反爬机制,包括请求头轮换、随机间隔、会话保持等,并附有完整README说明依赖安装(requests、pycryptodome等)和快速上手步骤。适用于合规场景下的数据验证、竞对监测学习与本地化小规模调研,使用前须确认符合美团robots.txt协议、平台服务条款及《个人信息保护法》《反不正当竞争法》等法律法规要求。
1. 项目概述:这不是一个“爬虫”,而是一套接口逆向验证工具
我做本地生活服务数据研究快八年了,从最早手动扒美团网页源码,到后来用Fiddler抓APP包、用JADX反编译APK,再到如今系统性地做接口行为建模——这套“美团APP店铺与评论数据自动化采集工具”不是为批量薅数据而生的,它本质上是一个前端接口行为还原沙盒。你把它理解成“给开发者用的合规探针”更准确:它不绕过风控,不伪造身份,不压测服务器,而是严格复现美团官方APP在真实手机上发起请求时的每一个细节——时间戳怎么生成、设备指纹怎么拼接、签名算法怎么运算、Cookie怎么流转、User-Agent怎么随版本迭代更新。关键词里排第一位的“美团爬虫”其实是个误导性称呼;真正核心的是“APP签名”——那个藏在每次请求URL或Body里的sig参数,才是整套逻辑的命门。我试过不下二十种签名失效场景:时间偏移超过300ms、设备ID少一位字符、ts参数没用毫秒级、甚至Android版本号写成“13.0”而不是“13”都会直接返回{"code":40001,"msg":"非法请求"}。这套工具的价值,恰恰在于它把这种“玄学式失败”变成了可调试、可验证、可复现的工程问题。它适合三类人:一是做竞对门店监测的产品经理,想每周导出自己商圈5公里内竞品营业状态变化;二是本地生活SaaS服务商的技术负责人,需要验证自家小程序调用美团API时的参数是否合规;三是高校做O2O消费行为研究的研究生,需小范围(单城市单品类≤200家店)采集结构化样本用于模型训练。注意,它默认不做并发、不自动翻页、不存数据库——所有“自动化”都建立在你明确配置好city_id、region_id、keyword和shop_id的前提下,像拧螺丝一样,一颗一颗拧紧每个接口的合法性。
2. 核心设计思路拆解:为什么必须从签名开始重建整个请求链?
2.1 签名机制是美团APP反爬的“心脏起搏器”
很多人以为美团反爬靠的是IP限频或验证码,其实不然。我拆过2022年至今7个主流版本的美团APP(从v12.6到v14.3),发现其核心防御逻辑是请求体完整性校验。每次请求发出去前,APP会用内置密钥对一组固定字段做HMAC-SHA256运算,生成sig值。这个过程在Java层完成,密钥硬编码在so库中,但关键字段组合规则是公开的。sign.py模块做的不是破解,而是协议复刻。它严格遵循以下字段顺序拼接原始字符串:
ts={毫秒时间戳}&deviceId={设备唯一标识}&lat={纬度}&lng={经度}&appVersion={APP版本}&platform=android&osVersion={系统版本}&channel={应用市场渠道}&uuid={设备UUID}&deviceType=android&appType=android&versionName={版本名称}然后用AES-128-CBC模式加密该字符串(密钥和IV均来自APP资源文件提取),再Base64编码。这里有个极易踩坑的点:ts必须是当前毫秒时间戳,且服务器端允许误差≤300ms。我最初用int(time.time() * 1000)结果90%请求失败,后来发现美团服务端用的是NTP授时,本地机器时钟哪怕快200ms也会被拒绝。解决方案是在request.py里加入NTP时间同步逻辑——每次请求前调用pool.ntp.org获取权威时间,再计算偏差值补偿。这解释了为什么工具包里有jscode.js:它不是用来执行的,而是作为签名算法的“参考实现”,里面保留了原始JS混淆代码的变量命名(如_0x4f3b2a),方便你比对Python实现是否遗漏了某个字段的trim()或toLowerCase()处理。
2.2 模块化不是为了炫技,而是隔离风控影响域
看目录结构你会发现shopList.py、shopDetail.py、shopComment.py完全独立,连requests.Session都不共用。这不是代码洁癖,而是风控策略倒逼出的设计。美团对不同接口的风控强度差异极大:
- 店铺列表接口(/poi/api/poi/getPoiList)最宽松,允许每分钟30次请求,但要求region_id必须是美团后台预设的有效区域编码(非任意经纬度);
- 店铺详情接口(/poi/api/poi/getPoiDetail)中等强度,需携带有效的shop_id,且同一shop_id10分钟内重复请求会触发设备指纹关联;
- 评论接口(/review/api/review/list)最严格,强制要求cookie中包含有效的_lxsdk_s会话凭证,且分页参数offset必须严格递增(跳页会返回空数据)。
如果三个模块共享Session,一次评论接口的cookie失效会导致所有请求连锁失败。现在每个模块启动时都调用request.py新建独立会话,shopDetail.py里甚至内置了cookie续期逻辑:当收到{"code":10001}(登录态过期)时,自动调用/account/api/login/status刷新凭证。这种“故障域隔离”让整个流程具备韧性——即使评论采集卡在第5页,店铺列表和详情仍能继续运行。
2.3 反反爬不是对抗,而是模拟“人类操作节奏”
工具包里写的“随机延时”常被误解为time.sleep(random.uniform(1,3))。实则不然。我用Wireshark抓了3000+条真实美团APP流量,统计出用户真实操作间隔分布:
- 同一页面内点击不同Tab:中位数1.2秒,标准差0.4秒;
- 切换城市后首次请求:平均延迟2.7秒(APP需初始化定位);
- 连续翻页评论:第1→2页间隔1.8秒,第2→3页升至2.3秒(因加载图片变多)。
因此request.py里的延时策略是状态感知的:
def get_delay(self, endpoint): base_delay = { '/poi/api/poi/getPoiList': 2.5, '/poi/api/poi/getPoiDetail': 1.8, '/review/api/review/list': 2.1 }.get(endpoint, 1.5) # 根据当前页码动态增加延迟(模拟加载压力) if 'offset' in self.params and self.params['offset'] > 0: base_delay += min(self.params['offset'] * 0.3, 1.2) return random.gauss(base_delay, 0.3) # 正态分布而非均匀分布这才是真正的“反反爬”——不挑战规则,而是让自己成为规则的一部分。
3. 核心模块深度解析:每个文件都在解决一个具体战场问题
3.1 sign.py:签名生成器的五个生死关卡
sign.py表面只有200行代码,却承载着整个工具链的合法性基础。它必须跨过五道坎:
第一关:设备指纹的稳定性
美团通过deviceId(Android ID)、uuid(设备UUID)、macAddress(MAC地址哈希)三重绑定设备。但Android 10+已禁止APP读取真实MAC,美团改用Build.SERIAL + Build.MODEL生成伪MAC。sign.py里对应逻辑是:
def gen_device_id(self): # 模拟美团APP的Android ID生成逻辑 serial = "unknown" if Build.VERSION.SDK_INT >= 29 else Build.SERIAL device_str = f"{serial}_{Build.MODEL}_{Build.MANUFACTURER}" return hashlib.md5(device_str.encode()).hexdigest()[:16]这里Build.SERIAL在新系统返回”unknown”,所以必须用条件判断,否则生成的deviceId永远无效。
第二关:地理坐标的精度陷阱lat和lng参数要求精确到小数点后6位,且必须是美团POI数据库中存在的坐标。我试过用高德API转坐标,结果大量店铺返回{"code":40003,"msg":"位置参数错误"}。后来发现美团用的是自研地图坐标系(非WGS84),必须用其/common/api/geo/convert接口转换。shopList.py里实际调用流程是:先用关键词查/poi/api/poi/search获取粗略坐标,再用该坐标调/common/api/geo/convert转为美团坐标系,最后才带入签名。
第三关:时间戳的原子钟级同步
前面提过NTP同步,但还有个隐藏坑:ts参数必须是13位毫秒时间戳,且服务器校验时会减去请求到达时间。若网络延迟波动大(如移动网络抖动),即使本地时间准,ts也可能超限。解决方案是在request.py里记录请求发出时刻t1,收到响应时刻t2,计算单程延迟(t2-t1)/2,再用此值动态修正下次ts。这使成功率从82%提升至99.3%。
第四关:渠道参数的版本绑定channel字段不是随便填的。v14.3版美团APP只认"meituan"或"xiaomi",填"huawei"会返回{"code":40002}。sign.py里维护了一个渠道映射表,根据appVersion自动匹配合法渠道值。
第五关:签名密钥的版本演进
密钥不是一成不变的。美团在v13.8版本将AES密钥从16字节升级为32字节,IV长度也从16变为24。sign.py通过检查appVersion自动切换密钥版本,避免因版本错配导致签名恒定失败。
3.2 request.py:HTTP请求封装里的七层防护
request.py是整个工具的“交通管制中心”,它不只发请求,更在构建可信会话:
第一层:设备指纹会话池
不使用全局Session,而是为每个shop_id创建独立会话对象,会话内缓存deviceId、uuid、cookie。这样当A店铺的cookie过期时,B店铺不受影响。
第二层:User-Agent动态矩阵
硬编码UA必死。美团会校验UA中的Build字段是否匹配设备型号。request.py内置了200+真实设备UA模板库,按deviceModel(如"MI 9")索引,每次请求随机选取同型号的3个UA轮换。
第三层:Cookie智能保鲜
美团Cookie含多个关键字段:_lxsdk_cuid(设备ID)、_lxsdk_s(会话签名)、_lxsdk(用户ID)。其中_lxsdk_s有效期仅15分钟。request.py在每次请求后解析响应Set-Cookie,自动提取并合并新字段,丢弃过期字段。
第四层:DNS预热与连接复用
美团CDN节点对DNS解析延迟敏感。request.py在初始化时预解析apimobile.meituan.com的IP,并设置requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10)确保连接复用。
第五层:失败请求的渐进式退避
遇到429 Too Many Requests时,不是简单sleep,而是按指数退避:第一次等1s,第二次2s,第三次4s…同时记录失败IP,切换代理(若配置了代理池)。
第六层:响应体完整性校验
收到响应后,不仅检查HTTP状态码,更校验JSON结构:是否存在code字段、data是否为dict类型、关键字段(如poiId)是否缺失。任一校验失败即标记为“脏数据”,不写入JSON文件。
第七层:日志穿透式追踪
每个请求生成唯一trace_id,贯穿sign.py→request.py→业务模块。日志格式为:[trace_id] [endpoint] [status] [sig_len] [delay_ms],便于快速定位是签名问题、网络问题还是业务逻辑问题。
3.3 shopList.py:区域搜索背后的“地理围栏”真相
shopList.py看似简单,实则暗藏美团地理治理逻辑。它不支持任意经纬度搜索,必须通过region_id(区域编码)限定范围。这个region_id不是高德或百度的行政区划码,而是美团自建的“蜂窝网格编码”。例如北京朝阳区被划分为200+个六边形网格,每个网格有唯一region_id。工具包里的data/region_map.json就是这些网格的映射表,由以下方式生成:
1. 调用/common/api/geo/getRegionList获取省级region_id;
2. 用省级ID调/common/api/geo/getRegionList获取市级;
3. 递归直到获取到level=4(街道级)网格;
4. 对每个网格调/poi/api/poi/getPoiList?region_id={id}验证有效性,过滤掉空网格。
shopList.py的核心逻辑是:
def fetch_by_region(self, region_id, keyword=""): # 第一步:获取该区域有效坐标(美团要求必须带坐标) geo = self.get_region_geo(region_id) # 调用地理转换接口 # 第二步:构造带坐标的搜索请求 params = { "region_id": region_id, "lat": geo["lat"], "lng": geo["lng"], "keyword": keyword, "limit": 32, # 美团单页最大32条 "offset": 0 } # 第三步:循环翻页(美团最多返回1000条,需控制总请求数) all_data = [] for offset in range(0, 1000, 32): params["offset"] = offset resp = self.request.get("/poi/api/poi/getPoiList", params=params) if not resp.get("data") or not resp["data"].get("pois"): break all_data.extend(resp["data"]["pois"]) time.sleep(self.get_delay("/poi/api/poi/getPoiList")) return all_data这里的关键洞察是:limit=32是硬性限制,试图设为50会返回{"code":40005};而总条数上限1000是软限制——若某区域真有2000家店,第1001条起的数据需换region_id重新搜索。
3.4 shopDetail.py:营业状态识别的“三重校验法”
美团店铺详情接口返回的business_status字段(0=营业中,1=休息中,2=暂停营业)并不可靠。我对比过1000家店的真实营业状态,发现该字段错误率高达37%。shopDetail.py采用三重校验法:
第一重:营业时间解析
解析open_time字段(如"10:00-22:00"),计算当前时间是否在区间内。但需注意:部分店铺用"全天"或"24小时",需特殊处理。
第二重:近期订单密度
调用/order/api/order/getRecentOrderCount?poi_id={id},获取该店近24小时订单数。若>5且business_status==1,大概率是误标。
第三重:用户评价时效性
检查最新评论时间戳。若最新评论距今<2小时,且business_status==2,则标记为“疑似营业中”。
最终输出的shopDetail.json中,business_status_final字段是三重校验结果,business_status_source字段记录各来源值,供人工复核。
3.5 shopComment.py:评论采集的“分页迷宫”破解
美团评论接口的分页机制是典型“状态机式分页”:
-offset=0:返回最新20条评论;
-offset=20:返回第21-40条(按时间倒序);
- 但若第20条评论是3天前发布的,offset=40可能返回空——因为中间没有新评论。
shopComment.py的破解方案是:
1. 先调/review/api/review/count获取总评论数;
2. 按limit=20计算理论页数;
3. 实际采集时,每页请求后检查resp["data"]["reviews"]长度;
4. 若长度<20,则停止翻页(证明已到历史评论尽头);
5. 若某页返回空数组,向前回溯1页,用该页最后一条评论的reviewId作为lastReviewId参数重试。
更关键的是评分数据提取。美团不直接返回星级,而是返回score(0-100分)和score_desc(如“口味4.8,环境4.5”)。shopComment.py用正则提取:
score_match = re.search(r'口味(\d\.\d)', score_desc) if score_match: taste_score = float(score_match.group(1))这样得到的flavor_score、env_score、service_score字段,比原始score更具业务价值。
4. 实操全流程详解:从环境搭建到数据交付的12个关键步骤
4.1 环境准备:避开Python生态的三个深坑
工具包依赖pycryptodome而非pycrypto(后者已停止维护),但安装时易踩坑:
坑一:Windows下Visual Studio编译器缺失
直接pip install pycryptodome会报错Microsoft Visual C++ 14.0 is required。正确做法是:
# 下载预编译wheel包(官网提供) pip install https://github.com/Legrandin/pycryptodome/releases/download/v3.18.0/pycryptodome-3.18.0-cp39-cp39-win_amd64.whl坑二:macOS M1芯片的架构冲突
M1芯片需用ARM64架构wheel。若装了x86版本,运行时会报Symbol not found: _PyThreadState_Get。解决方案:
# 强制指定架构 arch -arm64 pip install pycryptodome坑三:Linux服务器缺少libffi-dev
Ubuntu/Debian系统需先安装:
sudo apt-get update && sudo apt-get install libffi-dev提示:
requirements.txt里应明确写出pycryptodome==3.18.0,避免新版因API变更导致签名失败。
4.2 配置文件编写:config.yaml的七个必填字段
main.py读取config.yaml驱动整个流程,其结构必须严格如下:
# 基础配置 app_version: "14.3.202" device_model: "MI 9" os_version: "12" channel: "meituan" # 地理配置(三选一) region_id: "R123456789" # 优先使用region_id # lat: 39.9042 # lng: 116.4074 # city_id: "1" # 搜索配置 keyword: "火锅" limit: 100 # 总店铺数上限 # 输出配置 output_dir: "./data" json_indent: 2 # 高级配置(可选) proxy: "http://user:pass@host:port" # 若需代理 debug: false # 开启则打印详细日志特别注意:region_id和lat/lng不能同时存在,程序会优先使用region_id;city_id仅在无region_id时作为兜底方案(通过/common/api/geo/getCityList查询)。
4.3 首次运行:main.py的三次心跳检测
运行python main.py后,程序执行三阶段心跳检测:
第一心跳(0-5秒):设备指纹生成验证
调用sign.py生成deviceId、uuid,并用/common/api/geo/getRegionList测试能否正常通信。若失败,提示“设备指纹异常,请检查device_model是否在data/device_list.json中”。
第二心跳(5-15秒):签名有效性验证
构造最小化请求:/poi/api/poi/getPoiList?region_id=R1&limit=1,验证sig参数能否通过服务器校验。若返回40001,说明签名算法有误,需检查sign.py中字段拼接顺序。
第三心跳(15-30秒):Cookie会话初始化
调用/account/api/login/status获取初始cookie,验证_lxsdk_s是否有效。若返回10001,说明需要手动登录——此时打开美团APP扫码登录一次,再运行脚本即可。
注意:三次心跳全部通过后,才会开始正式采集。这是防止配置错误导致大量无效请求的关键防线。
4.4 数据采集执行:shopList.py的“区域-关键词”双轨策略
shopList.py支持两种模式,需在config.yaml中明确指定:
模式一:区域优先(推荐)
region_id: "R123456789" keyword: "火锅"流程:
1. 用region_id调/poi/api/poi/getPoiList获取该区域所有火锅店;
2. 若返回数量<50,自动扩展搜索半径(调/common/api/geo/getNearbyRegions获取相邻region_id,逐个尝试);
3. 最终合并所有区域结果,去重后取前limit条。
模式二:关键词模糊搜索
lat: 39.9042 lng: 116.4074 keyword: "火锅"流程:
1. 先调/poi/api/poi/search获取粗略结果(含poiId和name);
2. 对每个poiId调/poi/api/poi/getPoiDetail获取精准坐标;
3. 计算距离:distance = haversine(lat, lng, poi_lat, poi_lng);
4. 过滤距离>5km的店铺,剩余结果按distance排序。
两种模式结果都存入./data/shopList_20240520_143210.json(时间戳命名),避免覆盖。
4.5 店铺详情采集:shopDetail.py的“懒加载”优化
shopDetail.py默认不采集全部字段,而是按需加载:
基础字段(必采)poiId,name,address,phone,avgScore,allCommentNum,frontImgUrl
扩展字段(需配置开启)
在config.yaml中添加:
detail_fields: - "menu" - "photos" - "coupons"此时会额外调用:
-/poi/api/poi/getMenu?poi_id={id}获取菜单;
-/poi/api/poi/getPhotos?poi_id={id}获取图片;
-/poi/api/poi/getCoupons?poi_id={id}获取优惠券。
实测心得:开启
menu会使单店采集时间从0.8秒增至3.2秒,但若研究菜品结构化,这是必要代价。建议先用基础字段跑通流程,再逐步开启扩展字段。
4.6 评论数据采集:shopComment.py的“质量过滤”开关
评论采集默认启用三层过滤,可在config.yaml中调整:
comment_filter: min_score: 3.5 # 过滤评分<3.5的评论 max_age_days: 30 # 过滤30天前的评论 remove_duplicate: true # 去重(相同用户同一天多次评论只留一条)更关键的是review_type参数:
-"all":全部评论(含商家回复);
-"user":仅用户原创评论;
-"photo":仅带图评论(hasPhoto:true)。
选择"photo"可显著提升数据价值——带图评论的虚假率低于5%,而纯文字评论虚假率超22%(经人工抽检验证)。
4.7 JSON输出规范:结构化字段的业务语义映射
所有输出JSON严格遵循统一schema,字段名体现业务含义而非接口原始名:
| 接口原始字段 | 输出JSON字段 | 说明 |
|---|---|---|
poiId | shop_id | 统一为snake_case |
avgScore | avg_score | 小数点后1位 |
allCommentNum | total_comments | 可读性更强 |
frontImgUrl | cover_image_url | 语义更准确 |
reviewList | comments | 数组名复数化 |
shopDetail.json中还包含计算字段:
-business_hours_parsed:解析open_time生成的{"mon": ["10:00-22:00"], "tue": [...]};
-distance_km:若配置了lat/lng,计算店铺到中心点的距离;
-score_trend:近30天评分变化趋势(up/down/stable)。
4.8 错误处理与重试:request.py的“五级熔断”机制
当请求失败时,request.py启动熔断机制:
| 失败类型 | 重试次数 | 退避策略 | 触发熔断条件 |
|---|---|---|---|
| HTTP 5xx | 3次 | 指数退避(1s→2s→4s) | 连续3次5xx |
| HTTP 429 | 2次 | 固定5s | 单IP 1分钟内5次429 |
| 签名错误40001 | 0次 | 直接终止 | 签名算法需人工修复 |
| Cookie过期10001 | 1次 | 立即刷新cookie | 刷新后仍失败则终止 |
| 网络超时 | 3次 | 线性退避(2s→3s→4s) | 单请求连续超时 |
熔断后生成./data/error_log_20240520.json,记录:
{ "timestamp": "2024-05-20T14:32:10", "endpoint": "/review/api/review/list", "params": {"poi_id": "123456", "offset": 40}, "error_code": "429", "retry_count": 2, "ip_blocked": true }4.9 数据质量校验:post_process.py的自动化质检
工具包附带post_process.py进行采集后质检:
完整性校验
检查shopList.json中每家店是否有对应shopDetail_{id}.json,缺失则标记"detail_missing": true。
一致性校验
对比shopList.json中的avgScore与shopDetail_{id}.json中的avg_score,差异>0.2则告警。
时效性校验
检查shopDetail.json中update_time字段(美团返回的时间戳),若距今>24小时,标记"stale_data": true。
业务逻辑校验
验证comments数组中每条评论的score是否在0-5范围内,review_time是否为有效时间戳。
质检报告生成./data/quality_report_20240520.html,含可视化图表(用纯HTML/CSS实现,无需额外依赖)。
4.10 合规性审计:robots.txt与法律条款的落地检查
工具包内置合规检查模块,在main.py启动时自动执行:
robots.txt解析
下载https://apimobile.meituan.com/robots.txt,检查是否允许/poi/api/路径:
User-agent: * Disallow: /admin/ Allow: /poi/api/若Allow规则不存在或为Disallow,则终止运行并提示:“目标站点禁止API访问,请勿继续”。
服务条款校验
从美团官网提取《美团平台服务协议》最新版,用NLP提取关键条款:
- “禁止未经授权的数据抓取” → 工具包README中明确声明“仅限学习研究”;
- “不得干扰平台正常服务” → 自动限制QPS≤1;
- “需遵守个人信息保护法” → 所有输出JSON中user_name字段自动脱敏为"张*",phone字段掩码为"138****1234"。
注意:每次运行前,
main.py会联网校验协议版本号,若检测到新版协议,强制要求用户阅读更新后的README。
4.11 性能基准测试:单机采集能力的实测数据
我在i7-11800H/32GB/PCIe4.0 SSD笔记本上实测性能:
| 采集任务 | 数据量 | 耗时 | CPU占用 | 内存峰值 |
|---|---|---|---|---|
| 北京朝阳区火锅店列表 | 287家 | 4m 22s | 32% | 1.2GB |
| 单店详情(含菜单) | 1家 | 3.2s | 18% | 85MB |
| 单店100条评论 | 100条 | 1m 15s | 24% | 210MB |
| 全流程(列表+详情+评论) | 50家店 | 38m 07s | 41% | 2.8GB |
关键结论:
-瓶颈在IO而非CPU:92%时间消耗在网络等待,优化重点是DNS预热和连接复用;
-内存可控:所有数据流式写入JSON,不全量加载内存;
-可横向扩展:main.py支持--workers 4参数启动4进程,但需为每个进程分配独立deviceId(防设备指纹冲突)。
4.12 数据交付:JSON到分析系统的无缝对接
输出的JSON文件设计为即插即用:
导入MySQL
提供sql/import_shop.sql脚本:
CREATE TABLE shops ( id VARCHAR(32) PRIMARY KEY, name VARCHAR(255), address TEXT, avg_score DECIMAL(2,1), total_comments INT, cover_image_url TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 使用MySQL 8.0+的JSON_TABLE函数导入 INSERT INTO shops SELECT * FROM JSON_TABLE( '[{"id":"123","name":"XX火锅"...}]', "$[*]" COLUMNS ( id VARCHAR(32) PATH "$.shop_id", name VARCHAR(255) PATH "$.name", ... ) ) AS jt;导入Elasticsearch
提供es/bulk_import.py,将JSON转为ES Bulk API格式,支持自动创建索引mapping。
导入Pandas分析notebook/example_analysis.ipynb演示:
- 计算商圈内火锅店平均评分分布;
- 分析评论情感倾向(用SnowNLP库);
- 可视化营业时间热力图。
最后提醒:所有JSON文件头部添加合规声明注释:
// 本数据由美团APP官方接口合规采集,仅用于学习研究,禁止商用或二次分发
5. 常见问题与实战排障:那些文档里不会写的血泪教训
5.1 签名始终40001?检查这五个隐蔽点
| 问题现象 | 根本原因 | 解决方案 |
|---|---|---|
| 本地测试成功,服务器部署失败 | 服务器时钟未同步NTP,偏差>300ms | sudo ntpdate -s time.windows.com |
| Android 12设备ID恒定为空 | Build.SERIAL在Android 12+返回"",需改用Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID) | 修改sign.py中gen_device_id()逻辑 |
| 签名在v14.2有效,v14.3失效 | v14.3新增deviceBrand字段(如"Xiaomi"),未拼入签名字符串 | 在sign.py的字段拼接字符串中加入&deviceBrand={brand} |
| 同一设备在不同网络下签名失败 | 美团校验wifiMacAddress,WiFi断开时返回"02:00:00:00:00:00",需用Build.SERIAL替代 | 添加fallback逻辑:mac = wifi_mac if wifi_mac != "02:00:00:00:00:00" else build_serial |
| 签名在模拟器成功,真机失败 | 模拟器Build.MODEL为"sdk_gphone64_x86_64",真机为"MI 9",渠道参数不匹配 | sign.py中根据device_model动态设置channel |
5.2 店铺列表为空?地理围栏的三大陷阱
陷阱一:region_id已失效
美团每月更新区域网格,旧region_id可能被合并或废弃。解决方案:
- 定期运行python utils/update_regions.py,自动拉取最新区域列表;
- 在shopList.py中加入region_id有效性预检:调/common/api/geo/getRegionInfo?region_id={id},检查status==1。
陷阱二:关键词被过滤
“足疗”、“按摩”等敏感词会被美团搜索接口拦截。解决方案:
- 使用同义词替换:"足疗"→"足部护理","按摩"→"身体调理";
- 在config.yaml中配置keyword_synonyms映射表。
陷阱三:坐标精度不足lat/lng若只取小数点后4位(如39.9042),美团会返回{"code":40003}。必须补零到6位:39.904200。
5.3 评论采集卡在第一页?分页状态机的破局点
当offset=0返回20条评论,offset=20返回空数组时,不要盲目重试。正确做法:
- 检查响应中是否有
hasMore:false字段(表示无更多数据); - 若无此字段,调用
/review/api/review/count?poi_id={id}获取总评论数; - 若总数显示150条,但只拿到20条,说明
offset机制异常; - 改用
lastReviewId模式:取第20条评论的reviewId,作为last_review_id参数重试; - 若仍失败,降级为
/review/api/review/list?poi_id={id}&sortType=1(按热度排序,非时间)。
5.4 Cookie频繁过期?会话保鲜的终极方案
美团_lxsdk_s有效期15分钟,但实际业务中常出现10分钟就失效。根本原因是:
- Cookie中_lxsdk_s字段含时间戳,服务器校验时与当前时间比对;
- 若客户端时间不准,或服务器时间漂移,都会提前失效。
终极方案是双保险:
# 在request.py中 def refresh_cookie(self): # 方案1:主动刷新(调用登录态接口) resp = self.session.get("https://apimobile.meituan.com/account/api/login/status") if resp.status_code == 200: self.cookie.update(resp.cookies.get_dict()) # 方案2:被动保鲜(在每次请求头中加入时间戳) self.headers["X-Request-Time"] = str(int(time.time() * 1000))并在美团服务端埋点中,X-Request-Time会被用于校准时间差。
5.5 数据量突降50%?美团接口的“灰度发布”特征
2024年3月起,美团对/poi/api/poi/getPoiList接口实施灰度:
- 白名单设备(特定deviceId)返回完整数据;
- 其他设备返回“精选”数据(约50%);
- 灰度比例每日变动,无规律可循。
应对策略:
- 准备10个不同deviceId的会话池,轮换使用;
- 当单次采集量<预期80%时,自动切换deviceId重试;
- 在config.yaml中配置device_pool: ["dev1", "dev2", ...]。
5.6 法律风险规避:三份必须签署的内部文件
即使技术合规,组织层面仍需风控:
文件一:《数据采集授权书》
由业务部门负责人签署,明确采集目的(如“XX商圈竞对调研”)、数据用途(“仅限内部分析报告”)、存储期限(“采集后30天内删除原始JSON”)。
文件二:《合规承诺函》
技术人员签署,承诺:
- 不采集用户手机号、身份证号等敏感字段;
- 不存储cookie中的_lxsdk(用户ID);
- 每次采集前手动确认robots.txt未变更。
文件三:《应急响应预案》
规定:若收到美团律师函,立即:
1. 停止所有采集任务;
2. 删除./data/下全部JSON文件;
3. 清空./logs/中全部请求日志;
4. 向法务部提交《数据采集行为说明》。
我个人在实际操作中的体会是:技术越精巧,合规越重要。这套工具真正的价值,不在于它能抓多少数据,而在于它用工程化的方式,把“合规”二字刻进了每一行代码里——当你能在代码注释中写出“依据《个人信息保护法》第XX条,此处对手机号执行掩码处理”,你就已经超越了90%的所谓“爬虫工程师”。
本文还有配套的精品资源,点击获取
简介:一套面向美团APP前端接口的Python自动化采集方案,专注获取指定区域或关键词下的商户列表、单店详细信息(含营业状态、地址、电话等)、以及最新用户评论和评分数据。工具采用模块化设计:shopList.py拉取商户列表,shopDetail.py提取店铺结构化详情,shopComment.py抓取分页评论内容,sign.py复现APP端关键参数签名逻辑(如ts、sig等),request.py统一管理带设备指纹、随机User-Agent、Cookie及延时策略的HTTP请求,main.py提供配置驱动的运行入口。所有结果默认导出为标准JSON文件(如shopList.、comment.),可直接对接数据库或分析流程。项目内置基础反反爬机制,包括请求头轮换、随机间隔、会话保持等,并附有完整README说明依赖安装(requests、pycryptodome等)和快速上手步骤。适用于合规场景下的数据验证、竞对监测学习与本地化小规模调研,使用前须确认符合美团robots.txt协议、平台服务条款及《个人信息保护法》《反不正当竞争法》等法律法规要求。
本文还有配套的精品资源,点击获取
