资讯动态

Python接口数据采集实战:从请求构造到异步高并发

发布时间:2026/10/25 14:20:37 来源:尧图企业网站定制
1. 接口数据采集的底层逻辑与方案选型1.1 为什么接口采集比页面解析更高效做过数据采集的人都有一个共识能走接口就别去解析页面。页面结构说变就变今天改个class名明天调个DOM层级你辛辛苦苦写的解析规则一夜之间全部报废。而接口不一样接口是前后端约定的数据通道它的返回结构相对稳定字段含义清晰拿到的就是结构化数据省去了大量清洗工作。我刚开始做数据采集的时候也是老老实实写页面解析用选择器一层层往下扒。后来有一次抓一个电商平台的商品列表页面改版了三次我的解析代码也跟着改了三次每次都要重新分析DOM结构非常痛苦。后来我打开浏览器的开发者工具切到网络面板一看发现页面上的数据其实是通过一个接口一次性返回的JSON字段整齐、分页清晰我直接请求那个接口十分钟就搞定了之前折腾一整天的事情。这就是接口采集的核心优势数据结构化、字段稳定、分页逻辑明确、采集效率高。页面解析像是在垃圾堆里翻找有用的东西而接口采集像是直接打开了仓库的门。1.2 接口采集的常见类型与适用场景接口采集并不是只有一种模式根据接口的认证方式和请求特征大致可以分为以下几类接口类型典型特征采集难度适用场景公开接口无需认证直接请求低开放数据平台、公共信息查询参数签名接口需要拼接签名参数中电商平台、社交平台Token认证接口需要携带有效Token中高用户中心、订单系统加密参数接口参数经过加密处理高金融类、风控严格的平台对于大多数日常采集需求来说前两类已经能覆盖八成以上的场景。后两类涉及更复杂的逆向分析需要结合具体的加密逻辑来还原不在本文的讨论范围内。我个人的建议是先从公开接口入手练手熟悉了整个请求-解析-存储的流程之后再逐步接触需要认证的接口。不要一上来就挑战最难的容易打击信心。1.3 工具链选型用什么发请求、用什么解析工具选型这件事没有绝对的最优解关键看你的使用场景和熟悉程度。我把我用过的几套方案列出来供你参考请求库方面requests是最常用的选择API简洁文档丰富社区活跃。如果你需要异步高并发httpx和aiohttp是更好的选择。httpx的API和requests几乎一致迁移成本很低同时支持同步和异步两种模式我个人现在更倾向于用它。解析方面接口返回的通常是JSON直接用Python内置的json模块或者requests的.json()方法就够了。如果返回的是XML格式可以用lxml或者xmltodict。极少数情况下接口返回的是HTML片段那就需要用到BeautifulSoup或parsel。存储方面小规模数据用CSV或JSON文件就够了中等规模用SQLite大规模上MySQL或PostgreSQL。如果要做全文检索Elasticsearch是不二之选。提示不要过度设计。我见过很多新手一上来就搭分布式、上消息队列结果数据量才几千条。先用最简单的方案跑通全流程遇到瓶颈再优化。2. 接口分析与请求构造的核心细节2.1 如何从浏览器中定位目标接口定位接口是整件事的第一步也是最关键的一步。很多人卡在这一步不知道该看什么、该找什么。我分享一下我的标准操作流程打开目标页面按下F12打开开发者工具切换到Network网络面板勾选Fetch/XHR过滤器这样只显示接口请求过滤掉图片、CSS、JS等静态资源刷新页面观察有哪些请求发出逐个点击请求查看Response响应内容找到包含目标数据的那个请求这里有个小技巧如果你不确定哪个接口是目标接口可以先清空网络面板然后在页面上触发一次数据加载动作比如点击下一页、切换分类这时候新出现的请求大概率就是你要找的接口。还有一种更高效的方法在Network面板的搜索框里直接搜索你想要的数据字段名。比如你想采集商品价格就搜price浏览器会帮你筛选出所有响应中包含这个关键词的请求。2.2 请求头、参数与Cookie的完整拆解找到目标接口之后接下来要做的就是把这个请求完整地复制下来。在请求上右键选择Copy as cURL然后粘贴到终端里你就能看到这个请求的全部细节。一个典型的接口请求包含以下几个部分请求URL接口的地址通常由基础路径和查询参数组成。注意区分哪些参数是必须的哪些是可选的。请求方法GET还是POST。GET请求参数在URL里POST请求参数在请求体中。请求头Headers这是最容易被忽视但也最重要的部分。常见的需要关注的请求头包括User-Agent标识客户端类型有些接口会校验这个字段Referer来源页面部分接口会检查请求来源Content-TypePOST请求的数据格式通常是application/json或application/x-www-form-urlencodedAuthorization认证信息可能是Bearer Token或自定义的认证头Cookie会话信息很多接口依赖Cookie来识别用户身份查询参数Query ParametersGET请求的参数通常包括分页参数page、pageSize、筛选条件category、sort、时间范围startTime、endTime等。请求体Request BodyPOST请求的数据格式可能是JSON、表单或其它自定义格式。我的习惯是先用cURL把请求跑通确认能拿到数据然后再把cURL转换成Python代码。这样可以把请求是否能通和代码是否正确这两个问题分开排查效率高很多。2.3 参数规律分析与批量构造策略单个请求跑通之后下一步就是分析参数的规律构造批量请求。这里面的核心工作是搞清楚哪些参数是固定的、哪些参数是变化的、变化的参数遵循什么规律。以分页参数为例常见的分页模式有页码分页page1,2,3...配合pageSize控制每页数量偏移量分页offset0,20,40...配合limit控制每页数量游标分页cursorxxxxx每次请求返回下一个游标值前两种很好处理直接循环构造就行。游标分页稍微麻烦一点需要从上一次响应中提取游标值再拼接到下一次请求中。除了分页参数还有一些参数需要特别注意时间戳参数有些接口会带一个_t或timestamp参数通常是当前时间的毫秒数。这种参数一般不影响数据返回但有些接口会校验时间戳的合理性。签名参数形如sign、_signature的参数通常是将其他参数按特定规则拼接后经过哈希计算得到的。这种需要逆向分析签名算法。随机数参数形如nonce、random的参数一般用于防止重放攻击可以用随机数生成。注意分析参数规律的时候建议多抓几个请求做对比。把相同参数和不同参数分别标出来变化的参数就是你需要动态构造的。3. 完整实操流程与代码实现3.1 环境准备与依赖安装在开始写代码之前先把环境搭好。我习惯用虚拟环境来管理依赖避免不同项目之间的库版本冲突。python -m venv venv source venv/bin/activate # Windows下用 venv\Scripts\activate pip install requests httpx pandas如果你需要异步采集把requests换成httpx就行。pandas是用来做数据存储和导出的非常方便。3.2 单次请求的完整实现先从一个最简单的单次请求开始把整个流程跑通import requests import json url https://example.com/api/list headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://example.com/, Accept: application/json, text/plain, */*, } params { page: 1, pageSize: 20, category: all, } response requests.get(url, headersheaders, paramsparams, timeout10) response.raise_for_status() data response.json() print(json.dumps(data, ensure_asciiFalse, indent2))这段代码做了几件事构造请求头和参数、发送GET请求、检查响应状态码、解析JSON响应。timeout10是设置超时时间防止请求卡死。raise_for_status()会在状态码非200时抛出异常方便及时发现问题。3.3 分页循环与数据去重单次请求跑通之后加上分页循环import time import requests all_items [] seen_ids set() for page in range(1, 51): params {page: page, pageSize: 20} try: resp requests.get(url, headersheaders, paramsparams, timeout10) resp.raise_for_status() result resp.json() except Exception as e: print(f第{page}页请求失败: {e}) continue items result.get(data, {}).get(list, []) if not items: print(f第{page}页无数据停止采集) break for item in items: item_id item.get(id) if item_id and item_id not in seen_ids: seen_ids.add(item_id) all_items.append(item) print(f第{page}页采集完成累计{len(all_items)}条) time.sleep(1) print(f采集结束共获取{len(all_items)}条数据)这里有几个关键点用set做去重防止不同页之间有重复数据每页之间加延时避免请求过于频繁异常处理单页失败不影响整体流程空数据判断当接口返回空列表时自动停止。3.4 数据存储与导出采集到的数据最终要落地。我一般先用pandas整理然后导出成CSV或Excelimport pandas as pd df pd.DataFrame(all_items) df.to_csv(output.csv, indexFalse, encodingutf-8-sig) print(f已导出{len(df)}条数据到output.csv)encodingutf-8-sig是为了让Excel打开CSV时中文不乱码这个坑我踩过好几次。如果你需要存到数据库用df.to_sql()一行就能搞定import sqlite3 conn sqlite3.connect(data.db) df.to_sql(items, conn, if_existsappend, indexFalse) conn.close()3.5 异步高并发采集方案当数据量比较大、单线程采集太慢的时候可以考虑异步方案。用httpx的异步客户端配合asyncioimport asyncio import httpx async def fetch_page(client, page): params {page: page, pageSize: 20} try: resp await client.get(url, headersheaders, paramsparams, timeout10) resp.raise_for_status() return resp.json().get(data, {}).get(list, []) except Exception as e: print(f第{page}页失败: {e}) return [] async def main(): async with httpx.AsyncClient() as client: tasks [fetch_page(client, p) for p in range(1, 51)] results await asyncio.gather(*tasks) all_items [item for page_items in results for item in page_items] print(f共采集{len(all_items)}条) asyncio.run(main())异步方案的速度提升非常明显但要注意控制并发数。我一般用asyncio.Semaphore来限制同时进行的请求数量避免给目标服务器造成过大压力也避免被风控系统盯上。提示并发数不是越高越好。我实测下来对于大多数中小型接口并发控制在5到10之间比较稳妥。超过这个数失败率会明显上升。4. 常见问题排查与实战避坑指南4.1 请求返回403/401的排查思路403和401是最常见的两个错误码。401通常是认证问题403通常是权限或风控问题。排查顺序如下检查请求头是否完整特别是User-Agent、Referer、Cookie这几个字段。我遇到过很多次加上正确的Referer之后403就消失了。检查Cookie是否过期有些接口依赖登录态Cookie过期后就会返回401。重新登录获取新的Cookie即可。检查请求频率短时间内大量请求容易触发风控。降低频率、增加延时通常能解决。检查IP是否被限制如果以上都没问题可能是IP被临时限制了。这种情况需要等待一段时间再试。4.2 返回数据为空或字段缺失的处理有时候请求返回200但数据是空的或者字段和你预期的不一样。常见原因有参数不正确比如分页参数从0开始而不是从1开始或者参数名拼写错误接口版本变化接口可能已经升级字段名或结构发生了变化数据本身为空该条件下确实没有数据我的做法是先把单个请求的完整响应打印出来仔细核对字段名和数据结构。不要凭猜测写代码一切以实际返回为准。4.3 接口参数加密的应对策略遇到参数加密的接口处理起来确实比较麻烦。基本的思路是在开发者工具中找到发起请求的JS代码分析加密函数的逻辑用Python复现加密算法或者直接调用JS引擎执行对于简单的哈希加密如MD5、SHA用Python的hashlib就能复现。对于复杂的加密逻辑可以考虑用PyExecJS或js2py来直接执行JS代码。注意逆向分析需要一定的JS基础新手建议先从无加密的接口练起循序渐进。4.4 常见问题速查表问题现象可能原因解决方案403 Forbidden请求头不完整、频率过高补全Headers、降低频率401 Unauthorized认证信息缺失或过期更新Token或Cookie返回空数据参数错误、接口变更核对参数、检查接口文档返回HTML而非JSON被重定向到验证页面检查Cookie、降低频率连接超时网络问题、目标服务器限制增加超时时间、重试数据重复分页逻辑问题用唯一ID去重中文乱码编码问题设置正确的encoding4.5 采集频率控制与稳定性保障最后聊一下采集频率的问题。很多人只关心能不能拿到数据忽略了能不能稳定地拿到数据。我的经验是保守设置延时宁可慢一点也不要因为频率过高被封。我一般设置每请求间隔1到3秒。加入重试机制对于偶发的网络错误自动重试2到3次每次重试间隔递增。记录失败日志把失败的请求记录下来方便后续补采。定期检查数据质量采集完成后检查数据条数、字段完整性及时发现异常。from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min2, max10)) def fetch_with_retry(url, headers, params): resp requests.get(url, headersheaders, paramsparams, timeout10) resp.raise_for_status() return resp.json()用tenacity这个库可以很方便地实现重试逻辑wait_exponential让每次重试的间隔按指数增长避免密集重试。我在实际项目中最大的体会是接口采集的难点不在于写代码而在于分析接口和理解参数规律。代码本身可能只有几十行但前期的分析工作往往要花掉大半时间。把分析做扎实了后面的编码就是水到渠成的事情。另外一定要养成先跑通单个请求再批量采集的习惯不要一上来就写循环出了问题很难定位。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