当前位置: 首页 > news >正文

Python爬虫实战:极客实战 - 全自动化构建 GraphQL/REST API 结构化字典!

㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~
㊙️本期爬虫难度指数:⭐⭐⭐ (进阶)
🉐福利:一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:

      • 🌟 开篇语
      • 0️⃣ 前言(Preface)
      • 1️⃣ 摘要(Abstract)
      • 2️⃣ 背景与需求(Why)
      • 3️⃣ 合规与注意事项(必写,老司机的叮嘱 🛡️)
      • 4️⃣ 技术选型与整体流程(What/How)
      • 5️⃣ 环境准备与依赖安装(可复现)
      • 6️⃣ 核心实现:请求层(Fetcher)
      • 7️⃣ 核心实现:解析层(Parser)
      • 8️⃣ 数据存储与导出(Storage)
      • 9️⃣ 运行方式与结果展示(必写)
      • 🔟 常见问题与排错(排雷避坑指南 💣)
      • 1️⃣1️⃣ 进阶优化(可选但加分 ⭐)
      • 1️⃣2️⃣ 总结与延伸阅读
      • 🌟 文末
        • ✅ 专栏持续更新中|建议收藏 + 订阅
        • ✅ 互动征集
        • ✅ 免责声明

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟

我长期专注Python 爬虫工程化实战,主理专栏 《Python爬虫实战》:从采集策略反爬对抗,从数据清洗分布式调度,持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”,让数据价值真正做到——抓得到、洗得净、用得上

📌专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣专栏推广时间:如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。

💕订阅后更新会优先推送,按目录学习更高效💯~

0️⃣ 前言(Preface)

日常对接第三方 API,文档翻到眼花?今天我带你用 Python (requests+BeautifulSoup) 撸一个全自动的 API 文档抓取器,最终产出一份包含所有接口和字段定义的规整 CSV 文件。
读完这篇你将获得:

  • 掌握“左侧导航树 → 右侧详情页表格”的经典文档类爬虫模式。
  • 学会一套专治“表格嵌套”、“不规则 DOM”的高容错解析技巧。
  • 白嫖一份可以直接帮你构建本地 API 知识库的优质源码。

1️⃣ 摘要(Abstract)

本文以纯静态/SSR(服务端渲染)抓取技术为主干,详细演示如何遍历 API 文档的左侧目录树,并深入详情页提取类型名、字段名、接口名等核心数据,最终落地为结构化的 CSV 数据集。读完即可轻松应对大部分类似 Docusaurus / MkDocs 搭建的开发者文档网站。

2️⃣ 背景与需求(Why)

为什么要爬 API 文档?
很多优秀的开放平台(比如 Stripe、GitHub API)文档非常长。把这些文档爬下来并结构化,一来可以做离线信息聚合与快速检索,二来可以作为自动化代码生成或者喂给Copilot/大语言模型的极佳行业语料。

目标与字段清单:
咱们要从文档左侧导航树(Left Tree)抓到所有的页面链接,然后进入详情页抽取如下字段:

  • Type Name (类型名):如User,PaymentIntent(特别是 GraphQL 中非常重要)。
  • Field Name (字段名):如id,created_at,email
  • Endpoint Name (接口名):如GET /v1/usersQuery.getUser
  • Description (说明):字段或接口的具体功能描述。
  • Doc URL (链接):精准定位到该字段的原始文档链接。

3️⃣ 合规与注意事项(必写,老司机的叮嘱 🛡️)

技术再嗨,也得守规矩。爬取开发者文档时请牢记:

  1. 遵循robots.txt:开发者中心一般很开放,但如果明确写了Disallow: /api-docs/,咱们就得评估风险。
  2. 优雅的并发控制:API 文档站往往托管在 Vercel、Netlify 或 GitHub Pages 上。不要做攻击式并发,控制好频率(QPS < 5),别把同行的服务器打出告警。
  3. 不碰越权数据:我们只采集公开的 API Reference,绝对不要试图绕过需要携带私人 Token 才能查看的内测 API 页面(中立的技术研究除外)。

4️⃣ 技术选型与整体流程(What/How)

现代 API 文档分两种:一种是纯前端渲染的 SPA(如 Swagger UI),另一种是带服务端渲染的(如 Docusaurus、Next.js)。
这篇实战咱们按包含 HTML 结构的静态/SSR 页面来讲解。这不仅是爬虫的基础,也能让你真正学到 DOM 树遍历的精髓。

技术栈:requests(负责网络) +BeautifulSoup(负责解析)。稳定、直观、指哪打哪!

