news 2026/9/23 17:57:28

维基百科中文版API踩坑:手写实现稳定抓取方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
维基百科中文版API踩坑:手写实现稳定抓取方案

维基百科中文版API踩坑:手写实现稳定抓取方案

最近升级了内部数据同步服务,刚跑完测试,生产环境直接报了一堆 404 和字段缺失。检查日志发现,维基百科中文版的 MediaWiki API 在 1.40 版本后对部分批量查询接口做了不兼容变更,导致原有代码全崩。

这种“版本升级后 API 全变了”的情况,在对接开放数据源时太常见了。官方文档更新滞后,社区反馈又碎片化,这时候死磕官方 SDK 或第三方库往往解决不了根本问题。我的经验是:抛弃黑盒,手写实现核心请求逻辑

今天这篇教程,不聊虚的。我们直接面对市政公用工程中常见的“跨部门数据共享”痛点,结合运维开发视角,手把手带你手写实现一个稳定的维基百科中文版数据抓取器。别被“维基百科”这个名字吓到,这其实是一个绝佳的练习对象:它免费、开放、结构复杂,且经常变动,非常适合用来打磨你的 API 交互能力。

概念速懂:为什么选维基百科做练手

很多读者可能会问,写个爬虫有什么难的?为什么非要盯着维基百科中文版?

在实际的市政公用工程信息化项目中,我们经常需要对接政府公示数据、历史档案库或外部知识库。这些系统的特点和维基百科很像:

  1. 数据量巨大:单次请求无法获取全量,必须分页。
  2. 结构动态:字段名可能随版本迭代调整。
  3. 限流严格:IP 被封禁是常态,必须做并发控制。

维基百科中文版(zh.wikipedia.org)基于 MediaWiki 平台,其 API 遵循 RESTful 风格。对于运维开发来说,理解它的底层逻辑,比死记硬背某个 Python 库的函数更有价值。

这里有一个关键概念:Action API vs REST API

  • Action API (/w/api.php):老接口,功能全,但返回的是 JSON 包裹的复杂结构,解析麻烦。
  • REST API (/api/rest_v1/):新接口,更轻量,但覆盖范围有限。

本文我们主要使用 Action API,因为它的稳定性在长期项目中经过验证,且支持更复杂的过滤参数。这也是为什么很多老牌系统还在用它的根本原因。

环境准备:最小化依赖

为了体现“手写实现”的价值,我们尽量少用现成的高层封装库。你需要准备:

  1. Python 3.8+:推荐版本,语法特性支持更好。
  2. requests:唯一的第三方依赖,用于 HTTP 通信。
    • 安装命令:pip install requests
  3. 一个文本编辑器:VS Code 或 PyCharm 均可。

重要提示:在开始写代码前,请务必阅读维基百科的开发者文档MediaWiki API 官方指南)。特别是关于 User-Agent 的请求头要求。维基百科明确要求用户设置合法的 User-Agent,否则会被 403 拒绝。这是很多新手踩坑的第一道门槛。

核心语法:拆解请求与响应

在动手写完整代码前,我们先拆解一次典型的 API 交互。

假设我们要获取“北京市”这个条目的信息。 URL 构造如下: https://zh.wikipedia.org/w/api.php?action=query&titles=北京市&format=json&prop=extracts

参数解析:

  • action=query:指定操作类型为查询。
  • titles=北京市:查询的目标页面。
  • format=json:强制返回 JSON 格式,方便解析。
  • prop=extracts:指定返回页面的纯文本摘要。

关键点:维基百科的响应结构是嵌套的。

{"batchcomplete": "","query": {"normalized": [],"pages": {"12345": {"pageid": 12345,"title": "北京市","extract": "北京市,简称“京”,是中华人民共和国的..."}}}
}

注意 pages 是一个字典,Key 是 pageid,而不是固定的索引。很多开发者在这里犯错,试图用 pages[0] 取值,结果直接 KeyError。

完整代码示例:手写稳定抓取器

下面这段代码是我在实际项目中使用的简化版模板。它包含了重试机制、User-Agent 设置和基础的错误处理。

import requests
import time
import logging# 配置日志,方便运维排查
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)class WikipediaClient:def __init__(self):# 必须设置 User-Agent,否则会被维基百科屏蔽# 格式参考:https://meta.wikimedia.org/wiki/User-Agent_policyself.base_url = "https://zh.wikipedia.org/w/api.php"self.headers = {"User-Agent": "MyEngineeringBot/1.0 (contact@example.com) Python-requests"}self.session = requests.Session()self.session.headers.update(self.headers)def fetch_page_extract(self, title, retries=3):"""获取指定页面的纯文本摘要"""params = {"action": "query","titles": title,"format": "json","prop": "extracts","exintro": 1,  # 只取首段,减少数据量"redirects": 1 # 自动重定向}for attempt in range(retries):try:response = self.session.get(self.base_url, params=params, timeout=10)response.raise_for_status()  # 非200状态码抛异常data = response.json()# 检查是否成功if "error" in data:logger.error(f"API Error: {data['error']}")return None# 遍历 pages 字典,因为 Key 是动态的pages = data.get("query", {}).get("pages", {})for page_id, page_data in pages.items():# 检查是否是被重定向的页面if page_data.get("missing") == "":logger.warning(f"Page {title} not found.")return Nonereturn page_data.get("extract")return Noneexcept requests.exceptions.RequestException as e:logger.warning(f"Request failed (Attempt {attempt + 1}): {e}")if attempt < retries - 1:time.sleep(2 ** attempt)  # 指数退避策略continuereturn None# 测试运行
if __name__ == "__main__":client = WikipediaClient()result = client.fetch_page_extract("北京市")if result:print(result[:200])  # 打印前200个字符预览else:print("获取失败")

