恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
前端转AI实战:用requests抓API、解析JSON与处理429限流
首页
资讯中心
/
前端转AI实战:用requests抓API、解析JSON与处理429限流
前端转AI实战:用requests抓API、解析JSON与处理429限流
发布时间:2026/10/17 21:10:47
前端转 AI 的第 7 天我终于开始碰爬虫了。今天的目标很直接用 Python 的 requests 库抓一个真实 API把数据拿到本地。以前做前端数据都是后端接口给好了、浏览器渲染好了我负责展示今天要反过来让我写的代码主动去请求别人家的服务然后把结果解析出来——这种角色互换是前端转 AI 之后第一个需要适应的地方。这篇文章记录的就是我这一天从选目标、抓包、写代码到处理限流踩坑的全过程。如果你也是前端出身想往 AI 方向走或者单纯想学 Python 爬虫这篇应该能帮你省不少时间。1. 内容整体设计与思路拆解1.1 前端转 AI为什么要先练爬虫很多前端同学转 AI第一反应是去啃模型架构、看 Transformer 论文结果没几天就被矩阵和注意力机制劝退了。我的路线不太一样先解决“数据从哪来”的问题。AI 项目里模型是引擎数据是燃料。无论是做大模型微调、做 RAG 知识库还是做数据分析第一步永远是采集数据而采集数据最基础的手段就是 HTTP 请求。说白了爬虫干的事情就是把浏览器帮你做的那套“输入网址、发请求、拿响应、渲染页面”的过程改成用代码自动执行。前端做这件事有天然优势。我们天天跟 HTTP 打交道知道 GET、POST、Header、Cookie、状态码开发者工具里的 Network 面板更是老朋友了。以前我们在 Network 里看接口是为了知道页面数据从哪个 API 来现在用爬虫只是反过来让代码去调用这个 API。知识完全平移唯一要补的是 Python 语法和 requests 这个库的用法。所以我给 Day 7 定的任务就是用 requests 打一个真实 API把返回的 JSON 结构化存下来完成一次完整的“程序主动获取数据”闭环。1.2 为什么选 requests而不是 Scrapy 或 Playwright开始之前我纠结了一下工具选型。Python 爬虫生态里requests、Scrapy、Playwright 都有大量用户在推不搞清楚差异容易学偏。我列了一个对比表最后决定今天先用 requests方案定位优点缺点适合场景requests轻量 HTTP 客户端上手快、依赖少、请求细节完全可控不提供解析、调度、去重能力调 API、中小规模采集Scrapy完整爬虫框架自带调度、去重、管道、并发控制学习成本高、框架约束多大规模网站爬取Playwright / Selenium浏览器自动化能执行页面 JS、模拟点击、处理动态渲染重、慢、内存开销大需要渲染 JS 的页面型爬虫requests 的定位就是“把 HTTP 请求这件事做到极致”。它不会帮你解析 HTML不会帮你管理代理池更不会帮你做 JS 渲染它只负责把请求发出去、把响应收回来。但今天的目标是抓真实 APIAPI 返回的是结构化 JSON不需要渲染 JSrequests 就是最直接的工具。至于 Playwright 这类浏览器自动化工具等后面真遇到“必须模拟登录、必须等页面渲染完才能拿数据”的场景再上也不迟。先学会用 requests 把 HTTP 通信这块掌握扎实后续学其他工具会快很多。2. 环境准备与前置知识2.1 Python 环境、虚拟环境与 requests 安装我的电脑是 macOS系统自带 Python 3但直接裸装依赖很容易把环境搞乱。前端那边我们有 nvm 管理 Node 版本Python 这边也有类似工具不过最轻量的方案就是先用 venv 建一个虚拟环境把依赖隔离在当前项目目录里。python3 --version mkdir day7-crawler cd day7-crawler python3 -m venv .venv source .venv/bin/activate pip install requestsWindows 上激活虚拟环境的命令不太一样是.venv\Scripts\activate这里要注意。装完后可以用pip show requests确认一下版本我实测装出来是 2.31 左右这个版本 API 已经非常稳定。关于虚拟环境多说一句千万别嫌麻烦跳过这一步。Python 的包依赖冲突比 npm 还坑不同项目共用一套全局包早晚会踩到版本互踩的雷。养成每个项目建独立环境的习惯后面调大模型 SDK、装 pandas 的时候会省心很多。2.2 用前端的 fetch 思维理解 requests前端同学学 requests 不需要从零开始因为你已经在用它的“双胞胎兄弟”了——浏览器里的 fetch。两个东西做的事情一模一样发 HTTP 请求拿响应。对比看一下就明白了。先看前端的 fetch 写法fetch(https://api.github.com/search/repositories?qpythonsortstars, { method: GET, headers: { User-Agent: Mozilla/5.0 } }) .then(res res.json()) .then(data console.log(data))再看 requests 的写法import requests resp requests.get( https://api.github.com/search/repositories, params{q: python, sort: stars}, headers{User-Agent: Mozilla/5.0} ) data resp.json() print(data)对照起来看就清晰了fetch 里的url对应requests.get()的第一个参数headers直接对应 headers 参数query string 参数在 fetch 里要手动拼在 URL 上requests 则直接给一个params字典库会自动做 URL 编码。返回结果方面fetch 要先res.json()解析requests 直接调用resp.json()也一样底层都是把 JSON 字符串反序列化成对象。点击/跳转这类浏览器行为对 requests 不存在它就是一个纯粹的函数调用。前端新手最常犯的错是拿到resp就急着resp.json()完全没看状态码。记住一个原则resp.json()只适合 JSON 响应的接口如果服务器返回 HTML 错误页调用它会直接抛异常。所以实战时我都会先看resp.status_code再决定后续动作。2.3 状态码速查表与 429 的首次接触爬虫调试里状态码就是你和服务器对话的语言。以前前端联调时也看状态码但那时主要关心 200 和 404真的开始写爬虫401、403、429 出现的频率会远超想象。我整理了这次 Day 7 用到的状态码速查表状态码含义前端常见场景爬虫遇到怎么办200请求成功正常返回正常解析数据301 / 302重定向路由跳转确认 URL 是否变更允许自动跟随或手动处理403服务器拒绝访问无权限补 User-Agent、Cookie、登录态404接口不存在路由不存在检查 URL 路径、域名429请求过于频繁限流降低频率读取 Retry-After退避重试5xx服务端错误后端异常延迟后重试检查是否服务临时不可用429 是我这次最深刻的“老朋友”。热门话题里经常看到类似于exceeded retry limit, last status: 429 too many requests的报错包括一些 AI 命令行工具也会频繁出现原因就是短时间请求次数超过了服务器限制。爬虫调 API 和大模型调用本质上没有区别都是在消费别人的服务端资源。服务器不拦你是因为你有配额你超了配额它就甩给你 429。这个意识越早建立越好。3. 抓取一个真实 API完整实操流程3.1 用浏览器开发者工具定位真实接口理论学习差不多该上手了。我选的目标是 GitHub 的公开 API。一方面它是完全公开的不需要注册就能调用测试另一方面 GitHub API 数据结构规范返回的是标准 JSON适合练手。更重要的是GitHub 有明确的速率限制说明文档未认证请求默认 60 次/小时非常方便我们后面实测 429 限流是怎么触发的。操作流程是这样的打开 GitHub 的仓库搜索页按 F12 打开开发者工具切到 Network 面板刷新页面勾选 Fetch/XHR 过滤出接口请求。这时能看到页面实际调用了哪个 API点进去可以看到 Request Headers、Query String Parameters 这些信息。这就是我们前端平时排查接口的老路径现在换成了找爬虫目标。以我这次抓的接口为例目标地址是https://api.github.com/search/repositories?qlanguage:pythonsortstarsper_page5它的含义是搜索 star 数最高的 5 个 Python 仓库。在 Network 面板里我能看到响应返回的 JSON 数组里面每个仓库对象都包含full_name、stargazers_count、language、html_url等字段——这些字段就是我们要提取的数据。确定接口没问题后下一步就是用 requests 把同样的请求“复刻”到代码里。3.2 第一版代码发送 GET 请求并解析 JSON我写的第一个脚本很简单核心就是三个动作拼参数、发请求、解析 JSON。import requests url https://api.github.com/search/repositories params { q: language:python, sort: stars, per_page: 5, } headers { User-Agent: frontend-to-ai-learning/0.1, } resp requests.get(url, paramsparams, headersheaders, timeout10) print(状态码:, resp.status_code) data resp.json() items data.get(items, []) for repo in items: print(repo[full_name], ★, repo[stargazers_count])这里有几个细节值得展开说。第一params参数由 requests 自动拼接到 URL 上它会处理空格和特殊字符的 URL 编码比如language:python会被转成language%3Apython你不用手动拼字符串。第二headers里的User-Agent必须带GitHub 对缺失 UA 或伪装成浏览器的请求识别得很严格不带头直接 403。第三timeout10是强烈建议加的否则遇到一个慢接口程序可能挂几分钟不返回排查起来非常痛苦。运行后我拿到了 5 个仓库的完整 JSON控制台打印出了full_name和 star 数。那一刻有一种很奇妙的感觉以前这些数据都是页面上别人做好的 UI 呈现给我现在它变成了我代码里的一个普通字典对象我想怎么处理就怎么处理。这就是爬虫给人带来的掌控感也是为什么前端转 AI 必须练这个能力。3.3 数据清洗与本地存储拿到 JSON 只是第一步真实项目里数据不能只停留在内存里。我这次把热门仓库信息清洗后存成了 CSV方便后续分析和喂给模型。import csv with open(top_python_repos.csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([name, owner, stars, language, url]) for repo in items: writer.writerow([ repo[full_name], repo[owner][login], repo[stargazers_count], repo.get(language, ), repo[html_url], ]) print(已保存, len(items), 条记录)这里要特意说明两个容易踩的坑。一个是newline如果漏掉Windows 下写 CSV 每行中间会多一个空行。另一个是encodingutf-8如果设置不对中文会乱码。如果你打算用 Excel 打开这个 CSV建议写成encodingutf-8-sig这样 Excel 能正确识别 UTF-8 的中文内容。数据清洗这块以前是我作为前端的盲区。页面渲染时 JSON 已经是被后端整理过的结构我只需要取字段、塞模板。但爬虫拿到的原始数据往往是脏的字段缺失、类型不对、嵌套层级混乱。比如上面的代码里我用repo.get(language, )而不是repo[language]就是因为有的仓库对象没有language字段直接用中括号取值会抛KeyError。这种“默认值兜底”的写法写爬虫时几乎天天用。4. 限流、状态码与容错处理4.1 429 限流是怎么发生的第一次跑通脚本后我顺手写了个循环想抓 30 个仓库 star 榜结果没几轮就收到一大片 429。GitHub 的速率限制机制是未认证的请求每小时最多 60 次超过之后所有请求都会返回 429。这个设计在 API 世界非常普遍本质是保护服务器资源防止某个客户端把服务拖垮。限流的实现方式通常有三种固定窗口、滑动窗口、令牌桶。前端同学可以类比理解固定窗口就是每秒最多 10 个请求多了就拒滑动窗口是每 10 秒最多 100 个过了时间窗口重新计数令牌桶更像食堂的打饭窗口——桶里事先放好一定数量的“令牌”每来一个请求取走一个令牌令牌用完了就得等厨师重新补充。GitHub 这种“每小时 60 次”就属于固定窗口思路。调试时我推荐直接看响应头里的限流信息GitHub 会返回X-RateLimit-Remaining和X-RateLimit-Reset。用代码打印出来print(剩余请求次数:, resp.headers.get(X-RateLimit-Remaining)) print(重置时间戳:, resp.headers.get(X-RateLimit-Reset))X-RateLimit-Remaining表示这小时内还能发几次X-RateLimit-Reset是 UTC 时间戳告诉你什么时候重置计数。把这两个值打出来就能非常直观地知道自己是撞了限流墙还是单纯的接口写错。很多 AI 工具报的too many requests you have exceeded...也是同一套逻辑只是服务的限额和窗口不同罢了。4.2 给爬虫加上重试与退避遇到 429 不能慌更不能无脑重试。正确的姿势是读服务器的Retry-After响应头它会直接告诉你“等多少秒后再来”。如果服务器没给这个头就自己做一个指数退避策略第一次失败等 1 秒第二次等 2 秒第三次等 4 秒逐步拉开间隔越急越容易被拉黑。我给 Day 7 的爬虫封装了一个带重试的请求函数import time import requests def fetch_with_retry(url, paramsNone, headersNone, max_retries3, timeout10): for attempt in range(max_retries): try: resp requests.get(url, paramsparams, headersheaders, timeouttimeout) except requests.RequestException as e: print(f网络异常: {e}) time.sleep(2 ** attempt) continue if resp.status_code 200: return resp.json() elif resp.status_code 429: retry_after int(resp.headers.get(Retry-After, 2 ** attempt)) print(f触发限流等待 {retry_after} 秒后重试...) time.sleep(retry_after) elif resp.status_code 500: print(f服务端错误 {resp.status_code}指数退避重试) time.sleep(2 ** attempt) else: print(f请求失败状态码 {resp.status_code}) break return None这个函数的思路是200 直接返回数据429 优先读Retry-After没有就按2 ** attempt退避5xx 服务端错误也用指数退避4xx 客户端错误则直接放弃重试因为 403、404 这类问题重试多少次都不会成功问题出在参数或目标本身。重试逻辑是爬虫的“保险丝”但别盲目依赖最终还是要靠控制请求频率来减少触发概率。4.3 控制速率与模拟真实浏览器跑爬虫最忌讳的是一口气把请求全部打出去我第一天就吃过这个亏。现在写循环抓取都会在每次请求之间加个延时让节奏更像真人操作。这里有两个常用手段import time import random # 固定间隔 time.sleep(1) # 随机间隔模拟真实操作 time.sleep(random.uniform(0.5, 1.5))随机间隔比固定间隔更好用因为固定间隔的请求特征太明显容易触发对方的风控策略。不过要注意延时不是越长越好单线程跑大型采集任务1 QPS 已经是比较保守的速率。这个速率下抓几千条数据的时间成本用数学算一下就知道每秒 1 个请求抓 3600 条要 1 小时所以大规模采集还是要靠异步、并发和分布式那是后面的事。另外请求头也别裸奔。至少带上常见的浏览器 UA 和一些基础字段HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept: application/json, Accept-Language: zh-CN,zh;q0.9, }这里必须强调合规性爬虫技术本身没问题但使用时要尊重目标服务的 robots.txt 和 API 使用条款只采集公开数据不碰账号密码、不抓个人隐私请求频率控制在合理范围。做一个有素质的爬虫工程师比写多牛的重试算法都重要。很多人的 IP 被对方封禁不是因为技术不行而是因为太贪婪。5. 前端视角的进阶玩法爬虫数据 大模型5.1 爬完数据的下一步是喂给 AI只用 requests 抓数据其实还停留在“数据搬运工”的层面。前端转 AI 的进阶玩法是把爬虫拿到的数据喂给大模型让模型帮你总结、分类、提取、生成内容。这也是我认为爬虫在 AI 工作流中最核心的价值它不只是把数据存下来而是作为 AI 的数据入口。以前端的数据思维来理解爬虫负责把散落各处的数据源汇总成一个 JSON 或 CSVAI 负责消费这个 JSON输出成人类能直接看懂的结论。这个过程相当于给数据加了一层“理解层”。比如我抓到的 GitHub top 仓库原始数据有一堆 star 数、owner、描述字段人工一个个看效率太低但把这些字段拼成一段文本塞给大模型它就能在几秒钟内生成一份推荐列表标注“哪个适合前端、哪个适合刚入门 Python”这就是一个典型的 AI 辅助数据解读场景。5.2 用 requests 调用大模型 API很多前端同学以为调用大模型一定要装 SDK其实大模型 API 本质上也是一个 HTTP 接口用 requests 就能直接调。现在主流的大模型服务基本都是 OpenAI 兼容格式只需要按照 Chat Completions 协议拼好 JSON用requests.post发出去就行。import requests API_URL https://api.your-llm-service.com/v1/chat/completions API_KEY sk-your-api-key def summarize_repos(repo_text): body { model: your-model-name, messages: [ {role: system, content: 你是一个程序员技术顾问请用中文给出简洁的推荐理由。}, {role: user, content: f下面是一些 GitHub 热门仓库请推荐其中最适合前端学习的 2 个并说明理由\n{repo_text}}, ], temperature: 0.7, } headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } resp requests.post(API_URL, jsonbody, headersheaders, timeout30) if resp.status_code 200: return resp.json()[choices][0][message][content] else: print(调用失败, resp.status_code, resp.text) return None这里有两个前端同学容易忽略的点。第一jsonbody参数会自动把 Python 字典序列化成 JSON 并设置Content-Type: application/json不需要手动json.dumps。第二大模型接口的鉴权一般是Authorization: Bearer key名字可能叫 API Key 或 Access Token本质上就是我们以前给接口传 Token 一样。把第 3 节抓到的仓库文本拼到repo_text里这个函数就能返回一段模型生成的推荐内容。至此整个链路就完整了requests 抓真实 API清洗数据再通过 requests 调用模型接口把数据变成带语义的结论。这条链路里没有用到任何前端框架但每一步用到的都是你作为前端已经熟悉的概念HTTP 请求、JSON 解析、Header 设置、鉴权。这也是我 Day 7 最大的认知刷新——AI 应用开发的前半程其实比想象中更靠近“接口调用工程师”。5.3 常见大模型与爬虫报错速查表实战一天下来我把踩到的和网上高频出现的报错整理成了速查表方便你遇到问题时直接对号入座报错特征常见状态码原因处理方式exceeded retry limit, last status: 429 too many requests429请求频率超过账号配额降速、指数退避、检查套餐/余额api error: 400 the supported api model names are ...400模型名写错或不支持检查 model 字段拼写参考接口返回的模型列表login failed. check api token...401API Key 错误检查 Key、环境变量、是否过期api error: 400 this models maximum context length is ...400输入/输出超过模型上下文长度截断文本减少 prompt 内容ConnectTimeout / ReadTimeout无网络不通或服务响应慢设置 timeout 参数增加重试requests.exceptions.JSONDecodeError200响应不是合法 JSON打印 resp.text 看实际返回内容这个表中第一个报错我印象最深因为现在很多 AI 命令行工具都会报。它本质上就是我们在 4.1 节讲的限流问题只是把场景从爬虫搬到了大模型调用。所以我在 Day 7 会刻意强调处理 429 的肌肉记忆不管你是爬别人网站还是调 AI 接口面对限流的第一反应都应该是读响应头、降频率、退避重试而不是暴力刷请求。6. 实战心得前端转 AI 第 7 天后的三个感受6.1 前端技能没有浪费都是可平移的能力这一天实操下来我最大的感受是前端转 AI不是从零开始学一门新语言而是把已有能力迁移到新的技术栈里。浏览器开发者工具的 Network 面板以前拿来查接口联调现在拿来抓包定位爬虫目标JSON 的直觉以前拿来解析后端数据结构现在拿来解析接口响应对 HTTP 状态码的敏感以前拿来排查线上 bug现在拿来写重试策略。前端这些基础越扎实转 AI 的前半程就越顺。我甚至觉得真正懂浏览器请求机制的人写爬虫比很多没碰过前端的后端同学更容易理解 SSO、Cookie、Header 衍生的问题。6.2 我踩过的 4 个坑提前帮你排掉这一天绝对不是一路绿灯踩了不少坑挑几个典型的记录下来第一次直接requests.get(url)没带任何请求头返回 403。加上一个正常的User-Agent后立刻变 200。很多站点反爬虫的第一道防线就是校验 UA这个最简单也最容易忘。写循环抓取时一次性把 30 个请求在几秒内全部打出去触发 429。后来每次循环加time.sleep(1)再看X-RateLimit-Remaining确认剩余配额问题就解决了。解析字段时想当然用了repo[language]结果有的仓库对象没有这个字段直接抛KeyError中断程序。改成repo.get(language, )后程序才稳定。存 CSV 时没指定编码用 Excel 打开中文全乱码。统一改用encodingutf-8-sig后问题解决。这些坑单个看都不难但真到自己写的时候每个都能浪费你半小时。爬虫的开发节奏就是“请求、解析、存储”不断循环任何一环疏忽都会让排查成本翻倍。6.3 后续可以这样扩展Day 7 的爬虫脚本已经跑通了最基础的链路后续可以扩展的方向很多。比如把脚本改成定时任务每天自动抓取一次数据形成自己的数据仓库或者用 pandas 替代 csv 模块做更复杂的数据清洗再进一步可以把抓到的数据自动接上大模型接口生成每日技术速报。这些都可以作为后面几十天的练习方向。爬虫本身不是终点它是我往 AI 数据链路上走的第一块敲门砖。第 7 天结束前我回头整理了一下这天的工作从选型 requests到用 Network 面板定位真实 API到写第一版抓取脚本再到处理 429 限流、封装重试、接通大模型接口——每一步用的都是我知道了很久、但从来没站在“数据生产者”角度用过的东西。前端转 AI 这件事真正的门槛不是 Python也不是爬虫而是能不能把一个“别人给的界面”变成“我能调度的数据源”。requests 只是那把钥匙从 F12 到resp.json()从 fetch 到requests.post每一个概念我都是见过的只是换了个身份去用。下一个 Day我准备把这份数据清洗能力再往上提一层开始接触结构化数据处理的库。爬虫这门手艺先到这里。