整体流程图(Workflow):

[ 采集 Fetcher ] ➡️ 访问 API 文档根目录 (Root URL) ⬇️ [ 解析 Parser ] ➡️ 抽取左侧树 <aside> / <nav> 中的所有文档分类及超链接 ⬇️ [ 采集 Fetcher ] ➡️ 遍历抓取每一个 API 详情页 HTML ⬇️ [ 解析 Parser ] ➡️ 定位详情页中的 <table> 或 <dl>,拆解 接口名 / 类型名 / 字段名 ⬇️ [ 清洗 Cleaner ] ➡️ 剔除 HTML 标签、处理多余空格和换行 ⬇️ [ 存储 Storage ] ➡️ 去重并落地到 api_dictionary.csv

5️⃣ 环境准备与依赖安装(可复现)

动手前,花 1 分钟把环境搭好:

  • Python 版本:坚决拥抱 3.8+。

  • 核心依赖安装

    pipinstallrequests beautifulsoup4 lxml

    (这里引入lxml是因为它的解析速度比 bs4 自带的 html.parser 快得多,处理包含大量 Table 的文档有奇效)

  • 项目结构

    api_doc_scraper/ ├── data/ │ └── api_dictionary.csv # 输出结果 (English filename) └── api_spider.py # 核心脚本 (English filename)

6️⃣ 核心实现:请求层(Fetcher)

开发者文档网站经常会套一层 Cloudflare。如果用裸请求,很容易喜提403 Forbidden
所以我们的Session必须要精雕细琢,加上极其仿真的 Headers 和 Retry 机制。

importrequestsfromrequests.adaptersimportHTTPAdapterfromurllib3.util.retryimportRetryimporttimedefcreate_robust_session():"""创建一个扛造的 Session 对象"""session=requests.Session()# 伪装得像个真实的开发者浏览器headers={'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8','Accept-Language':'en-US,en;q=0.9','Cache-Control':'no-cache',}session.headers.update(headers)# 失败退避重试机制 (遇到 429 限流和 5xx 错误自动重试)retries=Retry(total=4,backoff_factor=2,# 2s, 4s, 8s...status_forcelist=[429,500,502,503,504])session.mount('http://',HTTPAdapter(max_retries=retries))session.mount('https://',HTTPAdapter(max_retries=retries))returnsessiondeffetch_page(session,url):"""带限频的通用抓取函数"""try:time.sleep(1)# 文明爬取,每次请求休息 1 秒print(f"🔄 Fetching:{url}")resp=session.get(url,timeout=15)resp.raise_for_status()returnresp.textexceptExceptionase:print(f"❌ Error fetching{url}:{str(e)}")returnNone

7️⃣ 核心实现:解析层(Parser)

这是本文最核心、最值钱的部分!API 文档的痛点在于:有些页面讲的是 REST 接口,有些页面讲的是 GraphQL Type 定义。我们需要写一套兼容性高的解析逻辑。

frombs4importBeautifulSoupfromurllib.parseimporturljoindefparse_left_tree(html,base_url):"""从左侧导航树提取所有 API 详情页面的链接"""soup=BeautifulSoup(html,'lxml')# 用 lxml 加速links=[]# 假设左侧导航栏的 DOM 结构是一个 <nav class="sidebar"> 或 <aside># 注意:这里的 CSS 选择器需要根据目标网站实际情况调整!sidebar=soup.select_one('nav.sidebar, aside, .menu-content')ifnotsidebar:returnlinksfora_taginsidebar.select('a'):href=a_tag.get('href')ifhrefandnothref.startswith('#'):# 排除页内锚点full_url=urljoin(base_url,href)links.append(full_url)# 去重并保持顺序returnlist(dict.fromkeys(links))defparse_api_detail(html,url):"""解析详情页:抽取接口名、类型名、字段和说明"""soup=BeautifulSoup(html,'lxml')results=[]# 1. 抓取页面大标题 (通常是 Endpoint 或 Type Name)page_title=soup.select_one('h1')endpoint_or_type=page_title.get_text(strip=True)ifpage_titleelse"Unknown Type/Endpoint"# 区分这到底是接口(REST)还是类型(GraphQL)# 简单的 heuristic (启发式) 判断is_endpoint='GET 'inendpoint_or_typeor'POST 'inendpoint_or_typeor'/'inendpoint_or_type type_name="N/A"ifis_endpointelseendpoint_or_type endpoint_name=endpoint_or_typeifis_endpointelse"N/A"# 2. 核心:解析参数/字段表格 <table> (API文档通常用表格来列举字段)tables=soup.select('table')fortableintables:rows=table.select('tr')# 跳过表头forrowinrows[1:]:cols=row.select('td')iflen(cols)>=2:# 假设第一列是字段名,最后一列是描述说明field_name=cols[0].get_text(strip=True)description=cols[-1].get_text(strip=True).replace('\n',' ')results.append({'Type Name':type_name,'Field Name':field_name,'Endpoint Name':endpoint_name,'Description':description,'Doc URL':f"{url}#{field_name}"# 伪造一个精确到字段的锚点链接})# 3. 容错:如果页面没有表格,尝试抓取段落作为整个 API 的摘要ifnotresults:summary_p=soup.select_one('article p, .content p')summary=summary_p.get_text(strip=True)ifsummary_pelse"No description available"results.append({'Type Name':type_name,'Field Name':'N/A','Endpoint Name':endpoint_name,'Description':summary,'Doc URL':url})returnresults

