饿了么商家信息爬虫实战:接口分析、protobuf与反爬对抗
简介本资源是一款面向数据分析初学者与市场研究从业者的饿了么平台商家信息自动化采集工具聚焦解决外卖行业竞品分析、消费行为研究及本地生活服务数据获取等实际问题。压缩包共19个文件含5个核心Python爬虫脚本如eleme_spider.py、main-spider.py、2个编译缓存文件、10张界面与流程示意图PNG、1份PDF使用指南及1份Markdown说明文档整体大小3.93MB结构清晰便于快速部署与调试。资源已获37人学习下载适合希望掌握真实电商平台反爬应对策略如Cookie分割、动态请求模拟、HTML解析与结构化数据存储JSON/CSV的实践者。用户可直接运行主爬虫程序获取商家名称、地址、菜品详情、价格、评分及评论等完整字段并通过txt_to_excel.py一键转为Excel表格配套图文指南与代码注释降低了学习门槛。 我一直觉得爬虫项目是入门和进阶Python最好的“实战训练场”尤其是当你把目标锁定在外卖平台这种业务逻辑复杂、反爬手段拉满的头部产品上时整个学习过程会变得异常过瘾。今天分享的这个项目就是围绕“饿了么商家信息爬取”展开的一个被打包成.zip文件的完整工具核心目标是解决一个非常具体的问题如何高效、稳定地拿到一个区域内的商家列表及其基础信息。这篇内容不是给爬虫小白扫盲的而是给那些已经会用requests、能解析JSON但想更进一步想知道怎么啃硬骨头的人。这个项目能帮你搞定的不只是“把数据下载下来”而是通过一个真实场景把接口分析、签名机制、数据结构和反爬对抗这几个环节完整串起来。你会发现真正有价值的不是那个爬虫脚本本身而是你在拆解“饿了么为什么这么设计接口”“它的数据为什么是这种格式”的过程中建立起来的系统分析能力。无论你是想做本地生活的数据观察、竞品分析还是单纯想找个项目练手顺便吃点技术红利这篇内容都值得你花几分钟看完。1. 立项前先想清楚这个爬虫到底在爬什么很多人在写爬虫的时候有个坏毛病拿到一个网站就开始F12、翻包、写正则结果爬到一半发现数据对不上、反爬升级整个项目推倒重来。真正的问题在于你根本没想清楚“你要的数据在系统里是怎么流动的”。1.1 商家信息不等于网页上的卡片数据在饿了么的场景里一个商家能被你看到背后经过了“LBS定位服务”算出距离范围、“流量分发系统”做排序、最后通过商家中心服务把营业状态、评分、配送时长等等字段拼装成前端展示数据。所以当你打开饿了么网页版看到的每一个商家卡片其实是一个已经聚合完毕的ViewObject。我们做爬虫如果只盯着网页上的卡片去解析会非常痛苦因为前端字段名是压缩混淆过的而且一次展示的数据量也有限。真正合理的方式是直接找到这个ViewObject背后的HTTP接口拿到它返回的原始数据再按自己的需求重新组装。1.2 工具的使用边界和预期目标这个项目最终的定位是只爬取“公开可见”的商家基础数据包括商家ID、名称、电话、地址评分、月售、平均配送时间、起送价、配送费营业时间、公告、品类标签商家所属的经纬度坐标这个在接口里很重要有一点必须提前说明白爬虫工具从立项开始就要把合规放在第一位。利用爬虫抓取公开数据用于学习、研究和有限度的数据分析是有价值的但如果把这个工具用在商业竞争、用户隐私收集、或者任何违法违规的用途上那性质就完全变了。我写这篇文章的初衷是希望大家通过这个案例掌握爬虫技术底层逻辑不是鼓励你去骚扰平台、批量拉取商业数据谋利。任何技术的价值都在于应用方式请务必守住底线。1.3 为什么拿饿了么当案例最合适因为饿了么的数据交互方式代表了一类“重业务”App的通用形态protobuf协议传输、字段强校验、请求签名保护、用户身份动态追踪。你把这个项目啃下来了再去看美团、京东、抖音本地生活这类的接口会发现很多思路是相通的。这个项目的学习收益远远大于“能爬到一个网站的数据”这件事本身。2. 动手前的技术预研为什么我没有直接用浏览器自动化很多人拿到这个需求的第一反应是用Selenium或者Playwright模拟浏览器点击然后从渲染完的页面上拿数据。这个方案在技术上可行但完全不推荐原因有两个慢而且脆。2.1 浏览器自动化方案的根本缺陷一个饿了么商家列表页无限滚动加载五次能展示大约40个商家每个商家卡片上至少包含了30多个字段。用Selenium去模拟滚动每一次滚动都要等待图片、CSS、JS全部渲染完成再加上页面本身的懒加载机制抓完一个城区的商家可能要跑一整天。更麻烦的是前端页面经常改版一旦DOM结构调整你的所有定位器全部失效维护成本极高。另外一个隐患是浏览器的环境特征太明显反爬系统可以很轻松地识别出你是不是真实用户。当你在无头浏览器里高频刷新页面时大概率触发验证码或滑块项目还没跑起来就先被盾了。2.2 正确的技术路径直接分析HTTP接口真实的做法是抓包分析饿了么web端和App端的请求找到商家列表的数据接口然后直接模拟这个接口的请求方式。这样有几个好处接口返回的数据是结构化的不用自己从乱七八糟的DOM里抠字段请求体积小、速度快单位时间能拿到的数据量是浏览器方案的几十倍接口路径相对稳定只要平台不改协议代码不用大动从实际效果来看用接口方式爬取一个城市某个商圈基本上能把数据控制在分钟级完成而且反爬触发概率比浏览器方案低很多。2.3 抓包工具的选择与配置细节我在这套工具里用的是Charles mitmproxy组合。Charles用来分析Web端mitmproxy用来跑脚本分析App端。如果你只用Web端其实Charles就够了但要注意配置好SSL代理并且安装证书到你的测试设备上。在抓包的时候有个经验饥饿么的很多接口不是一次性返回全部数据而是分页的你要注意观察请求参数里的offset、limit字段。我第一次抓的时候翻遍了响应体也没找到“下一页”的信息后来才发现它的分页逻辑是靠请求参数控制的服务端通过返回的has_next字段告诉客户端还有没有更多数据。这个细节直接决定了你后面写爬虫循环的时候怎么控制终止条件。3. 核心攻坚一protobuf字段还原与词表映射机制如果说这个项目里最让人头疼的地方排第一的绝对是接口返回的数据看不懂。当你第一次打开商家列表接口的响应体时大概率会看到一堆乱码不是JSON不是XML而是一坨二进制内容。3.1 为什么饿了么选择protobuf协议protobufProtocol Buffers是Google推出的结构化数据序列化格式特点是压缩率高、解析速度快、传输体积小。对于饿了么这种日请求量巨大的平台用protobuf比用JSON能省下至少30%的带宽成本服务端和客户端的解析速度也会快很多。但对爬虫开发者来说protobuf带来的直接问题就是你看不到字段名了。JSON里清清楚楚写的“shopName”在protobuf里可能变成了数字1、2、3这种编号你需要自己去还原每一个数字编号对应的真实含义。3.2 通过已知数据反推proto结构还原protobuf字段的整个过程就像是在没有地图的情况下拼一张拼图。我的思路是这样的第一步先构造一个可控的查询请求比如只搜索一个非常偏僻的地址让返回的商家数量只有一个。这样响应体就非常小方便逐步拆分。第二步用protobuf的通用解析工具比如blackboxprotobuf这个Python库去解析二进制流它会自动把每个字段的编号、类型、值都识别出来。比如你会看到一个field 1的类型是string值是某个商家的名字field 2的类型是varint值是5结合常识可以猜测这可能是评分。第三步反复改变请求条件比如精确搜索一个你知道评分的商家看哪个字段的数值跟着变化。通过这种“控制变量法”可以逐步把字段编号和业务含义对上号。3.3 词表映射工具的工程实现一旦还原了关键的proto结构下一步就是做一个字段映射表。我在这套工具里维护了一个Python字典把proto字段编号翻译成可读的中文字段名类似这样SHOP_BASE_FIELDS { 1: shop_id, 2: shop_name, 3: shop_phone, 4: shop_address, 5: rating, 6: month_sales, 7: delivery_time, 8: delivery_fee, 9: min_order_amount, 10: opening_hours, 11: announcement, }做完这一步你的爬虫工具才算是真正“看懂”了饿了么的商家数据。后续所有数据清洗、入库、分析都建立在这个词表映射之上。在开始写代码之前必须得提醒一句如果你拿到的二进制数据里出现了无法识别的字段类型或者字段编号跳跃很大不要硬猜。先用已知数据把最核心的10到15个字段还原出来就够了商家信息层面的分析完全够用。那些乱七八糟的扩展字段等你确实需要了再慢慢补。4. 核心攻坚二请求签名机制的分析与模拟搞定了响应数据解析下一个拦路虎就是请求签名。饿了么的接口不是说你拿着URL直接get就能返回数据的它要求请求头里必须带上一系列校验参数而且这些参数有时间戳、加密算法、防重放机制缺一个都不行。4.1 请求头里到底藏了哪些秘密通过抓包对比你会发现饿了么的请求头里有几个关键的动态参数token用户身份标识但不是简单的登录凭证而是由服务端下发的一个加密字符串sign请求签名是请求参数与一个秘钥拼接后做MD5或SHA256得到的device_id设备指纹用于识别当前请求来自哪个物理设备shumei_id数美设备的标识这是风控系统的重要组成部分标记你的设备是否可信cookie这里面包含了用户ID、登录状态等信息这几个参数里sign是核心。因为就算你伪造了其他所有参数只要sign不对服务端直接拒绝响应。4.2 定位签名算法的基本思路关于sign的生成逻辑我不能在这里把完整的可复现代码给你因为那是直接把进攻性的攻击工具递到每个人手里。但分析思路是通用的我可以讲清楚第一步是在JS文件里搜索关键词sign、sha256、md5、encrypt找到所有可能加密的位置。第二步是在浏览器控制台里打断点跟踪签名生成函数被调用的上下文看它到底拼接了哪些参数。第三步是把整个加密逻辑用Python复写一遍用自己的代码生成签名然后用这个签名去请求接口验证是否能用。这个过程本质上是逆向工程它非常考验功底的细腻程度和编程的调试能力。需要你反复尝试、比对没有任何现成的库能直接帮你算出签名。而且饿了么的签名逻辑会不定期更新可能这个月你破解了下个月它就换了算法。所以在设计爬虫框架的时候我建议做一层签名模块抽象把签名的生成逻辑单独封装成一个类后续平台改算法你只需要改这一个文件。4.3 为什么我不建议你去搞逆向破解的“一劳永逸”我见过很多做爬虫的人把大量精力花在逆向破解签名上追求“完美模拟客户端”。但说实话对于商家公开信息这种级别的数据投入产出比是很低的。原因有三第一签名算法再复杂它的核心目的是反作弊不是反爬虫。平台真正要拦截的是刷单、薅羊毛这种影响业务的行为而你用低频率去爬商家的公开信息本来就不在风控的优先级里。第二绝大多数情况下你可以绕开签名。比如Web端有些接口的签名校验比App端宽松或者你可以直接用服务端渲染的页面数据根本不需要走那个加密接口。第三如果你的爬虫真的需要做到“高并发、高强度、持续稳定”地获取商家数据那你问的问题就不是技术问题了而是商业模式和法律风险的问题。这种需求通常意味着你可能正在做与平台核心业务直接竞争的商业项目那已经不是爬虫技术能兜住的事。所以我的建议是签名机制你要会分析、能讲清楚原理就够了。具体到项目落地优先找那些公开的、无鉴权的数据入口把精力花在数据处理和业务挖掘上这才是爬虫项目真正的价值所在。5. 落地实施数据解析、清洗与入库的工程细节前面讲了协议、签名这些相对硬核的环节现在聊点实实在在的工程落地。5.1 从原始响应到结构化DataFrame当你成功拿到接口响应并且通过词表映射解析出可读字段之后剩下的工作就是常规的数据处理了。我一般会先丢进Pandas做一遍快速清洗主要做这几件事去重同一个商家在多个分页里可能出现按shop_id去重格式化把时间戳字段转成可读时间把字符串数字转成数值类型缺失值处理有些商家没有评分、没有公告这些字段要置为默认值清洗完之后的数据大概长这样import pandas as pd df pd.DataFrame(parsed_data) df[shop_id] df[shop_id].astype(str) df[rating] pd.to_numeric(df[rating], errorscoerce).fillna(0.0) df[month_sales] pd.to_numeric(df[month_sales], errorscoerce).fillna(0) df[delivery_fee] pd.to_numeric(df[delivery_fee], errorscoerce).fillna(0)5.2 数据存储方案的选择对于这种量级的数据目标城市大概几千个商家用SQLite就够了不需要上MySQL、PostgreSQL。SQLite的好处是零配置、单文件、Python内置支持非常适合做个人项目的数据存储。表结构我建议至少分两张表shops存储商家静态信息包含shop_id、名称、电话、地址、经纬度等shop_metrics存储商家的动态指标包含评分、月售、配送时间等每次采集都插入一条新记录之所以要分开是因为商家的静态信息变化很少但动态指标每天都会变。分开存之后你就可以很方便地做时间序列分析比如观察某个商圈两个月内的评分变化趋势。CREATE TABLE IF NOT EXISTS shops ( shop_id TEXT PRIMARY KEY, shop_name TEXT, shop_phone TEXT, shop_address TEXT, latitude REAL, longitude REAL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS shop_metrics ( id INTEGER PRIMARY KEY AUTOINCREMENT, shop_id TEXT, rating REAL, month_sales INTEGER, delivery_time TEXT, delivery_fee REAL, min_order_amount REAL, opening_hours TEXT, announcement TEXT, collected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (shop_id) REFERENCES shops(shop_id) );5.3 增量爬取与数据更新的策略商家数据的爬取不能只跑一次就完事。评分、月售这种指标是会实时变化的所以需要设置定时任务做增量更新。增量爬取的两个核心策略是按时间维度增量只爬取昨天之后数据有变化的商家怎么判断有没有变化比较当前值和上次入库值的差异差异超过阈值才更新按区域维度增量对于一个城市可以按商圈划分批次每天选择不同的区域去爬避免对同一个区域造成太大的请求压力这也就回到了输入信息里提到的增量型爬虫概念。它的核心价值就在于既保证数据的时效性又不用每次都全量爬一遍对目标站点的压力也更小是更专业的爬虫形态。6. 抗封禁与异常处理爬虫生命周期管理的必修课爬虫写得好不好不看它在正常情况下跑得有多快而是看它在遇到封禁、风控、接口变动的时候能不能优雅地降级、恢复、继续。6.1 会话保持与并发控制requests库的Session对象一定要用好它能帮你在多次请求之间保持Cookies、Headers等上下文信息模拟一个完整的用户会话。另外强烈建议在Session中设置一个统一的Headers模板把User-Agent、Referer、Accept-Language都配好减少被风控系统标记的概率。并发控制在爬虫里是一门艺术。爬太快会被封爬太慢效率低。我实践下来比较舒服的节奏是线程池控制在4到6个线程每个请求间隔0.5到1.5秒随机延时。这个节奏既能保证数据采集效率又不会触发压力比较大的风控策略。当然具体阈值跟你的网络环境、目标区域都有关系需要实测调参。6.2 反爬触发后的应急响应机制不管你的爬虫设计得多优雅总会有那么一刻你的请求会突然返回一个验证码页面或者干脆返回一堆“非法请求”的JSON。这个时候怎么办我的经验是建立一套完整的异常检测和自动降级机制。具体来说在代码里做三件事第一识别风控特征。提前把“验证码”、“网络异常”、“非法请求”这些关键词整理成一个列表每次响应返回后先做一次关键词匹配一旦命中立刻停止当前任务。第二自动等待退避。触发风控后不是立即重试而是等待一段时间比如30分钟再继续并且降低后续请求的并发数。我试过很多风控在低频请求下会自动解除。第三断点续爬。每次爬取任务开始前把任务的进度比如当前爬到了哪个商圈、哪个页码记录到一个JSON文件里。每次启动时先读取进度从断点处继续而不是从头开始。这个设计救了我不止一次。6.3 日志记录是不可省略的工程习惯还有一个很多人忽略的细节日志。不要只是print打印到控制台而是用Python的logging模块输出到文件并且按天切割。这样出了问题你可以回溯当时爬虫到底在做什么、请求了什么URL、收到了什么响应。import logging from logging.handlers import TimedRotatingFileHandler handler TimedRotatingFileHandler(crawler.log, whenmidnight, backupCount7) formatter logging.Formatter(%(asctime)s - %(levelname)s - %(message)s) handler.setFormatter(formatter) logger logging.getLogger(eleme_crawler) logger.addHandler(handler) logger.setLevel(logging.INFO)这套日志系统在项目初期看起来是“过度设计”但当你真的遇到“跑了一晚上第二天一看数据少了三分之一还不知道发生了什么”的场景时就会明白日志有多重要。7. 这类项目最常见的三个深坑与避坑指南每个爬虫项目都有它自己的“脾气”饿了么商家信息这个项目我踩过这三个坑在这里写出来帮后面的人少走弯路。7.1 坑一把城市坐标当成单一入口来爬一开始我做这个工具时天真地以为只要把目标城市的中心点坐标作为搜索原点设置一个足够大的搜索半径就能把这个城市所有商家一网打尽。结果发现饿了么的搜索接口对搜索半径是有限制的超过一定范围返回的数据就会变得很少甚至为空。后来我才反应过来一个城市的餐饮供给是离散分布的市中心、大学城、产业园、居民区每个区域的商家密度和类型都完全不同。如果你只用一个中心点去搜索得到的结果会有强烈的“中心偏移”完全无法代表整个城市的商家分布。正确的打法是把城市分解成若干商圈或者网格每个网格单独发起搜索最后合并结果。这一步相当于把垂直型爬虫的思路落地了——你不是在爬一个泛化的全网数据而是在一个垂直领域里做精细化的区域覆盖。7.2 坑二位置漂移导致的商家覆盖不均这是定位类爬虫最容易忽略的细节。我刚才说按网格爬但如果你直接用经纬度网格来切分城市会撞上一个新问题格子边缘的搜索区域会有重叠和空洞。而且对于郊区、工业区这种商家稀疏的区域按网格爬会导致大量空白请求浪费请求额度还可能触发风控。更聪明的做法是从“地标列表”出发。每个城市都有大量地标POI比如购物中心、写字楼、大型住宅小区你把这些地标作为搜索中心点配合合理的半径就能比较自然地把城市的主要商圈覆盖到。这样做还有一个额外的好处——爬下来数据本身就带有了“商圈”这个维度后续做分析时特别有用。7.3 坑三完全依赖接口版本的稳定性爬虫项目做久了你会有一个感觉每次平台App更新版本接口参数大概率会变。要么新增一个校验字段要么改字段名要么干脆换协议版本。针对这个问题我在工程上做了两个应对策略一是在解析层做兼容性处理。字段解析的时候不直接用dict[key]的方式取值而是用dict.get(key, default)这样就算某个字段消失了程序也不会崩溃。二是写一个冒烟测试脚本。每次爬虫启动后先请求一个已知的、固定的商家详情检查返回的字段是否完整。如果发现字段缺失立刻报警提示你可能遇到了接口升级。这样一来平台版本更新后你能第一时间感知到问题而不是等到数据入库之后才发现异常那可就晚了。7.4 爬虫的终点是数据处理和业务价值最后还想多说一句。很多人做爬虫把“能爬到数据”当成终点这是本末倒置的。爬虫只是手段数据的处理、分析和价值挖掘才是目的。当你把商家信息爬下来之后可以做很多有意思的事情分析一个地区外卖商家的品类饱和度观察一段时间内商家的评分变化判断其经营健康状况对比不同区域的配送费水平发现定价差异背后的逻辑结合天气数据研究恶劣天气对配送时间的影响这些才是那个.zip压缩包里真正值钱的东西。单纯从爬取工具的角度看它只是一段代码但从数据价值的角度看它是一个可以为许多研究提供基础数据支撑的工具箱。8. 从爬虫工具到系统思维的进阶之路做完了这个项目我最大的感悟是爬虫技术本质上是一门系统集成技术它考验的不是你会不会写某个函数而是你有没有能力把网络协议、数据解析、反爬对抗、工程架构这些东西融会贯通。这个项目里我用到和学到的技术栈足够丰富Python的requests处理HTTP会话blackboxprotobuf和手动字段还原做协议的解析Pandas做数据清洗SQLite做持久化存储logging做运行监控ThreadPoolExecutor做并发控制每一个环节拆开来看都不是特别难但要把它们有机地组合在一起形成一个稳定运行的工具却需要系统性的思考和持续的调试。如果你也在做类似的项目我的建议是不要止步于“跑通”而是追问几个问题如果平台改了协议怎么办如果要把这个工具扩大到一个省、一个国家的量级架构上需要哪些调整如果数据要支撑一个完整的数据分析报告前期的字段设计是不是足够合理这些问题想得越深你从这个项目里学到的就越多。因为对外卖平台商家信息进行合理规模的爬取与分析本身就是理解本地生活服务行业的一个很有意思的观察窗口。而用这个项目打底你以后再遇到任何复杂的爬虫需求都会有一种“哦这个我之前处理过类似的”的信心。这也是这个项目的真正价值所在。本文还有配套的精品资源点击获取

相关新闻

最新新闻

日新闻

周新闻

月新闻