一个面向个人/小团队的微信公众号文本数据底座:扫码登录、搜索确认、后台抓取、正文预览、导出、数据库浏览、MCP 接入一站式完成,让 AI 能直接消费公众号文本做数据挖掘与分析。
- AI 缺少可用数据源:公众号正文分散在微信后台,无法被大模型稳定访问。
- 数据沉淀困难:抓取、清洗、去重、入库链路分散,难形成可复用的数据资产。
- 分析流程割裂:抓取和 AI 工具脱节,无法快速完成检索、摘要、主题归纳与问答。
这个项目的目标是把流程做成AI 可消费的低心智负担控制台:前端负责采集与可视化,后端负责入库与任务管理,MCP 负责把结构化文本安全地暴露给 AI 工具。
- 数据资产化:把公众号正文持续沉淀为可检索、可复用、可追溯的本地文本库。
- AI 可直连:通过 MCP 直接把标题、正文与元数据暴露给大模型,不再手工复制粘贴。
- 分析可落地:支持从“抓取数据”走到“摘要、分类、对比、趋势洞察”的完整闭环。
- 协作可扩展:个人可自用,小团队可共享同一套数据口径与分析流程。
- 内容团队:做选题雷达、爆款拆解、竞品账号跟踪。
- 投研/咨询团队:沉淀行业观点,做主题归因与长期观察。
- AI 应用开发者:把公众号文本作为 RAG/Agent 的稳定数据源。
| 维度 | 传统方式 | mp-data-console |
|---|---|---|
| 数据获取 | 手工登录 + 零散复制 | 后台任务批量抓取 + 去重入库 |
| 数据形态 | 网页片段,难复用 | SQLite 全文 + 元数据,结构化沉淀 |
| AI 接入 | 复制粘贴给模型,链路不稳定 | MCP 工具直连,查询与分析可复现 |
| 产出效率 | 人工通读,耗时高 | AI 先筛选再深读,显著缩短分析周期 |
| 抓取视图 | MCP 接入 | 数据库 |
|---|---|---|
![]() |
![]() |
![]() |
- 扫码登录微信公众号后台(含状态轮询与会话恢复)
- 公众号搜索与抓取前确认(头像、别名、时间范围)
- 后台抓取任务(
queued/running/canceling/success/failed/canceled) - 时间范围抓取:只按
date_start ~ date_end设定抓取窗口 - 常用公众号自动同步:支持按频率增量抓取(回看天数 + 重叠小时)与全局开关
- 任务中心:任务历史、筛选、详情日志、取消与重试
- 抓取文章列表 + 正文
- 正文在线预览(处理隐藏样式和图片防盗链问题)
- 导出 Markdown / HTML / PDF
- 批量导出 ZIP
- 数据库在线浏览(分页、关键词、指定字段搜索、精确过滤)
- 表注释 / 列注释展示
- MCP 文章文本服务配置一键生成(直连 SQLite)
- 图片代理与导出图片本地化,降低防盗链导致的丢图
- 通过 MCP 直接读取公众号正文(标题 + 全文 + 元数据)
- 支持在 Claude Desktop / Cursor / OpenCode / Codex 中即插即用
- 提供
search_articles/get_article_text等工具,便于 AI 检索与长文分析 - 为分类、摘要、关键信息抽取、主题趋势分析提供统一文本数据入口
- 选题趋势监测:统计过去 30 天某垂类公众号高频主题、叙事角度与发布时间规律。
- 竞品内容对标:对比多个账号在同一时间窗口的选题重合度与表达差异。
- 长文快速提炼:对单篇或多篇文章抽取核心结论、关键事实与可执行建议。
示例提示词:
请基于最近 30 天抓取的「AI 行业」相关公众号文章,输出:
1) 高频主题 Top 10
2) 每个主题的代表文章与关键观点
3) 下周可执行的 5 个选题建议
flowchart LR
UI[Vue Console] -->|REST API| API[FastAPI]
API --> DB[(SQLite)]
API --> WX[WeChat MP Backend]
API --> JOB[Capture Job Worker]
API --> EXP[Export Service]
API --> IMG[Image Proxy]
EXP --> OUT[data/exports]
flowchart LR
WX[微信公众号后台] --> C[抓取任务]
C --> D[(SQLite 正文库)]
D --> M[MCP Server]
M --> A[Claude/Cursor/OpenCode/Codex]
A --> I[摘要/分类/趋势分析]
I --> O[报告/导出/知识库]
- 后端通过
requests.Session模拟公众号后台会话,请求微信首页提取二维码参数;若页面结构变化,自动回退到startlogin + scanloginqrcode流程生成二维码。 - 轮询登录状态时,根据微信状态码映射为
waiting_scan / scanned / logged_in / expired,扫码成功后再调用登录接口完成 token 建立。 - token 不是单点来源:会从响应 JSON、URL、页面文本、Cookie、历史 token 多路提取,并用
switchacct接口校验可用性,降低偶发失败。 - 会话信息(token、cookie、fingerprint、状态)持久化在
auth_sessions;服务重启后会自动恢复,并在 token 仍有效时回写为logged_in。
- 公众号先搜索后入库,按
fakeid(必要时biz)做 upsert,避免重复创建。 - 抓取文章时优先使用
appmsgpublish,若返回空再回退appmsg,提升兼容性。 - 每页按 5 条扫描,任务内用
url + aid双键去重,重复项计入duplicates_skipped。 - 按时间范围抓取:扫描会在触达时间窗口下界后自动停止。
- 前端提交抓取后,后端先写入
capture_jobs(queued),再启动守护线程异步执行,不阻塞接口返回。 - 运行中持续把
created/updated/content_updated/scanned_pages/max_pages回写数据库,前端可轮询得到实时进度。 - 服务内用互斥锁保证同一时刻只跑一个抓取 worker,避免并发抓取导致登录态冲突与频控风险。
- 若进程异常重启,数据库里遗留的
queued/running任务会在下次查询时自动标记为failed,并提示“任务进程已中断”。
sequenceDiagram
autonumber
participant U as 用户
participant FE as Vue前端
participant API as FastAPI
participant JOB as 抓取Worker
participant WX as 微信公众号后台
participant DB as SQLite
U->>FE: 选择公众号并提交抓取参数
FE->>API: POST /mps/:mp_id/sync/jobs
API->>DB: 写入 capture_jobs(status=queued)
API-->>FE: 返回 job_id
API->>JOB: 启动后台线程(job_id)
JOB->>DB: 更新状态 running + started_at
loop 分页抓取直到结束或达标
JOB->>WX: 拉取文章列表(appmsgpublish/appmsg)
JOB->>WX: 可选拉取正文详情
JOB->>DB: upsert mps/articles
JOB->>DB: 回写进度(created/updated/scanned_pages)
end
JOB->>DB: 更新 success/failed + finished_at
loop 前端轮询
FE->>API: GET /mps/sync/jobs/:job_id
API->>DB: 读取任务状态与统计
API-->>FE: 返回最新进度
end
FE-->>U: 展示任务结果并支持导出
- 正文抓取后使用
BeautifulSoup解析#js_content/#js_article,移除脚本样式,并清理visibility:hidden、opacity:0、display:none等常见隐藏样式。 - 图片链接统一回填到可用
src(优先data-src),同时提取content_text、封面、作者、发布时间等字段。 - 遇到“当前环境异常,完成验证后即可继续访问”页面时,自动回退 Playwright,并复用登录 cookie 获取可渲染正文。
- Markdown 导出时将图片地址改写为本地图片代理接口,减少外链防盗链导致的失效。
- HTML/PDF 导出会尝试把正文图片下载到本地
assets目录并重写相对路径;下载失败再降级为代理链接。 - 图片代理仅允许微信相关域名,使用
sha256(url)做缓存键,二进制 + 元信息分离存储,默认 7 天 TTL,支持重试与内容类型嗅探。 - 批量导出时会把文章文件和对应
assets一并打包 ZIP,下载接口带路径校验,防止越权读取。
- Python 3.11+
- Node.js 20+
- Linux / macOS / Windows
如果需要 PDF 导出,请安装 Playwright 浏览器:
playwright install chromium./dev.sh脚本会自动检测依赖是否需要安装(requirements.txt / web/package-lock.json 变化会自动重装)。
强制重新安装依赖:
./dev.sh up --install安装 PDF 导出依赖(Chromium):
./dev.sh up --install-playwright停止服务:
./dev.sh down重启服务:
./dev.sh restart默认访问地址:
- 前端:
http://127.0.0.1:5173 - 后端:
http://127.0.0.1:18011 - API 文档:
http://127.0.0.1:18011/docs
- 运行
./dev.sh,打开前端扫码登录并抓取 12 个公众号(建议先抓 2050 篇,含正文)。 - 在「MCP 接入」页点击“刷新配置”,复制对应客户端配置(OpenCode / Codex / Claude / Cursor)。
- 在你的 AI 客户端加载 MCP 后,先调用
list_mps和search_articles确认数据已可读。 - 发送分析指令(示例):
请对最近 30 天「新能源」相关公众号文章做分析:
- 总结 5 个高频主题及其代表观点
- 提取各主题的分歧点和共识点
- 给出 3 条值得继续跟踪的信号
- 如需交付结果,可在控制台导出 Markdown / HTML / PDF,或批量打包 ZIP。
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
uvicorn app.main:app --host 0.0.0.0 --port 18011 --reloadcd web
npm install
cp .env.example .env
npm run dev- 获取二维码并扫码登录
- 搜索公众号并确认抓取目标
- 设置抓取时间范围(开始日期 / 结束日期,或快捷范围)
- 提交抓取任务(任务进入后台执行,可离开当前页面)
- 回来查看任务状态(新增/更新/重复跳过/扫描进度)
- 在文章区预览正文并导出
- 在 MCP 客户端连接本地数据库,让 AI 执行检索、摘要与文本挖掘
GET /auth/qrGET /auth/qr/imageGET /auth/statusGET /auth/sessionPOST /auth/logout
GET /mps/search?keyword=关键词POST /mpsGET /mps(支持favorite_only=true仅看常用)PATCH /mps/{mp_id}/favorite(设为/取消常用)PATCH /mps/{mp_id}/auto-sync(配置自动同步策略)POST /mps/{mp_id}/sync/jobs(后台任务,推荐)GET /mps/sync/jobsGET /mps/sync/jobs/{job_id}GET /mps/sync/jobs/{job_id}/logsPOST /mps/sync/jobs/{job_id}/cancelPOST /mps/sync/jobs/{job_id}/retryPOST /mps/{mp_id}/sync(阻塞模式,兼容保留)
GET /articlesGET /articles/{article_id}POST /articles/{article_id}/refreshPOST /exports/article/{article_id}POST /exports/batchGET /exports/files/{relative_path}
GET /ops/overviewPOST /ops/quick-syncGET /ops/auto-sync/statusPATCH /ops/auto-sync/enabledPOST /ops/auto-sync/run-nowGET /ops/db/tablesGET /ops/db/table/{table_name}GET /ops/mcp/configPOST /ops/mcp/generate-fileGET /assets/image?url=<原图地址>
项目内置了 MCP 服务模块 app.mcp_server,可直接读取 SQLite 中的文章正文文本(articles.content_text),便于在 Claude Desktop / Cursor / OpenCode / Codex 等 MCP 客户端里做问答、检索、摘要与主题分析。
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtpython3 -m app.mcp_server --db-path "$(pwd)/data/wechat_mini.db"方式 A:在前端「MCP 接入」页点击“刷新配置 / 生成配置文件”
方式 B:直接调用接口:
curl "http://127.0.0.1:18011/api/v1/ops/mcp/config"返回中的 claude_config_json(兼容字段 config_json)可直接用于 Claude / Cursor,例如:
{
"mcpServers": {
"mp-data-console": {
"command": "/abs/path/to/python",
"args": [
"-m",
"app.mcp_server",
"--db-path",
"/abs/path/to/data/wechat_mini.db"
]
}
}
}OpenCode 请使用 opencode_config_json(opencode.json 配置片段),例如:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"mp-data-console": {
"type": "local",
"enabled": true,
"command": [
"/abs/path/to/python",
"-m",
"app.mcp_server",
"--db-path",
"/abs/path/to/data/wechat_mini.db"
],
"environment": {
"PYTHONPATH": "/abs/path/to/we-mp-mini"
}
}
}
}OpenCode 配置位置:
- 项目级:项目根
opencode.json - 全局:
~/.config/opencode/opencode.json
保存后可执行:
opencode mcp listCodex 请使用 codex_config_toml(config.toml 配置片段),例如:
[mcp_servers.mp-data-console]
command = "/abs/path/to/python"
args = ["-m", "app.mcp_server", "--db-path", "/abs/path/to/data/wechat_mini.db"]
[mcp_servers.mp-data-console.env]
PYTHONPATH = "/abs/path/to/we-mp-mini"Codex 配置位置:
- 全局:
~/.codex/config.toml - 项目级:
.codex/config.toml(仅 trusted 项目生效)
也可以直接使用 codex_cli_add_command(CLI 一键添加命令),例如:
codex mcp add mp-data-console --env PYTHONPATH="/abs/path/to/we-mp-mini" -- /abs/path/to/python -m app.mcp_server --db-path "/abs/path/to/data/wechat_mini.db"保存后可在 Codex TUI 输入 /mcp 查看是否加载,或继续用 codex mcp 管理 MCP。
官方文档:
- Claude Code MCP: https://code.claude.com/docs/en/mcp
- Codex MCP: https://developers.openai.com/codex/mcp/
- Config: https://opencode.ai/docs/config/
- MCP Servers: https://opencode.ai/docs/mcp-servers/
list_mps:列出库内公众号(含文章数量、正文数量)list_articles_by_mp:按公众号名或mp_id列出该号文章db_overview:查看公众号数、文章数、带正文文章数search_articles:按关键词搜索标题/正文,返回正文预览与文章 IDget_article_text:按article_id或url读取完整正文文本
建议流程:先用 list_mps / list_articles_by_mp 定位公众号和文章,再用 get_article_text 拉取全文。
所有接口统一返回结构:
{
"ok": true,
"message": "ok",
"data": {}
}提交后台抓取任务:
curl -X POST "http://127.0.0.1:18011/api/v1/mps/<mp_id>/sync/jobs" \
-H "Content-Type: application/json" \
-d '{"date_start":"2026-01-01","date_end":"2026-01-31"}'查询任务状态:
curl "http://127.0.0.1:18011/api/v1/mps/sync/jobs/<job_id>"返回中的关键字段:
status:queued/running/canceling/success/failed/canceledstart_ts/end_ts:任务使用的时间范围(秒级时间戳)created:新增文章数updated:已有文章更新数duplicates_skipped:重复跳过数scanned_pages/max_pages:后台扫描进度(内部分页)
- 抓取只接受时间范围(
date_start+date_end),不再提供条数/页数入参 - 后端会在内部自动分页,直到触达时间范围下界或达到安全扫描上限
- 同一任务内若重复(按
url/aid),会跳过并计入duplicates_skipped - 已存在文章会计入
updated - 后台任务依赖后端进程存活;重启后端会中断进行中的任务
后端 .env(节选):
| 变量 | 默认值 | 说明 |
|---|---|---|
APP_NAME |
we-mp-mini |
服务名称 |
API_PREFIX |
/api/v1 |
API 前缀 |
HOST |
0.0.0.0 |
后端监听地址 |
PORT |
18011 |
后端端口 |
DATABASE_URL |
sqlite:///./data/wechat_mini.db |
SQLite 地址 |
REQUEST_TIMEOUT |
20 |
微信请求超时(秒) |
VERIFY_SSL |
true |
是否校验证书 |
AUTO_SYNC_ENABLED |
true |
是否启用自动同步调度器 |
AUTO_SYNC_TICK_SECONDS |
45 |
调度器轮询周期(秒) |
AUTO_SYNC_SCAN_LIMIT |
10 |
每轮最多扫描候选公众号数量 |
AUTO_SYNC_DISPATCH_JITTER_SECONDS |
180 |
下次执行随机抖动上限(秒) |
AUTO_SYNC_FAILURE_BACKOFF_BASE_MINUTES |
15 |
自动同步失败退避基数(分钟) |
AUTO_SYNC_FAILURE_BACKOFF_MAX_MINUTES |
360 |
自动同步失败退避上限(分钟) |
前端 web/.env(节选):
| 变量 | 默认值 | 说明 |
|---|---|---|
VITE_API_BASE |
/api/v1 |
前端请求前缀 |
VITE_DEV_API_TARGET |
http://127.0.0.1:18011 |
Vite 代理后端地址 |
app/
core/ # 配置 + DB
routers/ # API 路由
services/ # 微信认证/抓取/导出/任务
models.py # SQLAlchemy 模型(含 capture_jobs)
schemas.py # Pydantic 模型
mcp_server.py # MCP 服务(读取 SQLite 文章正文)
main.py # FastAPI 入口
dev.sh # 前后端启停一体脚本(up/down/restart,自动依赖检测)
web/
src/ # Vue 页面与 API 调用
vite.config.js # 开发代理配置
data/ # 本地数据库/导出文件/缓存(默认不提交)
mps 是“已保存公众号清单”表:用于记录你确认过的抓取目标,并作为 articles.mp_id 与 capture_jobs.mp_id 的归属主体。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
String(128) |
主键 | 内部公众号 ID(如 MP_WXS_xxx / MP_FAKE_xxx) |
fakeid |
String(255) |
唯一、索引、非空 | 微信后台 fakeid,核心识别字段 |
biz |
String(255) |
唯一、可空 | 公众号 biz,可用于更稳定识别 |
nickname |
String(255) |
索引、非空 | 公众号名称 |
alias |
String(255) |
可空 | 公众号微信号(如 gh_xxx 或品牌号) |
avatar |
String(1024) |
可空 | 头像 URL |
intro |
Text |
可空 | 简介 |
enabled |
Boolean |
默认 true |
是否启用(预留开关) |
is_favorite |
Boolean |
默认 false、索引 |
是否常用公众号(前端可一键抓取) |
use_count |
Integer |
默认 0 |
提交抓取任务次数 |
last_used_at |
DateTime(timezone=True) |
可空 | 最近一次提交抓取任务时间 |
last_sync_at |
DateTime(timezone=True) |
可空 | 最近一次抓取完成时间 |
auto_sync_enabled |
Boolean |
默认 false、索引 |
是否开启自动同步 |
auto_sync_interval_minutes |
Integer |
默认 1440 |
自动同步频率(分钟) |
auto_sync_lookback_days |
Integer |
默认 3 |
自动同步回看窗口(天) |
auto_sync_overlap_hours |
Integer |
默认 6 |
与上次成功同步的重叠窗口(小时) |
auto_sync_next_run_at |
DateTime(timezone=True) |
可空、索引 | 下次计划执行时间 |
auto_sync_last_success_at |
DateTime(timezone=True) |
可空 | 最近一次自动同步成功时间 |
auto_sync_last_error |
Text |
可空 | 最近一次自动同步错误 |
auto_sync_consecutive_failures |
Integer |
默认 0 |
自动同步连续失败次数 |
created_at |
DateTime(timezone=True) |
非空 | 创建时间(UTC) |
updated_at |
DateTime(timezone=True) |
非空 | 更新时间(UTC) |
补充说明:
- 新建/更新入口:
POST /api/v1/mps - 列表查询入口:
GET /api/v1/mps(支持favorite_only=true) - 设为常用入口:
PATCH /api/v1/mps/{mp_id}/favorite - 自动同步配置:
PATCH /api/v1/mps/{mp_id}/auto-sync - 提交抓取任务入口:
POST /api/v1/mps/{mp_id}/sync/jobs
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String(255) |
主键,通常由 mp_id + aid 组成 |
mp_id |
String(128) |
归属公众号 ID(对应 mps.id) |
title |
String(1024) |
标题 |
url |
String(2048) |
原文链接(唯一) |
publish_ts |
BigInteger |
发布时间戳 |
content_html |
Text |
正文 HTML |
content_text |
Text |
正文纯文本 |
images_json |
Text |
正文图片列表(JSON) |
created_at/updated_at |
DateTime |
创建/更新时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String(64) |
任务 ID(job_xxx) |
mp_id |
String(128) |
目标公众号 ID |
source |
String(32) |
任务来源:manual/retry/scheduled |
status |
String(32) |
queued/running/canceling/success/failed/canceled |
start_ts/end_ts |
BigInteger |
抓取时间范围(秒级时间戳) |
requested_count |
Integer |
历史字段(兼容保留,不再作为入参) |
created_count/updated_count |
Integer |
新增/更新文章数 |
duplicates_skipped |
Integer |
重复跳过数 |
scanned_pages/max_pages |
Integer |
后台扫描进度(内部分页) |
reached_target |
Boolean |
历史状态字段(兼容保留) |
error |
Text |
失败原因 |
created_at/started_at/finished_at |
DateTime |
任务时间线 |
| 字段 | 类型 | 说明 |
|---|---|---|
id |
Integer |
日志主键 |
job_id |
String(64) |
关联抓取任务 ID |
level |
String(16) |
日志级别(info/warn/error) |
message |
Text |
日志内容 |
payload_json |
Text |
结构化上下文(JSON) |
created_at |
DateTime |
记录时间 |
| 字段 | 类型 | 说明 |
|---|---|---|
id |
Integer |
主键(单行记录,固定为 1) |
status |
String(32) |
登录状态(logged_out/waiting_scan/scanned/logged_in/...) |
token |
String(128) |
微信后台 token |
cookie_json |
Text |
登录 cookie(JSON) |
account_name/account_avatar |
String |
当前账号信息 |
last_error |
Text |
最近错误 |
created_at/updated_at |
DateTime |
创建/更新时间 |
可以。抓取任务提交后在服务端后台执行,回来后页面会继续展示任务状态。
常见原因是该时间段内可见文章本来就少、限频导致部分页重试失败,或大多数内容已在库中(计入 updated 而非 created)。
常见原因包括登录态失效、微信风控限频、或后端进程重启导致任务中断。
图片经过代理以规避防盗链,会多一次转发请求,首次加载可能慢于直连。
- 仅抓取你有权限访问的账号内容
- 请遵守平台条款与当地法律法规
- 本项目定位研究/自用工具,不提供平台绕过能力


