# 公众号 API Python 教程：把文章转成 Markdown 给 RAG 用（2026）

> 用 Python 写一个小客户端：按名称找到公众号，列出最近的文章，每篇存成带 front matter 的 Markdown 文件，直接拿去切块和向量化。

网页版：https://okdata.online/zh/guides/wechat-official-account-api-python/

微信官方接口只服务你自己管理的公众号，所以对任意公开账号，这篇教程用 Python 调 okdata 的三个接口：公众号搜索、公众号历史文章和公众号文章详情。示例一共 14 次调用，搜索按满页计，花费 $0.84；失败的调用不扣费。

更新于 2026-11-12

微信官方接口能做什么？公众号的服务端接口都从 access_token 开始，它用公众号的 AppID 和 AppSecret 换取，有效期 7200 秒。拿到它之后，发布能力里的 `freepublish_batchget` 能返回你这个号已经发布的文章，其他接口还能管理草稿、素材和看统计数据。第三方平台在公众号授权之后，也能代它调用这些接口。所以如果号是你自己的，就用官方接口：免费、完整，还能拿到原始 HTML。它做不到的是读你不管理的号，而做行业研究或竞品知识库，要的恰恰是这部分。

这篇教程讲的就是这种情况。你会写一个 Python 小客户端：按名称找到公众号，列出最近的文章，拉每篇的全文，存成带 YAML front matter 的 Markdown 文件，开头写着标题、公众号、日期和原文链接。这些文件可以直接交给切块和向量化流程，每个块都能带上出处。

## 分步操作

### 第一步：准备环境

需要 Python 3.9 以上、`requests`，以及放在环境变量里的 key。没有 SDK，也不用登录微信。

```bash
pip install requests
export OKDATA_API_KEY=okd_...
```

每次调用都是对 `https://api.okdata.online` 加接口路径发 GET 请求，key 放在 Bearer 头里。成功时 `"code"` 为 `"ok"`；出错时返回 `code`、`message`、`request_id` 和 `retry` 提示（`yes` 表示等 `retry_after_seconds` 后可以重试）。

### 第二步：找到公众号