8️⃣ 数据存储与导出(Storage)

把所有字典数据存进一个 CSV 文件里。为了避免程序中途崩溃导致数据全丢,建议采用“追加模式(Append)”写入,并且根据Doc URL结合Field Name进行哈希去重。

importcsvimportosdefsave_to_csv(data_list,filename="data/api_dictionary.csv"):ifnotdata_list:returnos.makedirs(os.path.dirname(filename),exist_ok=True)headers=['Type Name','Field Name','Endpoint Name','Description','Doc URL']file_exists=os.path.isfile(filename)# 使用 csv.DictWriter 落地withopen(filename,mode='a',encoding='utf-8-sig',newline='')asf:writer=csv.DictWriter(f,fieldnames=headers)ifnotfile_exists:writer.writeheader()writer.writerows(data_list)

9️⃣ 运行方式与结果展示(必写)

整合以上代码,在我们的api_spider.py底部加上入口逻辑。

如何启动:
在终端执行:python api_spider.py

# --- 主控逻辑入口 ---if__name__=="__main__":BASE_URL="https://docs.example-api.com"# 替换为你真实要爬的 API 网站START_URL=f"{BASE_URL}/reference"print("🚀 启动 API 文档抓取器...")session=create_robust_session()# 演示逻辑 (为防意外请求请替换真实地址后解开注释)""" # 1. 获取主页,解析左侧树 html_index = fetch_page(session, START_URL) if html_index: doc_links = parse_left_tree(html_index, BASE_URL) print(f"🎯 共发现 {len(doc_links)} 个详情页链接。") # 2. 遍历详情页 for idx, link in enumerate(doc_links): print(f"[{idx+1}/{len(doc_links)}] 解析: {link}") detail_html = fetch_page(session, link) if detail_html: fields_data = parse_api_detail(detail_html, link) # 3. 边爬边存 save_to_csv(fields_data) print("🎉 爬取完毕!请检查 data/api_dictionary.csv") """

展示 3–5 行示例结果(English Default for Display):

Type NameField NameEndpoint NameDescriptionDoc URL
UseridN/AUnique identifier for the object..../user#id
UseremailN/AThe user’s email address..../user#email
N/AlimitGET /v1/usersA limit on the number of objects to be returned..../get-users#limit
N/AN/APOST /v1/authAuthenticates a user and returns a JWT token..../post-auth

🔟 常见问题与排错(排雷避坑指南 💣)

爬 API 文档绝对不是一帆风顺的,下面是我为你准备的“救心丸”:

  1. HTML 抓到空壳怎么办?(SPA 页面)
    很多 API 工具(如 Swagger UI / Redoc)是动态渲染的。如果你用requests抓下来发现全是<script>标签没有正文。
    解决绝招:按 F12 抓包!API 文档往往会在后台请求一个openapi.jsonswagger.json。直接去请求那个 JSON 接口,连解析 HTML 的功夫都省了,简直是降维打击!
  2. 解析报错,取不到值?
    不同页面的 DOM 结构可能极其不稳定(比如有些用<table>,有些用<div class="row">)。
    解决绝招:使用 CSS 选择器时尽量宽泛一点,并在代码里多加try...except捕捉AttributeError。遇到异常不要崩,记录到 log 里跳过当前行。
  3. 被 Cloudflare 拦截(403 报错)?
    如果加上了完美的 Headers 还是被 403 墙掉。
    解决绝招:放弃requests,直接上curl_cffi库(它可以完美模拟浏览器的 TLS 指纹),或者使用Playwright走无头浏览器路线。

1️⃣1️⃣ 进阶优化(可选但加分 ⭐)

