返回教程列表
教程2026年10月11日5 分钟阅读

微信公众号历史文章怎么获取?文章列表、正文与分页采集教程

从一篇公众号文章链接出发,用 Just One API 获取历史文章和正文。包含 POST 表单、Offset 翻页、多图文处理及 Python 归档示例。

从一篇公众号文章找到历史文章和正文

如果你已经有一篇微信公众号文章链接,可以通过 Just One API 定位对应公众号、获取历史文章列表,再按文章链接读取正文。这个流程适合整理品牌或行业公众号的内容样本,建立文章目录,或为后续内容检索准备数据。

历史列表和文章正文是两个步骤:先取得文章链接,再按需读取正文。本文用最多 2 页历史列表、最多 3 篇文章详情演示接入方式,不会一次遍历整个账号。

准备账户、Token 和文章链接

注册 Just One API 账户 获取 API Token,通过在线文档可以免费测试接口。一个 Token 可接入各平台 API,后续按实际调用量付费;可测试次数以账户内各接口显示为准。

准备一篇目标公众号的公开文章链接,例如自己在微信中打开并复制的文章地址。本文用这条链接定位公众号,不需要你手动推测账号标识。

  • 历史文章 V2:/api/weixin/get-account-history-articles/v2,使用 POST 表单请求。
  • 文章详情 V1:/api/weixin/get-article-detail/v1,使用 GET 请求。

两种调用方式的参数位置不同。历史文章接口将 token 和其他参数放在表单中;文章详情接口使用查询参数。

第一步:获取公众号历史文章列表

打开 历史文章 V2 文档,传入 token,再选择以下一种方式定位账号:

  • url:该公众号的一篇文章链接,本文使用这种方式。
  • ghid:以 gh_ 开头的公众号原始标识。

第一次请求把 offset 留空。请求体类型是 application/x-www-form-urlencoded;在 Python requests 中使用 data=... 即可,不要改成 json=...。

一次推送可能包含多篇文章

当前公开响应样例中,推送列表位于 data.MsgList.Msg,每条推送的文章条目位于 AppMsg.DetailInfo 数组,文章链接字段为 ContentUrl。如果只取每次推送中的第一条,会漏掉同一次推送中的其他文章。

示例会遍历这些文章条目,并按返回的文章链接去重,再选取最多 3 篇读取正文。

第二步:沿用 Offset 继续翻页

历史文章 V2 的 offset 是游标,不是可以自行递增的页码。当前公开样例中,下一次需要的值位于 data.PagingInfo.Offset。

  • 第一页:offset 留空。
  • 后续页:原样传入上一页返回的 Offset,不自行加一,也不拆解它。
  • data.PagingInfo.IsEnd 为 1 时停止。
  • 如果表示尚未结束却没有返回有效游标,先保存响应检查,不要无限请求同一页。

同一任务始终使用同一个公众号标识或来源文章链接。示例最多请求 2 页,即使后面还有内容也会停止;这个上限是示例设置,不是接口只能获取两页。

第三步:读取文章正文

将历史列表返回的 ContentUrl 传入 文章详情 V1 的 articleUrl 参数。注意,历史列表参数叫 url,文章详情参数叫 articleUrl。

mode=TEXT 用于纯文本正文,适合内容检索和文本处理;RICH_TEXT 用于富文本内容,具体呈现以接口支持和实际返回为准。本文使用 TEXT 并保存完整 JSON,方便同时保留标题、时间和正文等字段。

当前公开样例包含 title、create_time、content 等字段。不同文章形态的可用字段可能不同,先检查返回结构,再决定如何归档。正文接口与阅读、点赞等互动指标接口也是不同能力,不要把文章详情当成全部运营指标。

Python 示例:历史文章列表与正文归档

先安装依赖,再设置 Token 和一条真实文章链接。以下命令适用于 macOS 或 Linux。保留链接中的完整参数,并使用引号包住链接,避免终端将 & 当作控制符。

bash
python3 -m pip install requests
export JUSTONEAPI_TOKEN='your_token'
export WECHAT_ARTICLE_URL='https://mp.weixin.qq.com/s/your_article_id'

将下面代码保存为 wechat_sample.py,运行 python3 wechat_sample.py。最多调用 5 次接口:2 页历史文章和最多 3 篇正文。实际可能因提前结束或失败而更少。

python
import json
import os
from datetime import datetime, timezone
from pathlib import Path

import requests

BASE_URL = "https://api.justoneapi.com"
TOKEN = os.environ.get("JUSTONEAPI_TOKEN", "")


def call_api(path, method="GET", accepted_codes=(0,), **params):
    if not TOKEN or TOKEN == "your_token":
        raise RuntimeError("Set JUSTONEAPI_TOKEN before running")
    values = {"token": TOKEN, **params}
    arguments = {"data": values} if method == "POST" else {"params": values}
    try:
        response = requests.request(
            method, BASE_URL + path, timeout=120, **arguments,
        )
    except requests.RequestException:
        raise RuntimeError("Network error or timeout") from None
    if response.status_code != 200:
        raise RuntimeError(f"HTTP {response.status_code}")
    try:
        result = response.json()
    except ValueError:
        raise RuntimeError("Response is not valid JSON") from None
    if not isinstance(result, dict) or result.get("code") not in accepted_codes:
        code = result.get("code") if isinstance(result, dict) else None
        raise RuntimeError(f"API failed: code={code}; check the docs")
    return result