[公众号搜索](https://okdata.online/zh/apis/wechat_mp/accounts/search/)的 `query` 填公众号名称。每个结果有 `id`（原始 ID，`gh_…`）、`username`（微信号）、`name` 和 `followers`。只保留 `name` 完全一致的那个：公众号名称不唯一，排第一的不一定是你要的号。

搜索按返回的公众号数计费：每个 $0.058，一次最多 $0.58。用完整名称搜，通常只返回一个或几个。第一次跑完就把 gh_ ID 存下来，以后这个号再也不用搜。

```bash
curl -G "https://api.okdata.online/v1/wechat_mp/accounts/search" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  --data-urlencode 'query=少数派'
```

### 第三步：列出最近的文章

[公众号历史文章](https://okdata.online/zh/apis/wechat_mp/accounts/articles/list/)的 `id` 传 gh_ ID 或微信号，每次返回 10 篇，从新到旧，每次 $0.02。每篇有 `id`、`title`、`url`、`published_at` 和摘要 `text`。用 `cursor` 翻页，直到 `page.has_more` 为 false 或者到了你设的页数上限。

### 第四步：把每篇文章拉成文本

[公众号文章详情](https://okdata.online/zh/apis/wechat_mp/articles/get/)的 `url` 传文章链接，`text` 里是纯文本正文，段落和小标题各占一行，正文图片已去掉，每次 $0.02。同时返回 `title`、`published_at` 和 `author`（公众号的 gh_ ID 和名称）。加 `view=full` 时，`ext.author_byline` 是署名作者，转载文章的原文地址在 `ext.source_url`。

```bash
curl -G "https://api.okdata.online/v1/wechat_mp/articles/get" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  --data-urlencode 'url=https://mp.weixin.qq.com/s/Vt8nR2kLq5XwJ0cYhPa3dE' \
  --data-urlencode 'view=full'
```

### 第五步：客户端代码

下面的脚本跑的就是示例：搜索一次，最多读 `MAX_PAGES` 页历史文章，把列出的所有文章写进索引，再挑出最多 `MAX_ARTICLES` 篇本地还没有的文章拉全文，存成 `kb/wechat/<id>.md`。下周再跑一次，只为新文章付钱。

```python
import os
import pathlib
import re
import time
import uuid
import requests

API = "https://api.okdata.online"
SESSION = requests.Session()
SESSION.headers["Authorization"] = f"Bearer {os.environ['OKDATA_API_KEY']}"
OUT = pathlib.Path("kb/wechat")
MAX_PAGES = 3      # 历史文章页数，每页 10 篇
MAX_ARTICLES = 10  # 每次运行最多拉几篇全文


class ApiError(Exception):
    def __init__(self, body):
        super().__init__(f"{body['code']}: {body['message']} (request {body['request_id']})")
        self.code = body["code"]


def call(path, **params):
    key = str(uuid.uuid4())  # 重试沿用同一个 key：重放不会再次扣费
    for attempt in range(3):
        body = SESSION.get(API + path, params=params, headers={"Idempotency-Key": key}, timeout=90).json()
        if body["code"] == "ok":
            return body
        if body.get("retry") == "yes" and attempt < 2:
            time.sleep(body.get("retry_after_seconds") or 5)
            continue
        raise ApiError(body)


def find_account(name):
    accounts = call("/v1/wechat_mp/accounts/search", query=name)["data"]["items"]
    match = next((a for a in accounts if a["name"] == name), None)
    if match is None:
        raise SystemExit(f"no account named {name!r}; got {[a['name'] for a in accounts]}")
    return match


def history(account_id):
    cursor = None
    for _ in range(MAX_PAGES):
        body = call("/v1/wechat_mp/accounts/articles/list", id=account_id, **({"cursor": cursor} if cursor else {}))
        yield from body["data"]["items"]
        if not body["page"]["has_more"]:
            return
        cursor = body["page"]["next_cursor"]


def quoted(s):
    return '"' + (s or "").replace("\\", "\\\\").replace('"', '\\"') + '"'


def to_markdown(article, account):
    ext = article.get("ext", {})
    front = [
        "---",
        f"title: {quoted(article['title'])}",
        f"account: {quoted(account['name'])}",
        f"account_id: {account['id']}",
        f"byline: {quoted(ext.get('author_byline'))}",
        f"published: {article['published_at']}",
        f"source: {article['url']}",
        "---",
    ]
    return "\n".join(front) + f"\n\n# {article['title'] or ''}\n\n{article['text']}\n"


def main(name):
    OUT.mkdir(parents=True, exist_ok=True)
    account = find_account(name)
    listed = list(history(account["id"]))  # 从新到旧
    with open(OUT / "index.tsv", "w", encoding="utf-8") as f:
        f.writelines(f"{a['published_at']}\t{a['title']}\t{a['url']}\n" for a in listed)

    path_of = lambda a: OUT / (re.sub(r"[^\w-]", "_", a["id"]) + ".md")
    todo = [a for a in listed if not path_of(a).exists()][:MAX_ARTICLES]
    for a in todo:
        try:
            full = call("/v1/wechat_mp/articles/get", url=a["url"], view="full")["data"]["item"]
        except ApiError as err:
            if err.code == "not_found":  # 列出之后被删了；不扣费
                continue
            raise
        path_of(a).write_text(to_markdown(full, account), encoding="utf-8")
        print("saved", full["title"])


if __name__ == "__main__":
    main("少数派")
```

第一次运行就是示例里的 14 次调用：1 次搜索、3 页历史文章、10 篇全文。如果要把这个号最近 300 篇都补齐，还要再花 $6.60。

### 第六步：切块和入库

每个文件是 front matter 加正文。按空行把正文切成几百字一块，把 `title`、`account`、`published` 和 `source` 复制到每个块的元数据里，这样回答时能引用原文、附上链接。大多数加载器能直接读 YAML front matter；读不了的话，自己解析两行 `---` 之间的内容即可。

## 注意事项

- **是文本，不是排版。** 详情返回纯文本，表格会变成一行行文字，正文图片没了，封面链接在 `images` 里。要保留文章原本的样子，得用别的办法存 HTML。
- **转载。** 很多号会转载别人的文章。详情里的 `ext.source_url` 和 `ext.copyright` 可以帮你在入库前去重，免得重复内容干扰检索。
- **删掉的不收钱。** 列出之后被删的文章返回 `not_found`，不扣费，客户端会直接跳过。
- **重试是安全的。** 客户端对同一个请求的重试沿用同一个 `Idempotency-Key`，24 小时内同样的请求配同样的 key，会拿回上次的结果，`meta.replayed` 为 `true`，不再扣费。
- **文本不可信。** 文章是陌生人写的，要告诉模型把检索到的段落当数据看，不要当指令执行。
- **版权。** 只用于内部研究，每个块都带上原文链接，不要把整篇文章原样返回给用户。

只想看 Markdown 那一步的简版，见[怎么把公众号文章转成 RAG 能用的干净文本](https://okdata.online/zh/answers/wechat-articles-to-text-for-rag/)。手上只有名称、要先查 ID，见[怎么查公众号的原始 ID](https://okdata.online/zh/answers/find-wechat-official-account-id/)。

## 示例花费

搜一次“少数派”，翻 3 页历史文章（约 30 篇），把最新的 10 篇存成 Markdown 文件

| 接口 | 调用次数 | 单价 | 小计 |
|---|---|---|---|
| [公众号搜索](https://okdata.online/zh/apis/wechat_mp/accounts/search/) | 1 | $0.058 | $0.58 |
| [公众号历史文章](https://okdata.online/zh/apis/wechat_mp/accounts/articles/list/) | 3 | $0.02 | $0.06 |
| [公众号文章详情](https://okdata.online/zh/apis/wechat_mp/articles/get/) | 10 | $0.02 | $0.20 |
| 合计 | 14 |  | $0.84 |

按本页生成时的价格计算。失败的调用不扣费。

## 在你的 AI 里用（MCP）

先接好 MCP 服务，然后直接说你要什么。智能体会自己找到这些接口、看清价格，再用你的余额调用。

可以这样说：找到公众号“少数派”，取它最新的 10 篇文章，每篇整理成 Markdown，开头写上标题、公众号、发布日期和原文链接。

```json
{
  "endpoint": "wechat_mp.accounts.search",
  "params": {
    "query": "36氪"
  },
  "idempotency_key": "<新的 UUID>"
}
```

MCP：`https://api.okdata.online/mcp`

## 本指南用到的接口

- [公众号搜索](https://okdata.online/zh/apis/wechat_mp/accounts/search/) `wechat_mp.accounts.search`：$0.058/条 · 每次最多 $0.58
- [公众号历史文章](https://okdata.online/zh/apis/wechat_mp/accounts/articles/list/) `wechat_mp.accounts.articles.list`：$0.02/次 · 每页约 10 篇文章
- [公众号文章详情](https://okdata.online/zh/apis/wechat_mp/articles/get/) `wechat_mp.articles.get`：$0.02/次

## 更多微信公众号内容

- [微信公众号 API](https://okdata.online/zh/apis/wechat_mp/)
- [怎么拿到一个公众号的历史文章和全文](https://okdata.online/zh/guides/wechat-official-account-articles/)
- [为什么 ChatGPT、Claude 打不开公众号文章链接？怎么让 AI 读到全文？](https://okdata.online/zh/answers/ai-read-wechat-article-link/)
- [怎么不登录导出一个公众号的全部历史文章？](https://okdata.online/zh/answers/export-wechat-official-account-articles/)
- [怎么监控竞品公众号每天发了什么？](https://okdata.online/zh/answers/monitor-competitor-wechat-official-accounts/)
- [公众号文章链接里的 __biz 是什么？](https://okdata.online/zh/answers/wechat-biz-parameter/)
- [公众号的原始 ID（gh_ 开头）是什么？](https://okdata.online/zh/answers/wechat-gh-original-id/)
- [公众号文章数据 API 多少钱？](https://okdata.online/zh/answers/wechat-official-account-api-price/)
- [平台官方 API 和第三方数据接口怎么选（2026）](https://okdata.online/zh/compare/official-apis-vs-data-apis/)
- [社媒数据 MCP 服务器：自己部署还是用托管的](https://okdata.online/zh/compare/social-media-data-mcp-servers/)
- [公众号文章和阅读数接口有哪些？官方接口、第三方 API、开源工具、数据平台 4 类方案怎么选（2026）](https://okdata.online/zh/compare/wechat-article-data-apis/)
- [获取微信公众号数据的几种办法：官方接口、RSS 桥接和数据接口](https://okdata.online/zh/compare/wechat-official-account-data-apis/)
- [让 AI 读小红书、抖音、公众号的 MCP 有哪些？开源自部署、浏览器自动化、托管 3 类怎么选（Claude、Cursor、豆包）](https://okdata.online/zh/compare/xiaohongshu-douyin-wechat-mcp-servers/)
- [账号作品列表接口](https://okdata.online/zh/roles/accounts-posts-list/)

## 资料来源

- [微信开放文档: 获取接口调用凭据 (getAccessToken)](https://developers.weixin.qq.com/doc/subscription/api/base/api_getaccesstoken.html) (读取于 2026-11-12)
- [微信开放文档: 获取已发布的消息列表 (freepublish_batchget)](https://developers.weixin.qq.com/doc/subscription/api/public/api_freepublish_batchget.html) (读取于 2026-11-12)