代码逐行解读:

  1. requests.Session():复用到维基百科的连接,比每次新建 requests.get 性能高,且能自动保持 Cookie(虽然维基百科大多无状态,但这是好习惯)。
  2. raise_for_status():很多教程忽略这一步。如果服务器返回 500,response.json() 会解析失败或返回错误结构。显式抛出异常能让我们更早发现问题。
  3. 指数退避(Exponential Backoff)time.sleep(2 ** attempt)。如果第一次失败,等1秒;第二次失败,等2秒;第三次失败,等4秒。这能有效避免在服务器压力大时持续轰炸,也是遵守网络礼仪的表现。
  4. 遍历 pages 字典:再次强调,不要假设 pages 的长度或 Key。这是处理 MediaWiki API 的核心技巧。

常见报错与避坑指南

在实际生产环境中,你大概率会遇到以下三类问题:

1. HTTP 403 Forbidden

现象:所有请求都被拒绝。 原因:User-Agent 缺失或格式不规范。 解决:严格按照维基百科的 User-Agent 策略修改。必须包含联系方式和软件名称。不要使用默认的 python-requests/x.x.x

2. KeyError: 'pages''query'

现象:代码运行到解析 JSON 时报错。 原因

  • 网络抖动导致返回了 HTML 错误页面。
  • 请求参数错误,API 返回了 Error 对象而非 Query 对象。 解决:在访问 data['query'] 之前,务必先检查 data 中是否存在 error 字段。使用 .get() 方法代替 [] 取值更安全。

3. 频率限制(429 Too Many Requests)

现象:批量抓取时突然中断。 原因:并发过高或短时间内请求过多。 解决

  • 控制并发数:建议使用 concurrent.futures.ThreadPoolExecutor,将并发线程控制在 5-10 以内。
  • 增加间隔:在循环中加入 time.sleep(0.5)
  • 注意:维基百科对 IP 的限流非常严格,尤其是数据中心 IP。如果是生产环境,建议轮换 IP 或申请正式的 Bot 权限。

小结与互动

通过上述步骤,我们手写实现了一个基于维基百科中文版 API 的数据抓取器。这个过程不仅解决了“版本升级后 API 全变了”带来的脆弱性,更重要的是,让你彻底理解了 HTTP 交互、JSON 解析和错误处理的底层逻辑。

对于市政公用工程从业者而言,这种能力可以迁移到对接住建局数据接口、环保监测数据平台等场景。核心思想不变:理解协议,掌控请求,妥善处理异常

你公司项目里是怎么处理的?欢迎评论。

比如,你们在对接外部数据源时,是倾向于封装统一的 SDK,还是像这样每次手写?或者有没有遇到过更奇葩的 API 变更?在评论区聊聊,咱们一起避坑。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 17:57:07

一文搞懂omg命令,3步搞定项目落地不踩坑

一文搞懂omg命令,3步搞定项目落地不踩坑 很多开发者刚接触新工具时,常陷入“语法背熟却跑不通项目”的困境。比如你查了资料,知道omg命令能做什么,但真到搭环境、配参数时,又卡在半路。今天这篇文章,就用一个实战小项目,带你一文搞懂omg命令从安装到落地的全流程,把“知道”变成“会做”。…

作者头像 李华
网站建设 2026/9/23 17:56:51

Thymeleaf 实战避坑指南:5 个让你加班的坑及修复方案

Thymeleaf 实战避坑指南:5 个让你加班的坑及修复方案 Thymeleaf 官方文档写得像天书,翻了三遍还是报错?别慌,这篇避坑指南专治各种“文档看哭”。 作为用了五年 Thymeleaf 的老兵,我见过太多新人被简单的模板语法搞崩溃。很多人以为 Thymeleaf 就是“在 HTML…

作者头像 李华
网站建设 2026/9/23 17:56:45

3步搞定恢复磁盘:保姆级教程与避坑指南

3步搞定恢复磁盘:保姆级教程与避坑指南 刚接手旧服务器,发现 fsck 命令报错,日志里全是 EXT4-fs error ,心里瞬间咯噔一下。更崩溃的是,之前为了适配新内核,把 e2fsprogs 版本从 1.43 升到了 1.46,结果原本熟悉的 e2fsck -y 参数行为完全变了, -f…

作者头像 李华
网站建设 2026/9/23 17:56:44

别硬背文档了!3个真实Bug教你搞定音效管理器保姆级教程

别硬背文档了!3个真实Bug教你搞定音效管理器保姆级教程 是不是对着网页上的音效列表发呆,代码跑通了但声音卡得跟卡碟似的?很多兄弟看了一堆教程还是不会写项目,总觉得逻辑很简单,一上手就报错。这篇保姆级教程不整虚的,直接带你拆解我在项目里踩过的深坑。…

作者头像 李华
网站建设 2026/9/23 17:56:43

手写实现仓库设计避坑指南:3个致命错误教你少走弯路

手写实现仓库设计避坑指南:3个致命错误教你少走弯路 配置环境就卡半天?别急着骂娘,大概率是你的仓库设计没搞对。很多新手在写代码时,喜欢直接复制粘贴网上的片段,连目录结构都没看清,结果一跑起来,依赖冲突、路径报错轮番上阵。这时候, 手写实现 一个最小可用的仓库骨架,比装十个库都管用。…

作者头像 李华