如果你想把这个爬虫包装成一个更专业的工具,可以从以下方向卷一卷:

  • 并发提速:使用asyncio+aiohttp。把所有详情页的 URL 放进异步队列里跑,几百个 API 详情页 10 秒钟就能扒完。
  • Markdown 还原:相比于直接存 CSV,你其实可以把抓到的字段组装回.md格式的文件,然后丢进 Obsidian 里做个人知识库。
  • 断点续跑:在本地维护一个visited_urls.txt,一旦程序中断,下次启动时读取这个集合,只爬取没有抓过的 URL。

1️⃣2️⃣ 总结与延伸阅读

呼~ 爽快!我们一起走完了一个 API 结构化爬虫的生命周期。从伪装请求头突破防线,到解析不规则的左右树状 HTML,最后落地出干净的 CSV。这份代码稍加修改,足以应付市面上多数静态开发者文档!👏

下一步可以做什么?
我极度推荐你去了解一下OpenAPI Specification (OAS)。很多时候,与其费劲爬网页,不如去寻找文档站点隐藏的那个大 JSON 文件(通常叫schema.json)。懂得了如何直接解析 OAS 规范,你的 API 数据抓取功力将直接跨入架构师级别!

🌟 文末

好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持!❤️🔥

✅ 专栏持续更新中|建议收藏 + 订阅

墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新,争取让每一期内容都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣想系统提升的小伙伴:强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 “谁使用,谁负责” 。如不同意,请立即停止使用并删除本项目。!!!
http://www.jsqmd.com/news/907407/

相关文章:

  • 别再折腾Docker了!Ubuntu 22.04上源码编译ZLMediaKit保姆级教程(含libsrtp/openssl避坑指南)
  • Midjourney Remix mode保姆级教程:手把手教你修改提示词,让AI更懂你
  • 脉冲神经网络与二进制权重的能效优化技术
  • UE4半透明材质性能优化全指南:从Surface模式选择到RTGI参数调优
  • 千问大模型在阿里生态中的核心应用场景与落地价值
  • 告别‘一大片爆红’:手把手教你用CMake-GUI无错配置VTK(Windows/VS2022版)
  • 避坑指南:DataSophon部署中那些官方文档没细说的坑(防火墙、MySQL、Nginx配置)
  • 模型迁移的“翻译官”——AMCT异构计算管理实战与自定义算子解决方案
  • 形式化验证赋能可解释AI:ViTaX框架如何保证解释的鲁棒性与必要性
  • 【评测】CSDN大模型热点洞察创作流程与评测
  • QiLink 项目的发起人徐玉生孤岛筑塔与温柔渗透
  • [智能体-106]:在相同的输入的情况下,每次调用,大模型具有相同的输出或具有不同的输出的原理?
  • 别再自己造轮子了!盘点那些能直接提升UniApp开发效率的34个原生插件
  • Vue+Element UI项目里,Table数据刷新后展开状态丢失?教你用expand-row-keys动态恢复
  • 【OpenClaw篇】OpenClaw 实战入门:在 VMware 虚拟机里部署第一个本地 AI Agent
  • BarTender 2022 Print Portal安装踩坑实录:从‘无法访问localhost’到成功部署的完整排错
  • 如何3分钟搞定QQ空间数据备份:GetQzonehistory终极指南 [特殊字符]
  • PCA降维后数据还能‘还原’吗?用Python实战带你理解信息损失与重构误差(附避坑指南)
  • 生成式AI重塑网络安全攻防:开发者如何构建AI增强型防御体系
  • 告别繁琐组态:用SVG+JavaScript手搓一个可复用的HMI仪表盘组件
  • 第4章:寄生虫时代——当AI学会呼吸
  • FlashAttention训练反向传播:梯度是怎么传回来的?
  • SAP推出AI智能体中枢,统一管理企业多厂商智能体
  • Axure RP安装(已汉化)附下载地址
  • 用DeepXDE搞定薛定谔方程:一个Python物理信息神经网络(PINN)实战教程
  • PyEcharts常用图
  • Mermaid Live Editor:免费在线图表编辑器的终极解决方案,轻松创建专业图表
  • 别再为layui上传进度条发愁了!手把手教你用layer弹窗实现文件上传进度可视化(附完整PHP后端代码)
  • 宽频抗干扰更稳定:鼎讯信通 ZN‑061A 手持式信号综合分析仪应用
  • 为什么92%的团队用Sora 2做不出可用元宇宙资产?揭秘3层隐性技术门槛与2024Q2最新破解方案