neurapress-cms 技术调研文档
neurapress-cms 是一个自动化微信公众号文章发布管线。

核心流程:
数据源采集 (RSS/API) → AI 内容生成 → Markdown → HTML (适配微信) → 微信草稿箱
关键约束:
-
微信文章仅支持内联 CSS,不支持外部样式表 -
图片必须通过微信 API 上传至微信 CDN,外链图片会被过滤 -
未认证个人订阅号无法调用发布 API,只能创建草稿,最终发布需在微信后台手动操作
2. 微信公众号 API 调研
2.1 认证机制 (access_token)
获取 access_token:
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
|
|
|
|
|---|---|---|
grant_type |
|
client_credential |
appid |
|
|
secret |
|
|
成功响应:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
错误响应:
{"errcode":40013,"errmsg":"invalid appid"}
关键注意事项:
-
access_token 有效期 2 小时(7200 秒),必须缓存,重复请求会使旧 token 失效 -
存储空间至少预留 512 字符 -
必须在「微信公众平台 > 开发 > 基本配置 > IP 白名单」中配置服务器 IP,否则返回错误码 40164 -
刷新 token 时,新旧 token 在 5 分钟内同时有效 -
最佳实践:使用中央服务统一获取和刷新 access_token,其他服务从该中央服务获取
2.2 草稿箱 API
创建草稿:
POST https://api.weixin.qq.com/cgi-bin/draft/add?access_token=ACCESS_TOKEN
官方文档:
-
订阅号: https://developers.weixin.qq.com/doc/subscription/api/draftbox/draftmanage/api_draft_add.html -
服务号: https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_draft_add.html
请求体(JSON)— articles 数组,每篇文章包含:
|
|
|
|---|---|
title |
|
author |
|
digest |
|
content |
|
content_source_url |
|
thumb_media_id |
|
need_open_comment |
|
only_fans_can_comment |
|
pic_crop_235_1 |
|
pic_crop_1_1 |
|
限制:
-
每篇文章最多 20 张图片 -
第一张图片为封面 -
裁剪坐标使用 (0,0) 左上角到 (1,1) 右下角的坐标系 draft/add
和 media/uploadimg即使未认证的个人订阅号也可调用
2.3 发布 API
POST https://api.weixin.qq.com/cgi-bin/freepublish/submit?access_token=ACCESS_TOKEN
重要限制(2025 年中): 微信已撤销未认证个人账号的发布 API 权限。可以通过 API 创建草稿,但最终「发布」步骤需要在微信后台手动操作。个人账号暂无解决方案。
2.4 素材上传 API
API 1:上传文章内容图片 (media/uploadimg)
POST https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN
-
用于文章 content字段中的内联图片 -
返回 URL(不是 media_id) -
不占用素材库 100,000 条限额 -
仅支持 jpg/png 格式,最大 1MB -
使用 POST form,字段名 media
curl -F media=@test.jpg "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN"
响应:
{"url":"http://mmbiz.qpic.cn/mmbiz/gLO17UPS6FS2xsypf378iaNhWacZ1G1UplZYWEYfwvuU6Ont96b1roYsCNFwaRrSaKTPCUdBK9DgEHicsKwWCBRQ/0"}
API 2:上传永久素材 (add_material)
POST https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=ACCESS_TOKEN&type=TYPE
-
支持类型:image (10M), voice (2M), video (10M), thumb (64KB) -
返回 media_id— 用作封面图的thumb_media_id -
永久素材图片 URL 仅在腾讯域名内可用(外部访问被屏蔽) -
素材库限额:图片/文章最多 100,000 条;其他类型最多 1,000 条
发布文章的完整工作流
-
通过 media/uploadimg上传文章内联图片,获取 URL -
通过 material/add_material(type=image) 上传封面图,获取media_id作为thumb_media_id -
通过 draft/add创建草稿,包含 HTML 内容和 thumb_media_id -
通过 freepublish/submit发布(仅已认证账号)
2.5 图片托管规则
- 外部图片链接会被过滤 — 不能使用外部 CDN 或图床
- 必须通过微信 API 上传(
media/uploadimg
或 material/add_material) -
微信图片有防盗链保护 — 不能从外部网站引用 -
仅支持 HTTPS 协议图片 -
粘贴到微信编辑器的图片会自动上传到微信服务器 -
静态图片宽度建议 1080px(PNG-24 格式减少压缩损失) -
宽度超过 1080px 会被压缩 -
单张图片像素面积(宽 × 高)不能超过 6,000,000 -
GIF:最多 300 帧,建议 12-20fps,640px 宽度,超过 1000px 宽度压缩显著增大
2.6 频率限制
-
每个公众号每个 API 有默认频率限制 -
超限返回: {"errcode": 45009, "errmsg": "api freq out of limit"} -
可在「微信公众平台 > 开发中心 > 接口权限」查看当日调用限额和实时使用量 -
已认证账号可重置配额 -
清除配额 API: POST https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=ACCESS_TOKEN - 订阅号:每天最多群发 1 次,每次最多 8 篇文章
- 服务号:每月最多群发 4 次,每次最多 8 篇文章
-
Token 刷新:最多 200 次/分钟(企业微信) -
核心问题:access_token 获取过于频繁会触发限流,必须缓存
2.7 多账号管理
- 第三方平台模式:通过
authorizer_access_token
管理多个公众号 - 独立客户端模式:每个账号有独立的 appid/appsecret,创建独立的客户端实例
2.8 wechatpy Python SDK
- PyPI:
wechatpy
v1.8.18(最新版) - GitHub:
https://github.com/wechatpy/wechatpy (4.2k stars, 2026年1月更新) - License: MIT
- 安装:
pip install wechatpypip install 'wechatpy[cryptography]'# 推荐 - 功能覆盖:公众号 API、企业微信 API、微信支付 API、第三方平台 API、小程序云 API
- 依赖:python-dateutil>=2.5.2, cryptography>=3.1, requests-pkcs12>=1.7
- 相关项目:wechatpy-asyncio(异步变体)、WeRoBot(4.7k stars)
3. 微信文章 HTML/CSS 限制
3.1 支持的 HTML 标签
文本与结构:
<p>
, <h1>–<h6>,<span>,<section>,<div>(可能被转换为<p>)<strong>
/ <b>,<em>/<i>,<u>,<br>,<hr><article>
, <aside>,<header>,<footer>,<nav>
链接与媒体:
<img>
(仅 HTTPS,仅网络图片;支持 jpg, png, gif, svg) <a>
(订阅号:仅内部链接;带微信支付的服务号:可外部链接) -
微信专属标签: <mpvoice>(语音消息)、<mpvideo>(视频)
列表:
<ul>
, <ol>,<li>(最多 2 层嵌套)
表格:
<table>
, <thead>,<tbody>,<tfoot>,<tr>,<td>,<th>,<caption>,<col>,<colgroup>-
支持 colspan,rowspan,width,height属性
代码:
<pre>,<code>(基础支持,但有诸多渲染问题)
文本格式:
<del>
, <ins>,<mark>,<small>,<sub>,<sup>,<s><blockquote>
, <address>,<cite>,<abbr>,<q>,<ruby>,<rt><center>
, <font>,<fieldset>,<legend>,<label><dd>
, <dl>,<dt>
3.2 CSS 限制
仅支持内联样式:
-
不支持 <style>标签 -
不支持 <link>外部样式表 -
不支持外部 CSS 文件 -
所有样式必须通过元素的 style属性设置 -
类似邮件 HTML 的渲染方式
支持的 CSS 属性(内联):
-
排版: font-size,font-family,font-weight,color,line-height,letter-spacing,text-align,text-decoration -
盒模型: margin,padding,border,border-radius -
布局: display(有限制),width,max-width,height,overflow -
背景: background-color,background-image(URL 值中的引号会被去除) -
视觉: box-shadow,opacity -
变换: transform(现在可用,以前被去除)
被去除/禁止的 CSS 属性:
position(absolute/relative/fixed)— 上传后被完全移除-
所有 id属性被去除(HTML 和 SVG 均如此) <script>标签完全禁止<iframe>不支持-
百分比单位可能失效(如 transform:translateY(-100%)),使用px,vw,vh替代
其他约束:
<div>可能被转换为<p>存储-
微信会移除多余标签: <p><span>xxx</span></p>变为<p>xxx</p> -
未包裹的文本会自动添加 <p>标签 -
标签前的空格会被移除,使用 进行缩进 -
正文字号建议 15px -
标题通常使用 h2-h4 -
支持 class属性(但id不支持) -
宽度 360px 常用于保证跨设备一致显示
3.3 代码块问题与解决方案
问题:
-
微信自带代码块功能简陋 — 不支持语法高亮 <style>
标签和 CSS 定义会被过滤,保存后样式丢失 -
空格/缩进被移除 — 必须使用 -
保存后换行可能丢失 -
移动端(尤其 iOS)水平滚动可能失效 -
原生不支持语法高亮
解决方案:
-
使用 Markdown 转换工具(Md2All、Doocs-MD)输出内联样式代码 -
使用 highlight.js 预渲染语法高亮 -
Chrome 扩展: crx_wx_code_highlight用于微信平台 -
使用 Carbon 将代码转换为图片
3.4 SVG 限制
-
SVG 内的 <style>,<script>,<a>标签不允许 -
所有 SVG ID 被去除 -
SVG 不能嵌套其他 SVG(图片嵌套现在有限支持:href 必须引用微信素材库) -
iOS 特有问题:动画在重复点击时重新执行; <g>标签中的内联样式在 iOS 上失效
4. 技术选型
4.1 推荐库及版本
|
|
|
|
|
|---|---|---|---|
| httpx |
|
|
|
| feedparser |
|
|
|
| fastfeedparser |
|
|
|
| beautifulsoup4 |
|
|
|
| mistune |
|
|
|
| Jinja2 |
|
|
|
| pydantic-settings |
|
|
|
| APScheduler |
|
|
|
| SQLAlchemy |
|
|
|
| asyncpg |
|
|
|
| psycopg |
|
|
|
| wechatpy |
|
|
|
| anthropic |
|
|
|
| openai |
|
|
|
APScheduler 4.0(alpha)注意:重大重写有破坏性变更 — workers 合并到 schedulers,job executor 概念,仅异步数据存储。生产环境使用 3.11.2。
4.2 Markdown 库对比
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
推荐 mistune:速度优势明显,无依赖,插件系统灵活,可自定义渲染器输出微信兼容 HTML。
5. 项目结构
采用 src layout(PyPA 推荐):
api/├── pyproject.toml├── README.md├── src/│ └── neurapress_cms/│ ├── __init__.py│ ├── config.py # pydantic-settings 配置│ ├── models/ # SQLAlchemy 模型│ │ ├── __init__.py│ │ ├── source.py│ │ ├── material.py│ │ ├── article.py│ │ ├── wechat_account.py│ │ └── publish_record.py│ ├── services/ # 业务逻辑│ │ ├── __init__.py│ │ ├── collector.py # RSS/API 数据采集│ │ ├── ai_generator.py # AI 内容生成│ │ ├── converter.py # Markdown → WeChat HTML│ │ └── publisher.py # 微信 API 交互│ ├── scheduler/ # APScheduler 任务│ │ ├── __init__.py│ │ └── jobs.py│ └── templates/ # Jinja2 HTML 模板│ └── wechat_article.html└── tests/ ├── test_collector.py ├── test_converter.py └── test_publisher.py
为什么选择 src layout:
-
明确可导入代码的位置 -
避免 flat layout 常见的导入问题 -
任何可安装项目或含测试项目的现代标准
pyproject.toml 作为中央配置文件:
[build-system]
— 声明构建后端 [project]
— 基本元数据、依赖 [tool]
— 工具配置(pytest, black, mypy 等) -
替代 setup.py, setup.cfg, requirements.txt
6. 数据库设计
6.1 异步 PostgreSQL 配置
from sqlalchemy.orm import DeclarativeBasefrom sqlalchemy.ext.asyncio import AsyncAttrs, async_sessionmaker, create_async_engineDATABASE_URL = "postgresql+asyncpg://user:password@localhost:5432/neurapress_cms"engine = create_async_engine(url=DATABASE_URL, pool_size=5, max_overflow=10)async_session_maker = async_sessionmaker(engine, expire_on_commit=False)classBase(AsyncAttrs, DeclarativeBase): __abstract__ = True
最佳实践:
-
每个任务一个 AsyncSession -
使用 lazy="raise"防止隐式 I/O(懒加载) -
设置 expire_on_commit=False -
配置连接池: pool_size(默认连接数)和max_overflow(溢出连接数) -
使用 select()风格查询(2.0 模式) -
使用 Alembic 进行数据库迁移 -
开发环境可通过 Docker 运行 PostgreSQL: docker run -d -p 5432:5432 -e POSTGRES_DB=neurapress_cms -e POSTGRES_PASSWORD=password postgres:16
6.2 核心模型
|
|
|
|---|---|
| Source |
|
| Material |
|
| Article |
|
| WeChatAccount |
|
| PublishRecord |
|
7. 开源参考项目
Doocs Markdown Editor(推荐)
- GitHub:
https://github.com/doocs/md - 在线版:
https://md.openwrite.cn/ -
功能:Markdown 转微信 HTML,自定义主题,多图床支持,AI 助手,Docker 部署,npm CLI( @doocs/md-cli)
WenYan (wxmp)
-
多平台适配器(微信、知乎、头条) -
“WenYan MCP Server” 支持 AI 辅助直接发布到微信草稿箱,自动上传图片
wechat-format (lyricat)
- GitHub:
https://github.com/lyricat/wechat-format -
已停止维护,作者推荐 Quaily Markdown Tools
vscode-markdown-to-wechat (xianmin)
- GitHub:
https://github.com/xianmin/vscode-markdown-to-wechat -
VSCode 扩展,使用 remark 库,主题系统,TypeScript
BND-1/wechat_article_skills
-
综合工具包:Markdown 转 HTML、AI 写作助手、草稿发布工具
其他工具
-
Markdown-Weixin: https://md.qikqiak.com/ -
BTool: https://md.btool.cn/ -
Markdown Here 浏览器插件 -
135 编辑器、96 编辑器、秀米
8. 关键风险与注意事项
- 个人订阅号发布限制:未认证个人账号无法调用发布 API(
freepublish/submit),只能创建草稿,最终发布需手动操作 - access_token 管理:必须缓存,频繁获取会触发限流;需实现中央 token 管理服务
- IP 白名单:调用微信 API 前必须配置 IP 白名单,否则返回 40164 错误
- 图片上传必经微信 CDN:所有文章图片必须通过微信 API 上传,外链图片会被过滤
- 内联 CSS 限制:
position
属性被完全移除, id属性被去除,必须使用内联样式 - 代码块渲染:微信原生不支持语法高亮,需使用 highlight.js 预渲染或 Doocs-MD 等工具
- SVG 限制:SVG 内不允许
<style>
, <script>,<a>,所有 ID 被去除 - 群发限制:订阅号每天最多 1 次,每次最多 8 篇;服务号每月最多 4 次
- APScheduler 版本选择:4.0 为 alpha 版本有破坏性变更,生产环境使用 3.11.2
- 百分比 CSS 单位:可能在微信中失效,优先使用
px,vw,vh
夜雨聆风