def output_dir(prefix):
    stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
    folder = Path(prefix) / stamp
    folder.mkdir(parents=True, exist_ok=False)
    return folder


def save_json(folder, filename, value):
    (folder / filename).write_text(
        json.dumps(value, ensure_ascii=False, indent=2), encoding="utf-8",
    )


SOURCE_ARTICLE_URL = os.environ.get("WECHAT_ARTICLE_URL", "")
MAX_HISTORY_PAGES = 2
MAX_ARTICLES = 3


def main():
    if not SOURCE_ARTICLE_URL.startswith("https://mp.weixin.qq.com/"):
        raise RuntimeError("Set WECHAT_ARTICLE_URL to a WeChat article URL")
    folder = output_dir("wechat-output")
    offset = ""
    seen_offsets = set()
    article_urls = []
    seen_urls = set()

    for page in range(1, MAX_HISTORY_PAGES + 1):
        history = call_api(
            "/api/weixin/get-account-history-articles/v2", method="POST",
            url=SOURCE_ARTICLE_URL, offset=offset,
        )
        save_json(folder, f"history-{page}.json", history)
        data = history["data"]
        for message in (data.get("MsgList") or {}).get("Msg") or []:
            entries = (message.get("AppMsg") or {}).get("DetailInfo") or []
            for article in entries:
                article_url = article.get("ContentUrl")
                if article_url and article_url not in seen_urls:
                    seen_urls.add(article_url)
                    article_urls.append(article_url)
        paging = data.get("PagingInfo") or {}
        is_end = paging.get("IsEnd")
        if is_end in (True, 1, "1"):
            break
        if is_end not in (False, 0, "0"):
            raise RuntimeError("Missing IsEnd; inspect saved history JSON")
        next_offset = paging.get("Offset")
        if page < MAX_HISTORY_PAGES:
            if not next_offset or next_offset in seen_offsets:
                raise RuntimeError("Missing or repeated history Offset")
            seen_offsets.add(next_offset)
            offset = next_offset

    selected = article_urls[:MAX_ARTICLES]
    save_json(folder, "selected-article-urls.json", selected)
    for index, article_url in enumerate(selected, start=1):
        detail = call_api(
            "/api/weixin/get-article-detail/v1",
            articleUrl=article_url, mode="TEXT",
        )
        save_json(folder, f"article-{index}-detail.json", detail)
    print(f"Saved {len(selected)} article samples to {folder}")


if __name__ == "__main__":
    try:
        main()
    except RuntimeError as error:
        raise SystemExit(str(error)) from None

结果保存在 wechat-output 下本次运行的目录中,包括历史列表、选中的文章链接和各篇详情 JSON。代码依据当前文档公开响应样例,文章是否可获取及具体返回字段以运行时结果为准。

代码按完全相同的链接去重。若你的数据源存在同一文章的多个链接形式,可先核对并建立统一文章标识;不要直接删除所有 URL 参数,部分参数用于标识具体文章。

脚本遇到错误会停止,不会自动重试。已保存的响应仍然保留,再次运行会重新请求。需要持续同步时,应记录成功文章、最后游标和采集时间,并按 失败重试指南 增加有上限的重试。

常见问题

我有公众号名称,为什么这里需要文章链接或 ghid?

历史文章 V2 接受的是文档中定义的账号标识或文章链接,公众号名称不能直接代替 ghid。可以先使用 公众号搜索接口 查找目标账号,再按实际返回和接口参数要求衔接。

能保证获取账号全部历史文章吗?

不能保证。平台可见范围、文章状态和接口返回都会影响覆盖。分页结束代表本次查询没有后续页,不等于证明账号所有历史内容都已获取。报告中应标明采集时间、页数及范围。

列表有文章,正文却没有返回,怎么办?

先查看业务错误码和文章状态,确认链接完整、Token 和权限正常。文章可能已删除、不可见或出现临时采集失败;不要把无正文直接保存为“这篇文章没有内容”。保留失败记录,再按错误类型决定是否重试。

可以只读取一篇已知文章吗?

可以。直接调用文章详情接口即可,无需获取历史列表。如果原始链接需要展开,可先查看 文章链接转换接口。

先测试一个账号,再扩展归档范围

从一篇你熟悉的公众号文章开始,在 历史文章在线文档 中免费测试,核对账号和文章列表,再测试正文。确认数据满足需求后,再增加分页数量和持续同步逻辑。

继续使用 Just One API

登录 Dashboard 获取 Token,查看完整 API 文档,或打开 MCP GitHub 项目了解最新配置。