微信文章转存API接入要点:从请求构造到正文与图片的工程化处理
在实际业务中,经常需要把微信公众号文章转为可复用、可检索、可二次渲染的内容。无论是做知识库归档、离线阅读,还是为内部编辑器提供素材,手动复制粘贴往往丢失排版和图片,效率也很低。微信文章转存 API 提供了一种程序化方案:输入文章链接,返回 Markdown/纯文本正文、图片资源列表和基础元信息。本文不讨论商业价值,只从接口使用角度,说明如何正确接入这个能力。
适用场景
先从使用场景出发,判断这个接口是否适合你的项目。
- 内容归档与知识库建设:把公众号文章转为 Markdown 后存入 Git 仓库或文档系统,保留标题、作者、公众号名、发布时间,便于全文检索。
- 离线阅读与转存:将正文和图片批量下载到本地,生成离线可读的 HTML 或 PDF。
- 内容迁移与数据清洗:从公众号迁移到自有平台时,需要统一格式;或者需要从多篇文章中提取正文做 NLP 预处理。
- 监控与通知:定时扫描某个公众号的更新,发现新文章后触发后续流程,接口返回的
publish_time可用于判断文章时效。
这些场景的共同点是需要“结构化”而非“截图式”的文章数据。接口直接输出 Markdown 和纯文本,省去了自己解析 HTML 的工作。
接口能力边界
在使用前要明确接口能做什么、不能做什么。根据接口文档:
- 输入:微信公众号文章链接(
mp.weixin.qq.com/s/...格式)。 - 输出:Markdown/纯文本内容、图片资源列表、文章元信息(标题、作者、公众号名称、发布时间)。
- 额外能力:下载正文中的图片资源,每个图片对象包含 URL 和大小(字节数)。
不承诺的能力(以文档为准):
- 不保证所有公众号文章都能成功抓取,部分文章可能因访问限制或反爬策略而失败。
- 不提供 PDF 转换、评论抓取或阅读量/点赞量统计;响应中
read_num、like_num可能为null。 - 调用次数限制、并发限制等无公开承诺,只能确认单接口 QPS 为 1/s,即每秒最多请求一次。
接口的限速是工程设计中必须考虑的因素。如果业务需要批量处理,不能直接 for 循环并发请求,必须做限流。
请求参数与鉴权
接口地址:
POST https://v1.apizero.cn/api/wechat-archive请求体为 JSON 对象,字段定义如下:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | 是 | 微信公众号文章链接,例如https://mp.weixin.qq.com/s/hy31xZK6FH3H51qh1zeSKA |
format | string | 否 | 输出格式:markdown/text/both,默认按文档实现 |
timeout | number | 否 | 超时秒数,示例为20 |
鉴权通过 Header 传递。事实卡中标注的 Header 参数为Authorization,而接口文档给出的 curl 示例使用的是X-API-Key。两者在实际调用中可能存在版本差异,建议以文档页为准,并在代码中做成可配置项,方便同时支持两种 header 名。
curl 接入示例
下面是一个完整的 curl 调用,使用接口文档中的鉴权方式:
curl -sS \ -X POST \ -H "X-API-Key: $APIZERO_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://mp.weixin.qq.com/s/hy31xZK6FH3H51qh1zeSKA", "format": "both", "timeout": "20"}' \ "https://v1.apizero.cn/api/wechat-archive"注意:$APIZERO_API_KEY需要替换为你自己的密钥。如果你使用的网关版本要求Authorization: Bearer <token>,则把X-API-Key行替换为对应的 header。
返回示例(成功时):
{ "code": 0, "data": { "content": { "markdown": "# 文章标题\n\n正文...", "text": "文章标题\n\n正文..." }, "images": [ { "size_bytes": 45000, "url": "https://mmbiz.qpic.cn/..." } ], "meta": { "account_name": "公众号名", "author": "作者名", "like_num": null, "publish_time": "2026-05-01T10:00:00+08:00", "read_num": null, "title": "GitHub史上最快破10万星项目来了" } }, "msg": "成功", "request_id": "req_abc123" }响应字段解读
响应最外层是标准信封结构:code、msg、request_id和data。其中request_id是请求的唯一标识,排查问题时应记录下来。
data内部分为三块:
content
markdown:Markdown 格式正文,适合直接存储到文档型数据库中。text:纯文本正文,适合全文索引(如 Elasticsearch、SQLite FTS)。
注意:当请求format只指定一种格式时,另一个字段可能不存在或为空,代码要做好空值处理。
images
数组,每个元素包含:
url:图片的绝对地址,通常是mmbiz.qpic.cn域名。size_bytes:图片大小(字节)。可用于下载前判断资源是否过大。
这里的图片列表是正文中引用的图片资源,需要自己发起下载。下载时建议携带合适的 User-Agent,并设置超时和重试机制。
meta
文章元信息:
title:文章标题。author:作者名。account_name:公众号名称。publish_time:发布时间,ISO 8601 格式,带时区偏移(如+08:00)。read_num/like_num:阅读数和点赞数,当前可能为null,不能假设一定返回数字。
常见错误处理
以下错误是接入中较常遇到的,处理策略如下:
1. 401 / 403 鉴权失败
- 检查 API Key 是否正确、是否过期。
- 确认 header 名称是
X-API-Key还是Authorization,以文档页为准;如果两个都可能,先用 curl 手动验证。
2. 400 参数错误
url必须是完整的https://mp.weixin.qq.com/...链接,不能只给文章 ID。format取值范围限制在markdown、text、both,传其他值应视为参数错误。timeout是数字类型,示例中为字符串"20"只是 JSON 序列化示例,实际应传数字20或按文档要求处理。
3. 429 限流
接口 QPS 为 1/s,超过后可能返回限流错误。应对策略:
- 同一文章链接避免在短时间内重复调用。
- 批量任务使用队列,设置至少 1.2 秒的请求间隔。
- 对限流错误做指数退避重试,但不能无休止重试。
4. 5xx 或网络超时
- 微信文章抓取依赖目标站点可用性,偶尔会有波动。
timeout参数控制的是接口内部抓取超时,不是 HTTP 客户端超时;HTTP 层也应设置自己的超时(如 30 秒)。- 对于失败任务,建议把
request_id记录到日志,便于向服务方反馈。
工程化注意事项
1. 所有配置外部化
API Key、接口地址、超时时间、最大重试次数不要硬编码,放在环境变量或配置中心。示例:
import os import requests API_URL = os.getenv("WECHAT_ARCHIVE_API_URL", "https://v1.apizero.cn/api/wechat-archive") API_KEY = os.getenv("WECHAT_ARCHIVE_API_KEY", "") TIMEOUT = int(os.getenv("WECHAT_ARCHIVE_TIMEOUT", "30")) def archive_wechat_article(url: str, fmt: str = "both") -> dict: headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"} payload = {"url": url, "format": fmt, "timeout": "20"} resp = requests.post(API_URL, json=payload, headers=headers, timeout=TIMEOUT) resp.raise_for_status() body = resp.json() if body.get("code") != 0: raise RuntimeError(f"API error: code={body['code']}, msg={body.get('msg')}, request_id={body.get('request_id')}") return body["data"]上面是 Python 示例,核心是检查code字段而不是仅依赖 HTTP 状态码。
2. 正文与图片的落盘策略
拿到markdown后,直接写入文件时要注意编码统一为 UTF-8。图片建议按文章 ID 分目录存储,文件名用图片 URL 的哈希值,避免与微信自带的随机名冲突。示例思路:
import hashlib from pathlib import Path def save_markdown(article_id: str, markdown_text: str) -> Path: out_dir = Path("articles") / article_id out_dir.mkdir(parents=True, exist_ok=True) md_path = out_dir / "article.md" md_path.write_text(markdown_text, encoding="utf-8") return md_path def image_filename(image_url: str) -> str: return hashlib.sha1(image_url.encode("utf-8")).hexdigest() + ".jpg"3. 去重与幂等
相同文章链接可能在业务中被多次提交。建议在数据库中记录url+publish_time作为唯一键,或者使用request_id做错误重试的去重,避免重复下载图片和重复入库。
4. 元信息的时间处理
publish_time是带时区的 ISO 字符串,不要直接当本地时间用。使用 JavaOffsetDateTime、Pythondatetime.fromisoformat或 Gotime.RFC3339解析,统一转成 UTC 存储。
5. 日志与监控
记录每次请求的url、request_id、HTTP 状态码、接口返回码、耗时。当code非 0 或images为空时,告警条件要与正常文章(无图文章)区分开,避免误报。
6. 重试策略
对于 HTTP 429、5xx 以及部分网络超时,可以采用“最多 3 次、间隔 1s/2s/4s”的退避策略。但对 400 类参数错误不要重试,直接记录业务异常。
参考文档
- 接口文档:https://apizero.cn/aidocs/wechat-archive
- 原始文档:https://apizero.cn/aidocs/wechat-archive/raw.md
以上接入要点均基于接口事实卡整理,具体鉴权方式、限流数值和错误码定义请以最新文档为准。