
1. 这次想做的事情是什么1.1 痛点手动搬运内容到底有多烦作为一个常年在飞书写稿的人我一直被一个问题卡着稿子写完之后怎么同步到公众号、CSDN、知乎这几个平台。刚开始是复制粘贴后来发现复制粘贴根本不是“复制粘贴”这么简单我在飞书里排得整整齐齐的标题、列表、代码块、表格一到公众号编辑后台就散架了。标题层级丢了列表缩进乱了表格粘贴过去经常变成一堆看不懂的HTML碎片。CSDN倒是支持Markdown但飞书导出的Markdown跟CSDN的编辑器又有兼容性问题最典型的就是图片转存失败飞书图片链接在CSDN上根本加载不出来。知乎就更麻烦直接粘贴飞书的内容格式基本全丢还需要重新配图、重新调整标题层级一次手动搬家快的话二十分钟慢的话四十分钟。这个痛点持续了大概两个月我终于决定做点什么。我的想法很直接既然每次都要做同样的事情不如写一个“内容分发管理器”把飞书当作内容源头一键把文章分发到公众号、CSDN、知乎。当时把这个想法跟几个朋友聊了聊有人觉得可行有人劝我别折腾。我属于那种不撞南墙不回头的人于是就开始动手了。1.2 理想中的自动化流程我最初画了一个非常理想化的流程飞书写文章 → 脚本自动拉取文档内容 → 转换成各平台需要的格式 → 自动发布到三个平台 → 回传发布链接这个流程看起来很顺滑实际操作起来涉及的东西远超预期。首先我要想办法从飞书取到内容然后把飞书的块结构转成通用的Markdown再把Markdown分别转成公众号能接受的HTML、CSDN能直接导入的Markdown以及知乎需要的富文本格式。每个平台的接口各不相同公众号要先去申请开发者权限CSDN需要找到它的发布接口知乎那边则完全是另一个次元的问题。我当时对自动化的理解其实存在一个根本性的假设错误我默认“内容是一样的只是发布衔接不同”。但我后来才意识到内容分发不是转发而是再排版。公众号、CSDN、知乎这三个平台的读者群体不同内容调性、排版风格、图片要求都有差异。同一个标题放公众号可能需要更口语化放CSDN则需要带上明显的技术关键词。这不是一个脚本能解决的问题。1.3 技术方案选型背后的考虑技术选型上我几乎没有犹豫就选了Python。原因很简单生态好第三方SDK齐全不管是飞书还是公众号都有现成的SDK封装省去了自己拼签名、算时间戳的麻烦。Node.js和Go当然也能做但Python在处理文本转换、Markdown解析、HTML生成这块明显更有优势可以说是一抓一大把现成的库。在自动化方式上我考虑过RPA那种模拟鼠标键盘的方式但是很快就放弃了。原因也很现实RPA方案太脆了平台前端一改版脚本就废了而且发布时一旦网络卡顿、弹窗提示、验证码弹出完全没法处理。用API虽然有各种权限限制和渠道门槛至少逻辑是稳定的。所以整体架构就是Python脚本 各平台开放API 中间转格式模块。核心数据格式选Markdown因为从飞书拉下来的内容可以转成Markdown而Markdown再到各平台做渲染理论上最接近我们日常写作习惯。2. 飞书侧接入第一道坎就差点劝退2.1 创建应用和申请权限的细节飞书接入的第一步是在飞书开放平台创建一个企业自建应用。这个流程本身并不难填个应用名称、描述上传个图标就能建起来。但真正的坑在权限申请环节。要读取文档内容我需要申请两个权限一个是docx:document用来读取文档的正文内容另一个是云空间相关的权限用来访问文档所在的文件夹。当时我在开放平台后台申请这些权限提交之后需要管理员在飞书管理后台审批。审批本身等了一天但真正折腾人的是权限范围和版本发布的限制。飞书开放平台的权限设计是按应用版本管理的你修改了权限之后必须发一个新的应用版本审核通过之后权限才真正生效。也就是说我改一次权限就要等一次审核每次审核间隔少则几个小时多则一天。整个调试期间因为权限不足导致的接口报错特别多很多时候明明代码逻辑已经对了还是返回“无权限访问”排查半天才发现是版本没有重新发布。后来我学到一个经验在项目一开始就把所有可能用到的权限一次性申请完不要抱着“用到再申请”的心态。应用权限申请通过后会在开发版本里生效但你调试时会发现当下用的是正式版本还是测试版本这个一定要搞清楚。我就是因为混淆了“开发环境”和“正式环境”的权限白白浪费了一个下午。2.2 飞书文档的导出和读取两条路都走了一遍读取飞书文档内容我当时调研出两条技术路径。第一条路径是使用导出接口把飞书文档导出成Markdown文件然后下载。这个接口的形式是一个异步任务调用之后需要主动查询任务状态等到导出完成再下载文件。听起来很方便但实践后发现导出的Markdown质量并不理想。飞书导出的Markdown保留了文档里大部分内容但会出现很多额外的空行、特殊标记而且表格的转换方式尤其诡异导出的表格在其它平台里经常变成行列错乱的代码块。更致命的是图片没有导出本地文件而是保留了飞书域名的临时链接这些链接放到外部平台要么加载不出来要么过期失效。第二条路径是读取飞书文档的Block块结构。飞书文档本质上是一棵块树的JSON结构标题、段落、列表、代码块、图片、表格每一种内容都对应不同的Block类型。我需要递归遍历所有Block然后根据Block类型自己组装成Markdown。这条路更灵活能完全控制内容输出的格式。但代价是工作量巨大。我当时先试了第一条路径发现质量和预期差距太大于是转到第二条路径结果这一转就让我彻底体会到了什么叫“看似简单实则复杂”。飞书的Block结构里嵌套列表是重点难点。一个列表项下面可能有子列表子列表下还有引用块、代码块这让我必须处理树的递归。我花了一个晚上才写完第一版转换函数调试到凌晨两点看到控制台输出的Markdown基本成型的那一刻还挺有成就感的。2.3 飞书块结构里那些预料之外的坑第一个坑是标题层级。飞书的标题分为标题一、标题二、标题三共三个层级但每个标题Block包含一个子Block真实的标题文本在子Block里转换的时候稍不留神就会输出成普通的段落文本标题层级信息就丢了。更烦人的是从飞书文档复制出来的标题可能会带上自带的序号如果你在飞书文档里用了“1. 2. 3.”这样的自动编号导出之后序号和内容会重复。第二个坑是图片。飞书文档里的图片BlockAlt文本和图注是分开存储的转成Markdown时需要自己拼图片语法以及可选的图注文本。但图片链接本身存放的是临时地址大部分情况下这些链接只在特定时间内有效一旦超过时效分发目标平台就会得到一张裂图。因为这个问题我后来只能单独写一个图片下载模块把飞书图片下载到本地或图床然后在转换的时候替换成新的链接这又是一个额外的工程。第三个坑是思维导图、流程图这类高级块它们在飞书里展示很好看但导出到Markdown之后只剩下一个链接点击链接跳转到飞书页面跟内容分发到外部平台的场景格格不入。我在转换器里对这类特殊块做了注释标记提示自己“这里需要手工处理”结果就是把这个问题延后了而不是解决了。说实话飞书侧接入花费的精力约占整个项目的四成我一度以为熬过了最难的部分后续会顺利很多。但后来的经历很快证明我天真了。3. 公众号发布第二道坎也是第二座山3.1 公众号API的基本约束和权衡公众号的接入方式跟飞书完全不是一个路数。飞书开放平台是面向企业应用的权限和接口都相对透明。公众号虽然也有开发者接口但它在设计上就把“自动发布”这条路卡得很死。我的目标用户是个人订阅号没有支付认证只能申请订阅号。订阅号的接口权限本来就少包括“生成草稿”、“发布接口”在内的一些能力我花了不少时间确认才发现个人主体订阅号部分接口受到限制尤其是主动推送接口基本无缘。还有一个硬约束是即使是已经接入的开发模式调用接口也必须在白名单IP里配置服务器IP这意味着如果脚本跑在本地IP变化一次就要重新配置一次非常痛苦。还有access_token有效期的问题。公众号的access_token有效期是两小时而且获取接口有频率限制。本来以为这不是什么大事但在处理图片素材上传、正文HTML生成、草稿创建这些步骤时如果每个步骤都各自获取一次token很容易中途就把配额耗完而且刷新token的并发问题也会导致后面的请求拿着过期token报错。这个问题的标准做法是用一个全局缓存每次取token时先判断是否过期过期了再刷新。可正因为这个机制我写的第一版代码在调试时频繁报401错误排查了几次才发现是缓存失效逻辑写反了。3.2 草稿箱、图文素材和封面的连环坑公众号发布内容最核心的接口是“新增图文草稿”调用它需要三样东西正文HTML、标题和封面图的media_id。正文要的是HTML这意味着我得把Markdown转成HTML。这一步本身倒不难Python的markdown库就能完成。但公众号编辑器支持的HTML标签是有限集合JavaScript脚本全部不允许部分CSS样式也会被过滤。代码块高亮需要额外引入样式和类名否则发出去代码块就是白底黑字阅读体验很糟糕。表格的HTML需要加上特定样式才能正常显示边框而图片则必须用公众号的域名或者转为material_id形式。也就是说正文里的每一张图都要先通过素材管理接口上传得到media_id再把素材URL放到正文里。这一套流程如果遇到一篇二十多张图的文章处理时间肉眼可见地变长。封面图是另一个隐蔽的坑。封面图在飞书文档里往往就是最上面一张图但公众号的封面要求有大小比例通常建议900x500或者2.35:1的比例。我的脚本里做了自动裁剪但经常把人物脸裁没了或者把代码截图裁掉一半最后只能回到编辑器里手动调整封面。一旦有了手动操作“全自动分发”的愿景就破灭了。3.3 “自动发布”在公众号上很难是真的最让我崩溃的是我整理完所有逻辑、接口也调通之后才发现就算我成功把内容投递到了草稿箱真正的发布动作仍然需要人工到后台点一下“群发”。公众号开放平台确实提供了群发接口但对订阅号来说每天只能群发一次并且群发本身还有原创性校验、敏感词审核、失败后当日不能重发等一系列潜在风险。像我这个项目脚本一天跑一次还是需要有人盯着并不能做到真正的全自动化。到最后我对公众号的目标做了一个降级处理不再追求自动群发只做到“自动生成草稿并填入正文、标题、封面”剩下的点按钮动作由人工完成。这一步虽然缩小了目标但至少让前面写的代码没有完全白费。4. CSDN 与知乎平台差异是终极考验4.1 CSDN接入原本以为最省心结果还是踩坑CSDN是三个平台里最接近开发者习惯的一个因为它原生支持Markdown发布我的飞书转Markdown输出理论上可以直接用它。实践起来CSDN的编辑器对Markdown的方言限制让我重新理解了“兼容”这个词。CSDN的Markdown解析器支持的是CommonMark标准的一个子集但不是完整支持。比如飞书文档里常用的高亮语法CSDN不支持markdown标准的表格语法倒是支持但要求表格前后必须有空行否则不渲染代码块的语言标识符CSDN只认自己列表里的那几十种我传一个rust它也能识别但我传一个text有时候会莫名加上奇怪的类名。最麻烦的还是目录标签CSDN会自动提取你的标题生成文章目录但如果你在文章里手动添加了[TOC]可能造成目录重复。我对每个平台写了一个独立的Markdown预处理函数对语法做裁剪和替换这些工作比预想的琐碎得多。CSDN的图片问题也不容小觑。它的编辑器支持外链图片但会给图片加上水印还有加载限制。更麻烦的是CSDN的图片平台有自己的防盗链策略当你在公众号文章里引用CSDN图片URL时有时候能显示有时候不能完全看对方心情。我在设计时直接做了统一处理所有图片先下载到一个统一图床再往三个平台分发这样至少保证图片不裂。4.2 知乎接入根本没法用常规API玩知乎的接入是压垮我这个项目的一根重要稻草。知乎本身不提供面向个人开发者的内容发布API想发布文章只能模拟网页端的登录会话带着Cookie去请求发布接口。这意味着我做的是一个典型的网页端脚本适配整个方案和飞书、CSDN、公众号的API思路完全不同。网页端接口的每一次请求都要带着正确的Cookie、X-XSRF-Token、User-Agent等一堆请求头有一次没带全知乎直接返回302跳转或者400错误。Cookie的有效期还特别短过了几个小时就要重新登录重新过滑块验证。就算登录状态保持住了知乎的内容发布也不是简单的POST文本。知乎的编辑器用一套自定义JSON结构来表示富文本标题、段落、代码块、图片、引用都有独立的类型我在把Markdown转换到知乎JSON结构时还要费劲理解它的嵌套规则。整个过程下来知乎这一块的代码量占了整个项目近三分之一但是成功率最低经常发布失败后返回一串对我来说毫无意义的错误码。更要命的是知乎发布内容后会触发内容审核和风控。一篇正常的技术文章第一次发布就提示“正在审核请稍后查看”过几个小时再刷新居然被拦截或者判定为疑似营销内容。这种不确定性让“自动化发布到知乎”变成了一场体力活你得盯着状态随时准备重新排版、重新发布。4.3 多平台字段映射配置爆炸的开始如果只有知乎一个平台难搞我还能忍。真正让项目失控的是三个平台之间的字段映射差异。我们在飞书里定义了一篇标准文章包含标题、作者、标签、分类、封面、摘要、原文链接。但公众号需要的是标题、作者在图文消息里、封面素材ID、正文HTML、原创声明CSDN需要的是标题、标签多个用逗号分隔、分类一个、文章类型、Markdown正文知乎需要的是标题、问题绑定或者专栏选择、标签最多三个、富文本JSON。我最初的设计是定义一个大而全的中间格式把飞书内容输出成统一的JSON然后每个平台写一个适配器从JSON里取自己需要的字段。结果适配器越写越多每个适配器里又充满了条件判断如果source这个字段为空就用title的值如果summary为空就从前200个字符截取封面如果是横图公众号那边要裁成竖图。这些规则属于典型的“经验沉淀型”逻辑每一条单独看都对组合在一起就是一个巨型配置系统代码的复杂度肉眼可见地膨胀。我大概写了四个适配器到了第五个终于撑不住了。我开始意识到问题不在于某个平台的接口有多难调而在于我试图用一套脚本去覆盖三种并不完全相同的发布逻辑。5. 失败原因复盘问题远比“技术难度”更深5.1 低估了平台的“内容语义”差异回头来看这个项目失败的根本原因不是飞书接口难调也不是公众号权限卡人而是我低估了“内容分发”这件事本身的复杂度。不同平台对同一篇文章的期望是截然不同的。公众号的文章读者倾向于阅读完整篇幅那么排版需要更注重视觉舒适度段落之间要有呼吸感CSDN的文章读者要的是快速定位技术点所以标题要清晰代码块要突出tab键层级要准确知乎的文章讨论氛围更强开头需要抛出问题或观点否则用户很难有耐心往下看。这些内容语义层面的差异并不是靠“转格式”能解决的。我当时只想着把Markdown转成HTML、把HTML转成知乎JSON却从来没有问自己读者在哪个平台看到这篇文章时内容形态是否符合该平台的语境。等到想明白这一点我已经在适配器里改了一轮又一轮每一个平台的文章都需要人工二次修改自动化成本反而比手动发布更高了。5.2 哪些模块其实没有白做虽然整体项目是失败的但有几个模块沉淀下来在后续其他项目里依然能用。第一个是飞书文档块结构转Markdown的转换器。后续我自己写技术文档时还是先在飞书写再用这个转换器导出到本地Markdown仓库配合Git做版本管理体验比之前手动复制好很多。第二个是图片统一处理模块。飞书文档里的图片会被脚本自动下载并重新上传到我的图床这个流程后来用在了很多其他内容同步任务里。第三个是“平台适配器”的思路。它虽然不适合用于自动化分发但很适合用来做发布检查比如发布前根据各平台规则做格式预检有没有不支持的语法、图片链接是否有效、标签是否超长这比盲发然后等反馈高效得多。5.3 如果重新来一次我会怎么做回看整个历程如果重新动手我先会做单平台闭环而不是一上来就做三平台统一分发。先专注把飞书到公众号这条链路打通把格式转换、图片处理、草稿创建做到稳定中间所有“需要人工点一下”的步骤都明确标记出来。确认单平台跑通了再考虑扩展到CSDN最后才是知乎。横向扩展时不再写一个通用适配器而是每个平台独立写一个小型发布脚本每个脚本只关心自己的平台规则避免被“统一抽象”绑架。这相当于把“内容分发管理器”降级成“内容辅助发布工具箱”。格局小了但每一步都更稳。6. 一些实际操作中值得保留的经验6.1 内容中间格式别贪心够用就行如果你也想做类似的事情我强烈建议不要设计一套大而全的中间格式。只需要定一个最小字段集标题、作者、正文Markdown、封面URL、标签列表、原文链接。正文只用Markdown格式不要既存Markdown又存HTML更不要存JSON。各平台的适配器各自负责把Markdown转成自己需要的格式各平台特有的字段放在适配器内部配置不要塞进中间格式。这套做法的好处是中间格式足够简单任何脚本都能直接处理不会变成一个大而全但谁都看不懂的结构。6.2 平台发布的“人工节点”值得保留我这个项目最大的执念就是全自动后来才意识到很多平台的设计初衷并不希望你全自动。公众号要求人工确认群发知乎要求内容审核CSDN虽然发布方便但也会有反作弊机制。与其费尽心思绕过这些限制不如把精力花在“如何减少人工确认的负担”上。比如公众号做到“脚本生成草稿 手机App推送提醒编辑确认”相比全自动就合理得多也稳定得多。这不是妥协而是对平台规则的理解与尊重。6.3 反过来做内容存档优先经历了这次失败我现在更倾向“反向分发”的做法先在某个主要平台完成内容发布再把已发布内容自动同步回飞书存档。比如先把文章发在CSDN然后用脚本抓取CSDN的已发布内容转成Markdown存进飞书知识库。这样飞书永远是最终归档的地方内容的准确性和完整性都能得到保证而分发端也不需要管各平台的排版问题。对于大多数个人创作者来说这套思路简单、实用、不容易被平台风控针对。最后说一个自己的体会折腾这个项目花了大半个月最后拆掉了很多代码表面看是白忙活。但正是这段失败经历让我真正理解了各平台内容分发与发布机制的设计逻辑。现在每次发布文章我至少能预判哪些环节容易出问题并提前准备好对应方案。如果你也在做类似的事希望这篇总结能帮你少踩几个坑。