我今年连续更新了四十多篇公众号文章,每天最烦的不是写稿,反而是发布那一步:复制标题、贴正文、传封面、填摘要、预览、定时群发,动作不复杂,但磨人。后来我把整套流程接到 OpenClaw 上,配了几个自定义 Skill,让代理自己去公众号后台上操作,实测一篇图文从内容就绪到进入草稿箱只需要两三分钟。这篇文章就把这套方案的架构、Skill 拆分逻辑、完整实现步骤和踩过的坑全部摊开讲一遍,适合日更或周更的内容创作者、给团队搭自动化发布管线的同学,以及对自主代理有兴趣的技术朋友。
1. 为什么要把公众号发布流程自动化
1.1 手动发布一篇图文到底要花多久
很多人觉得发布公众号不就点几下鼠标,能有多慢?我实测过一个比较常见的完整流程:从本地 Markdown 文件开始,先打开公众号后台新建图文,然后复制标题、把正文粘贴进编辑器,重新调一遍格式,接着上传封面图、勾选封面展示、填自定义摘要,最后预览一次,确认排版没乱,再设置定时群发。整个过程最快也要 15 分钟,如果文章里图片多、代码块多、需要反复调整排版,20 分钟都打不住。
这里面真正吃时间的不是点击本身,而是两个隐性成本:一是重复,但凡一周更新三篇以上,同一套机械动作就得反复来,纯体力活;二是低水平错误,标题漏填、摘要超字数、封面尺寸不对、定时时间设错,这些错我全犯过,每次都得重新走流程。对一个人运营账号或者小团队来说,这部分时间占了内容生产总耗时的相当比例,却完全没有创造性价值,是最值得自动化掉的环节。
我测算过自己一个月的数据:40 篇文章,每篇从准备到发布平均 17 分钟,一个月花在发布操作上的时间超过 11 个小时。自动化之后,同样 40 篇,每篇人工介入时间压缩到 2 分钟以内,主要就是看一遍草稿、点确认。省出来的时间当然不会让我多写出几篇爆款,但至少可以把原来对着编辑器发呆的精力挪到选题、标题和正文打磨上。
1.2 OpenClaw 加 Skill:这不是传统脚本
刚开始我也想过直接用一段 Python 脚本搭配浏览器自动化库去点后台,能跑通,但用起来非常别扭。传统脚本是“死”的,所有步骤都写死在代码里:先打开哪儿、点哪个按钮、等多少秒、输入什么内容。一旦公众号后台改版、某个按钮的路径变了、弹窗出现时机不一样,脚本就断给你看,你只能打开日志一行一行排查,维护成本居高不下。
OpenClaw 不太一样的地方在于它本身是个自主代理框架,核心不是替你执行固定步骤,而是能理解任务、拆解步骤、动态决策。我给它下指令“把 articles/ 目录下今天那篇新文章按惯例处理成草稿”,它会自己去读目录、找文件、提取正文、判断用哪个 Skill、按顺序调起来,中途遇到异常还会自己决定重试还是停下等指令。这种感觉就像真正多了个会干活的同事,而不是一段只会念台词的录音。
Skill 则是给代理配的“技能包”,每个技能包解决一类具体问题。比如我分了四个:文章加载、内容格式化、公众号发布、发布后核验。每个 Skill 自带描述文件、参数定义、执行脚本和配置,代理按需加载。好处是模块之间独立,哪个环节出问题就只改哪个包,单个 Skill 也可以复用到别的任务上,比如格式化那个包,我会拿来生成邮件正文。
1.3 哪些场景最适合这套方案
这套方案最适合三类人。第一类是日更或周更的公众号运营者,更新频率越高,省下的时间越可观。第二类是给公司或团队信息中心搭发布流水线的开发者,把发布动作封装成 Skill 后,其他同事只需要把 Markdown 文件丢进指定目录,剩下的事都自动化,不清楚后台操作细节的人也能上线内容。第三类是本身在用 OpenClaw 或其他自主代理框架、想把真实业务流程接进来的技术爱好者,这套发布流程是个很好的落地练手项目。
反过来也有明确不推荐的情况。如果你基本不发内容,一个月憋一篇,那没必要折腾,手动发一下反而是最稳的。如果你打算用自动化做批量注册、批量养号、高频刷量这类事情,我建议趁早放弃这个念头——公众号平台对异常操作看得很紧,自动化高频操作极易触发风控,轻则限制发布,重则封号,这不是技术能绕过去的问题。自动化是帮你把固定工作做规整,不是帮你钻空子。
2. 架构设计与核心思路拆解
2.1 把发布链路拆成四段
我搭建这套系统时,第一步不是写代码,而是把公众号文章发布的完整链路拆开,分成四个边界清晰的阶段。
第一段是内容准备,负责从本地目录读取待发布的 Markdown 文件,解析文件头里的标题、摘要、封面路径、分类这些元信息,同时把正文内容提取出来。第二段是内容格式化,把 Markdown 正文转成适合公众号后台粘贴的 HTML,处理标题层级、代码块、引用、图片引用这些元素,顺便做标题长度截断、摘要字数控制。第三段是公众号操作,这是最核心的一段,负责打开后台、登录、进入图文编辑页、填充内容、上传封面、保存草稿或者直接群发。第四段是发布后核验,发布完自动截个图、检查页面状态、把结果写日志,方便事后追溯。
这四个阶段拆出来之后,每个阶段对应一个独立 Skill,互不依赖。内容准备阶段从什么来源拉取文章,是本地目录、API 还是 RSS,都不影响后面三段。格式化规则改了也只需要动格式化那个 Skill,不会碰发布逻辑。发布失败也只需要看发布 Skill 的日志,不用从头排查。模块化带来的维护便利,在后续页面改版时体会特别深。
2.2 Skill 拆分的两个原则
Skill 怎么拆,直接决定这套系统的可维护性。我自己定了两条原则:第一条是高内聚低耦合,一个 Skill 只做一件事,并且把这件事做完整。比如发布 Skill 内部把“登录”“进编辑页”“填内容”“保存草稿”串在一起,而不是拆成四个微 Skill,因为这几个步骤共享同一个浏览器上下文,拆开反而要到处传回话柄,维护复杂。
第二条是故障隔离。每个 Skill 都要能独立失败、独立重试、独立标记状态。比如格式化 Skill 遇到无法处理的特殊字符,不能让它一路错下去把发布流程也带崩,而是应该抛出明确错误,主控代理记录下来,跳过这篇文章继续下一轮。同样,发布 Skill 遇到后台元素找不到,不能静默失败,要截一张图、写一段日志,再决定是否换策略重试。
按这两条原则拆出来的四个 Skill,我再补充一个比较重要的设计:发布 Skill 里保存草稿和直接群发用的是同一个流程,只是最后一步动作不同。草稿模式是默认的,代理跑完保存草稿就结束,等人工确认后再通过后台手动群发;只有人工显式指定 action=publish 且连续多轮验证稳定后,才会走上自动群发那条路。这个设计演练下来很有必要,相当于给自动化流程加了道保险。
2.3 为什么走浏览器自动化而不是官方接口
聊到自动发布公众号,很多人的第一反应是调官方接口。但现实是,公众号的真正群发接口能力,基本面向完成认证的服务号开放,个人订阅号既没有认证资质,也申请不到相关权限。即便你有接口权限,设置 IP 白名单、获取 access_token、处理素材库上传这些准备工作也是一套工期,对只想省掉重复点击动作的运营者来说,投入产出比并不划算。
浏览器自动化的思路更直接:人是怎么操作的,代理就怎么操作。打开公众号后台、登录、点进图文编辑页、往里填内容——人能点的,自动化都能点,而且不需要额外申请任何接口权限。这套方案的制约也很明显,依赖后台页面结构和元素路径,哪天后台改版了,需要花点时间更新选择器,但公众号后台属于企业级产品,改版频率并不高,维护成本完全可以接受。
我更愿意把官方接口和浏览器自动化看成互补关系而不是二选一。如果哪天公众号能开通发布接口,我大概率会把接口封装成一个新的发布 Skill,替换掉浏览器操作那个包;在接口资质不具备的阶段,浏览器自动化是真正的万能兜底方案。理解了这一点,就明白为什么整套架构要把“公众号操作”封装成 Skill 而不是写死在主流程里——替换实现时,其他部分完全不用动。
3. 实操准备与关键细节
3.1 环境与目录结构
实操层面,我建议从本地环境入手,先把目录结构和依赖准备好。我用 Python 3.10 加浏览器自动化库 Playwright,系统上装了 Chromium 内核。不需要服务器,普通的开发机或办公电脑就能跑,因为发布动作本质上和人工开浏览器一样,需要桌面环境或者虚拟屏幕。
目录结构参考如下:
wechat-auto-pub/ ├── main.py # 主控入口 ├── skills/ │ ├── article_loader/ # 内容读取 Skill │ │ ├── SKILL.md │ │ └── run.py │ ├── formatter/ # 内容格式化 Skill │ │ ├── SKILL.md │ │ └── run.py │ ├── wechat_publisher/ # 公众号发布 Skill │ │ ├── SKILL.md │ │ ├── config.yaml │ │ └── run.py │ └── verifier/ # 发布核验 Skill │ ├── SKILL.md │ └── run.py ├── articles/ # 待发布文章目录 ├── assets/covers/ # 封面图目录 ├── state/ # 登录状态存储 ├── logs/ # 日志目录 └── requirements.txt我最开始没分这么细,所有代码堆在一个文件里,结果改一次格式化逻辑要连带测试整条链路。后来按 Skill 重构后,单测和联调都清晰多了。requirements.txt 里主要就是 playwright、pyyaml、markdown 几个库,很轻量。
3.2 Skill 的格式规范
一个规范的 Skill 必须让代理清楚知道“这个技能是干什么的、需要传什么参数、怎么调用”。我统一使用 SKILL.md 文件描述技能元信息,YAML 头部定义名称、用途、参数和返回值。以公众号发布 Skill 为例:
--- name: wechat_publisher description: 将本地 Markdown 文章自动填充到公众号后台,保存草稿或直接群发 arguments: md_path: type: string required: true description: 待发布文章的 Markdown 路径 action: type: string required: false default: draft enum: [draft, publish] description: 保存为草稿或直接群发 returns: status: type: string description: success 或 failed draft_url: type: string required: false description: 草稿需要人工确认时可返回草稿链接 ---配套的 run.py 负责实际执行,用 argparse 接收参数,最后把结果以 JSON 格式输出。代理读取 SKILL.md 理解用途,调用 run.py 并解析 stdout 的结果来决定下一步动作。这个协议的约定越明确,代理的出错概率越低。
3.3 登录态与账号安全
公众号后台登录绕不开扫码验证。我的做法是首次运行时以有头浏览器模式打开后台,人工扫码登录一次,然后通过 Playwright 的 storage_state 把登录态保存到 state/wechat_state.json 文件。之后的自动化运行直接加载这个状态文件,正常三到五天内不需要重新扫码。
这里必须强调几个安全细节。第一,这个状态文件等价于登录凭证,泄露出去等于把公众号后台交给陌生人,我把它放在单独的 state 目录并加了只读权限。第二,不要在代码里硬编码任何账号密码,更不要明文保存 Cookie,如果用服务器跑,建议给文件做加密或放在受保护的密钥管理环境里。第三,每次自动化发布完,我把新的 storage_state 回写覆盖旧文件,确保登录态始终是最新的。
还有一个经验:有头模式跑半小时以上偶尔会触发后台的异常登录提醒,需要重新扫码,这个属于正常现象。无头模式下遇到扫码弹窗,我会让代理停下来、截图、发通知,等人来处理,而不是让代理自己去尝试破解验证码。自动化要解决的是重复劳动,不是和平台安全机制对抗。
4. 核心实现:内容准备、自动填充与定时发布
4.1 主控流程怎么串起来
主控 main.py 的逻辑比较简单,核心是让代理按顺序调用四个 Skill。我先把文章加载、格式化、发布、核验四个 Skill 串成一段可复用的 Python 函数:
import json import subprocess import sys from pathlib import Path SKILLS_BASE = Path("skills") def run_skill(skill_dir: str, **kwargs): result = subprocess.run( [sys.executable, str(SKILLS_BASE / skill_dir / "run.py")] + [f"--{k}", json.dumps(v) if isinstance(v, (dict, list)) else str(v) for k, v in kwargs.items()], capture_output=True, text=True, encoding="utf-8", ) return json.loads(result.stdout.strip().splitlines()[-1]) def publish_article(md_file: str, action: str = "draft"): article = run_skill("article_loader", md_path=md_file) formatted = run_skill("formatter", article=article) result = run_skill( "wechat_publisher", md_path=md_file, title=formatted["title"], content=formatted["html_content"], cover=article["cover"], digest=formatted["digest"], action=action, ) if result["status"] == "success" and action == "publish": verify = run_skill("verifier", result=result) result["verify"] = verify return result if __name__ == "__main__": target = sys.argv[1] action = sys.argv[2] if len(sys.argv) > 2 else "draft" report = publish_article(target, action) print(json.dumps(report, ensure_ascii=False, indent=2))当然,代理环境下并不是每次都要走这层 Python 封装,OpenClaw 自己也能直接调度 Skill。我保留这段封装的主要原因是方便本地调试和写定时任务,两套入口并存,出问题时不至于连手动跑一下的机会都没有。
4.2 编辑器填充里的三个坑
第一个坑是正文区域在 iframe 里。公众号的图文编辑器正文区不是普通输入框,而是嵌套在 iframe 里面的富文本编辑区,直接 page.fill 没反应。我在代码里用 frame_locator 定位 iframe,再在 frame 内部定位可编辑区,这样才能稳定填入内容。不同内核版本的编辑器结构略有差异,需要按下 F12 自己确认选择器。
第二个坑是富文本样式剥离。直接往编辑器粘贴带大量内联 style 的内容,后台会自动做一轮样式清理,很多效果会丢失。我把 Markdown 转 HTML 之后,只保留 h2、h3、p、pre、blockquote、img 这类基础标签,样式尽量不写,让公众号后台自己的排版体系接管。做这套格式化时,我的原则是“结构干净优先,视觉细节靠后台默认样式”。
第三个坑是标题和摘要的硬性限制。标题最长 64 字,超出部分在列表页会被截断,所以在格式化阶段统一做截断,避免代理把超长标题填进去。自定义摘要限制 120 字,超过也会提示,我在 formatter 里就把摘要按字符数切好,宁可短一点也别超。这几个硬限制是公众号后台一直没变的规则,写死在格式化 Skill 中最省心。
4.3 封面图和素材库处理
封面图是发布流程里最容易翻车的一环。公众号后台对封面的比例和文件大小都有要求,建议长图用 900 乘 383 的尺寸,小图用 400 乘 400 比例,单张图片大小不要超过 10 兆。我维护了一个 assets/covers 目录,每篇文章命名成日期加标题关键词的形式,方便 article_loader 解析文件头时直接引用。
发布 Skill 处理封面有两种路径:一是本地文件直接上传,这适合每篇新配一张封面的情况;二是从素材库选,适合团队统一视觉物料、反复用同一套图的情况。我目前用的是第一种,代码里通过文件上传控件把封面传上去,再在编辑器里勾选“封面和全文”展示方式。第二种路径需要额外维护素材库的命名规则,不建议刚开始就做,先把基础流程跑通再说。
4.4 定时任务与发布节奏控制
定时发布我这边分两个层级。第一层是系统层定时触发 main.py,Windows 下用计划任务,Linux 下用 cron,每天固定时间执行一次,把当天文章处理成草稿。第二层是公众号后台自身的定时群发功能,草稿准备好之后,人工看好时间再设置定时发布。这么设计是因为后台的定时群发比脚本等待更稳定,同时也强制保留了一个人工确认节点。
实际节奏上,我建议刚开始自动化时,前两周只跑草稿模式,每天生成草稿后人工进后台看一眼再发送。等确认格式化规则稳定、封面路径没问题、登录态不频繁失效,再打开定时任务跑自动群发。我见过有人第一天就把全自动开满,结果封面选错文件名、正文全是乱码,连发三篇才发现,账号阅读数据和口碑都受影响了。自动化发布是一场长跑,先稳后快才是正路。
5. 三大高频故障排查实录
5.1 登录态失效与滑块验证
用了一个月之后,我碰到最频繁的故障就是登录态失效。后台偶尔会弹出“环境异常”或者直接跳转登录页,加载 storage_state 也救不回来,因为失效的不是本地 Cookie,而是后台判定需要重新验证身份。遇上这种情况,无头模式跑多少遍都是白搭,排查方式很简单:看截图,页面停在登录界面,那基本就是需要人工扫码了。
我的处理流程分三步:代理检测到登录页元素出现就停止后续动作,截一张全屏图存档,然后给日志写一条明确错误。随后我把对应通知发到群里,人来了以后用有头模式打开后台、扫码、重新保存状态文件,再重新跑一次草稿流程。整套流程从发现到恢复,熟练之后五分钟内能搞定,损失最多就是当天的草稿晚一点生成。
有段时间登录态频繁失效,几乎每天都要手扫一次,后来发现是我把登录状态保存在服务器上,而服务器出口 IP 一直变,触发了后台的安全策略。换成固定出口后,一周多都没再需要扫码。排查这类问题的思路是:先看是不是你自己的环境特征不稳定,再考虑账号本身有没有异常,不要一上来就怪自动化。
5.2 群发失败后的草稿回退
自动群发出错是比登录失效更棘手的问题,因为群发是有真实影响的动作。我遇到过标题超过限制导致保存失败、图片上传中途断掉导致正文里缺图、甚至后台升级页面元素路径失效。这些情况如果代理傻傻地重试三次还是失败,极有可能造成半成品内容直接发出去。
所以发布 Skill 内置了一个兜底逻辑:所有动作默认优先保存草稿,只有在确认草稿正常且 action 指定 publish 时才执行群发。群发那一步再单独包一层 try 块,任何异常都先自动保存草稿,再抛出错误,绝不让“群发”这一步在半成功状态下继续。这套逻辑跑下来,最差的结果是草稿箱里多了一篇需要人工检查的文章,而不是读者收到一篇排版错乱的内容。
关于重试,我也做了次数限制。同一篇内容最多重试两次,两次失败就直接切换人工确认流程。日志里会记录每次重试的原因、耗时和截图路径,方便回看。自动流程容忍偶发故障,但要坚决防止同一个已知故障反复消耗资源,这是自动化工程里很重要的一条经验。
5.3 内容异常:排版丢失和怪字符
第三类高频问题出现在内容格式化这层。Markdown 转 HTML 再填进编辑器的过程中,我踩到过几个具体坑:正文里出现的全角空格在公众号编辑器里会被渲染成一堆占位符;某些版本的编辑器遇到连续换行会把行间距压缩掉;代码块里的尖括号偶尔被解析成 HTML 标签直接吞掉。
解决方案比较粗暴但有效:格式化 Skill 在输出前统一做一遍字符清洗。全角空格转成普通空格或删除,连续换行收敛成段落标签,代码块内容统一转义后再包进 pre 标签。另外,封面图 alt 文本和图片文件名里也不要放特殊符号,公众号后台对中文文件名兼容性尚可,但遇到“&”“?”这类字符偶尔会抽风,我尽量用纯字母数字加下划线命名。
整理下来,我把高频问题整理成了一个速查表,放在项目 README 里,每次出问题先查表:
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 打开页面停在登录页 | 登录态失效或环境异常 | 有头模式人工扫码,更新 state 文件 |
| 标题或摘要保存报错 | 超过后台长度限制 | 在 formatter 里提前截断 |
| 正文内容为空 | iframe 定位失效 | 重新获取编辑器元素路径 |
| 图片上传失败 | 文件过大或命名特殊字符 | 压缩图片、统一规范命名 |
| 定时任务没触发 | cron 环境变量缺失 | 脚本内指定 Python 绝对路径 |
| 发布后缺图 | 正文中的图片是外部链接 | 先将图片下载到本地再上传 |
6. 自动化的底线:合规、账号防封与长期维护
6.1 平台规则红线一票否决
自动化发布节约的是时间,不是内容审核的流程。公众号平台对内容有明确底线,诱导分享、标题党、虚假宣传、洗稿抄袭这些行为,不会因为发布方式变自动就网开一面,反而自动化高频操作更容易引起系统注意。我给自己定的规矩是:每篇发布前必须有人工审核环节,哪怕只是扫一眼草稿,也不能让机器当唯一的内容把关人。
频率也要控制。我见过有人把自动发布频率设到一天三更,实际上内容储备和选题根本跟不上,阅读数据一塌糊涂不说,后台还会因为频繁群发触发平台提醒。公众号是内容产品,读者之所以愿意看,是因为内容本身有价值,自动化的作用是让你把省下的精力投向内容质量,而不是让低质内容刷得更快。
6.2 账号安全与敏感信息管理
自动化脚本里必然涉及登录态和可能的后台配置信息,这些信息要按敏感资料对待。我的具体要求是:登录状态文件不提交到代码仓库,不通过聊天工具明文传输;服务器上设置独立目录并做最小权限;所有日志禁止打印完整的 Cookie 内容。如果多人协作,建议每个人维护自己的本地登录态,而不是共享同一个 state 文件。
另外一个容易被忽略的细节是浏览器内核的缓存文件。自动化跑多了,本地会积累不少临时文件,里面可能包含页面渲染数据。定期清理这套临时目录,尤其是在调试完有头模式之后,看起来是小习惯,但安全无小事。账号出现问题再想起来处理,代价往往比预想的大得多。
6.3 长期维护:页面改版与 Skill 版本管理
任何自动化方案都逃不开“依赖的页面改版”这一天。公众号后台虽然不是隔三差五大改,但按钮位置、编辑器结构变化是常态。我养成了一个习惯:每个月抽半天,手动打开后台对照一遍自动化检查清单,确认主要元素路径没变。一旦发现某个选择器失效,立刻更新到对应 Skill 里,并写一条变更日志。
Skill 的版本管理我建议纳入代码仓库,每个 Skill 单独记录改动历史。改格式化规则、调整封面路径、更新选择器,这些都要有据可查,否则两三个月后再看代码,根本说不清当时为什么那么改。我自己吃过这个亏:一度为了赶发布日期临时改过摘要截断长度,事后忘记记录,后来有人问我为什么有的摘要长有的短,我完全答不上来。从那以后,Skill 更新必须写 commit message,这是底线。
最后再分享一点运营层面的体会:自动化发布带来最大的收益不是把发布时间从 20 分钟压到 2 分钟,而是你不再被发布动作拖着走。以前每天发完稿,精力已经消耗了一半,根本没心思复盘阅读数据;现在草稿自动生成,我只需要定时看一眼后台,剩余精力能放在选题和话题打磨上。按我个人经验,第一次搭这套系统最关键的提醒就是:别急着全自动,先把草稿模式跑稳,再加群发,再上定时任务,一步一步来,后面会省掉很多不必要的事故处理时间。