写技术博客的人多半都经历过这个循环:文章在本地 Markdown 里写得干干净净,往平台编辑器里一贴,图片全裂了。CSDN 对站外图有防盗链,<img> 直连外链图床返回的是 HTTP 200 + 0 字节——状态码挺健康,页面上只剩一个占位框。于是下载、上传、替换链接、重查排版,文章五分钟写完,图片折腾半小时。
这还只是一个平台。等你开始"一稿多投",CSDN、知乎、掘金、开源中国、百家号、博客园、腾讯云开发者社区、语雀、头条号、SegmentFault 思否,每家编辑器各有各的脾气:有的要传它自己的图床,有的悄悄剥掉站外图,有的接口必须带签名,有的不开浏览器根本发不出去。十个平台贴十遍,一个晚上就没了。
所以我把这件事做成了一个命令行工具:cspub。用华为仓颉写的,主链路零第三方依赖。
cspub publish 我的文章.md --platform csdn # CSDN(Cookie + 网关签名,默认发草稿)
cspub publish 我的文章.md --platform zhihu # 知乎专栏(Cookie 登录态,图片自动转知乎 CDN)
cspub publish 我的文章.md --platform baijiahao # 百家号(Cookie 浏览器通道 / 开发者凭证)
cspub publish 我的文章.md --platform cnblogs # 博客园(官方 MetaWeblog API,纯 HTTP)
cspub publish 我的文章.md --platform juejin # 掘金(Cookie 两步流;图片外链优先,AtomGit 图床)
cspub publish 我的文章.md --platform oschina # 开源中国(Cookie 直通站点接口;图片走它自带图床)
cspub publish 我的文章.md --platform tencent # 腾讯云开发者社区(Cookie 零签名)
cspub publish 我的文章.md --platform yuque # 语雀(官方 OpenAPI v2 + Token)
cspub publish 我的文章.md --platform toutiao # 头条号(Cookie + X-CSRFToken)
cspub publish 我的文章.md --platform segmentfault # SegmentFault 思否(/gateway 网关 + 页面取 Token)
cspub publish 我的文章.md --live # 加 --live 直接发布,不带就是发草稿
下面把它怎么用、背后做了什么、以及哪些坑你得提前知道,一次说清楚。

一条命令背后是什么
publish 不是简单地把文件 POST 出去,背后是一条六个环节的流水线:
- 读 Markdown + front-matter(title / tags / categories / desc / draft / origin_url);
- 按平台自动转存图片——CSDN 外链图转 CSDN 图床(防盗链),知乎图转知乎 CDN(知乎会剥离站外图),头条号强制全量转存,开源中国用它自带图床;
- 把 Markdown 渲染成 HTML——各平台编辑器收的都是渲染后的 HTML,不是 Markdown 源文,直接发原文页面会显示字面
##和![](); - 按平台鉴权发文:CSDN 走阿里云 API 网关风格的 HMAC-SHA256 签名(
x-ca-*请求头)+ Cookie,知乎走 Cookie 草稿接口三步流,语雀走官方 OpenAPI v2,博客园走 MetaWeblog,SegmentFault 走/gateway网关 + 从写文章页实时取 Token; - 回显文章链接和 id(CSDN 支持用
--update <id>原地更新)。
主链路全部用仓颉标准库 + stdx 实现——HMAC、Base64、HTTP、JSON 这些 stdx 恰好齐活,没有依赖树要维护,cjpm build 出来就是一个可执行文件。唯一的例外是百家号 Cookie 通道,需要一个 Playwright 伴生脚本(原因后面讲)。
装它:从源码构建
需要仓颉 SDK 1.2.0 + 配套 stdx:
git clone https://atomgit.com/qq8864/cspub.git
cd cspub
export CANGJIE_STDX=<你的 stdx>/dynamic/stdx
export PATH=$CANGJIE_HOME/bin:$CANGJIE_HOME/tools/bin:$PATH
cjpm build
# 产物:target/release/bin/main
直接跑二进制报 libstdx.encoding.json.so 缺失的话,补一下动态库路径就好,不用重编译:
export LD_LIBRARY_PATH=$CANGJIE_STDX:$CANGJIE_HOME/runtime/lib/linux_x86_64_cjnative:$CANGJIE_HOME/third_party/llvm/lib
Windows 侧仓库里有 ./scripts/winbuild.sh 交叉编译,产出 exe 加一个解压即用的自包含 zip。
配凭证:唯一麻烦的一步,我帮你省到点三下
十个平台的鉴权方式没有一个是统一的,这是整件事最烦的部分,我没法消除它,只能帮你省事。
先说结论:用 cookie-export 提取,别 F12 手抄。
登录态里最关键的那几个字段——知乎的 z_c0、掘金的 sessionid、CSDN 的 UserToken——多半是 httpOnly,document.cookie 根本读不到;F12 手抄还容易漏字段、少分号,抄完发现 401,再回去猜哪个字段错了。

cookie-export 是我在 cj-tauri 仓库里写的桌面小工具,流程就三步:内嵌浏览器登录一次 → 点提取(按 cspub 各平台的字段硬需求逐项校验,缺哪个直接点出来,比如「已提取,但缺必需字段:UserToken」)→ 点导出,写进 ~/.cspub/config.json(chmod 600,合并写,不会覆盖其他平台的凭证)。全程本机运行,不上报任何 cookie。
不想用工具也行,环境变量和 config.json 二选一,环境变量优先:
export CSPUB_CSDN_COOKIE='<浏览器 F12 复制的 Cookie 整串>'
export CSPUB_ZHIHU_COOKIE='<须含 z_c0>'
export CSPUB_JUEJIN_COOKIE='<须含 sessionid>'
export CSPUB_OSCHINA_COOKIE='<须含 oscid>'
cspub whoami csdn # 校验凭证是否就位(只验格式不联网)
写进 ~/.bashrc 时 Cookie 整串务必用单引号包裹(里面有分号和空格)。几个特例记一下:
- 博客园不用 cookie,走官方 MetaWeblog:后台开启「允许博客客户端访问」,拿博客名、登录名和访问令牌(不是登录密码),填
CSPUB_CNBLOGS_BLOG_ID / USERNAME / TOKEN三个变量; - 语雀也走官方 Token:去设置页生成,namespace 取知识库 URL 的两段(
user/repo,写一段一定 404); - 腾讯云的
skey里的*原样保留,不要 URL 编码;tencentTagIds必填,空了接口回-9993,工具会本地先拦下; - 掘金想要最顺的图片链路,配一个
CSPUB_ATOMGIT_TOKEN(AtomGit 个人访问令牌),本地图自动传 AtomGit 图床、正文嵌外链,不再依赖浏览器。
一篇文章长什么样
front-matter 就五个字段,都很直白:
---
title: 文章标题 # 必填
tags: 仓颉, 工具 # 逗号/顿号分隔,最多 5 个(API 上限)
categories: 编程
desc: 摘要一行 # 不写就从正文剥 Markdown 取前 200 字
draft: true # 缺省 true;publish 加 --live 可覆盖
---
正文 Markdown……
发文前先预演:check / html / imgs
这三个命令都不联网,是我的"发文前安检":
cspub check 文章.md # 解析 front-matter + 渲染统计(标题数、图片数、代码块行数)
cspub html 文章.md # 打印渲染后的 HTML,肉眼确认排版
cspub imgs 文章.md # 预演图片去向:rehost / skip / BLOCKED
imgs 值得单独说。它会列出正文里每张图会被如何处理:rehost(要转存)、skip(平台图床或 data:,跳过)、BLOCKED(内网/回环地址,拦截)。发出去才发现图裂了,和发之前就知道,是两种完全不同的心情。
然后就一条命令:
cspub publish 文章.md --platform csdn # 默认发草稿
cspub publish 文章.md --platform csdn --live # 直接发布
cspub publish 文章.md --platform csdn --update <id> # CSDN 原地更新
注意 --platform 要放在子命令后面,写前面会报「未知命令或参数不足」。另外建议默认走草稿——先去后台看一眼排版,没问题再 --live。
十个平台的脾气
同样是"发文章",十家的实现路径完全不同。这张表是实测出来的,发之前值得扫一眼:
| 平台 | 鉴权方式 | 图片策略 | 你要知道的脾气 |
|---|---|---|---|
| CSDN | Cookie + HMAC-SHA256 网关签名 | 外链图强制转 CSDN 图床 | 站外图直连 200+0 字节;每天限 1 篇(草稿同算) |
| 知乎 | Cookie 三步流 | 转知乎 CDN | 知乎直接剥离站外图,不转存就是裂图 |
| 掘金 | Cookie 两步流 | 外链优先(AtomGit 图床) | 接受外链图,配了 AtomGit Token 全程不用浏览器 |
| 开源中国 | Cookie 直通站点接口 | 它自带图床 | 正文提交 Markdown 服务端自己转,别预渲染;tags 平台自动提取 |
| 百家号 | Cookie 浏览器通道 / 开发者凭证 | 伴生脚本处理 | 唯一必须开浏览器的:React 发布按钮需完整事件链,直调 API 缺 CSRF 必 500 |
| 博客园 | MetaWeblog 访问令牌 | 接口原生处理 | 最省心的一家,纯 HTTP,零浏览器 |
| 腾讯云 | 纯 Cookie 零签名 | 转腾讯云图床 | 字段语义全是坑:sourceType 必须 1,原创不能带 sourceDetail,专栏必须是自己已加入的 |
| 语雀 | OpenAPI v2 + Token | Markdown 原样提交 | 少数真有官方 API 的,反而最省事 |
| 头条号 | Cookie + X-CSRFToken | 强制全量转存头条 CDN | 任何非头条域名图都让整篇被拒(7115 图片uri非法);标题 5~30 字 |
| SegmentFault | Cookie + 页面实时取 Token | 需公网 URL | Token 从写文章页 __NEXT_DATA__ 现取,不用手工填 |
有意思的规律是:越是"大厂"的平台,接口越反直觉。CSDN 的签名看着唬人,其实逻辑很干净;百家号反过来,接口看着简单,实际上非走浏览器不可。
图片:真正费劲的地方,工具替你扛了
如果说发文接口是"一个平台一套鉴权",图片就是"一个平台一套生态"。cspub 的策略是按平台分流,但底线是共用的:
- 内网、回环、链路本地地址直接拦截——防止把本机服务当图源发出去;
- 下载外链图拒绝 30x 重定向——重定向可能把请求带到没检查过的主机;
- 单张上限 12 MiB;
- best-effort:单张失败保留原链接并打
⚠⚠提示,不阻断发文。写文章已经够累了,不该因为图床抽风丢一次草稿。
想要更严格,设 CSPUB_STRICT_IMAGES=1,转存后仍有未处理的本地图就中止发布(返回码 1)。
小技巧:正文里的图片写本机绝对路径最稳(跳过下载直接上图床);相对路径按当前工作目录解析。代码围栏里的 ![]() 是示例文本,自动跳过。
排查发文链路问题先开 CSPUB_DEBUG=1,stderr 会打请求体积、上游状态码和响应首段。
三个踩过的坑,值得单独说
其一:JSON 字段必须按类型取。 CSDN 图床签名响应里的 customParam.isAudit 是 JSON 数字,我用只认字符串的函数去取拿到空串,把空的 x:isAudit 发给华为 OBS,OBS 回的是 HTTP 200 + 0 字节——看起来像请求被网关吞了,其实是回调拿不到字段。改法是按 JsonKind 分派取值,数字、字符串、布尔各走各的。这个坑教育我:"HTTP 200"和"成功"之间可以隔着一整个空文件。
其二:UTF-8 截断不能按字节切。 摘要截断必须判断首字节 (b & 0xC0) != 0x80 才算字符边界,否则把汉字切成半个,文章里一串乱码。行内渲染同理,要按区间整体转码,逐字节 append 会把汉字拆成非法序列。
其三:CSDN 的证书链很怪。 它的根是 Certum,一个小众 CA。实测仓颉 stdx 的 TLS 在两代版本上、默认模式和 CustomCA 模式(内嵌逐字节一致的 Certum 根)都报 unknown CA,而系统 curl 验同一条链是通过的——怀疑和 stdx 动态加载 OpenSSL 的行为有关。所以我留了 --insecure(TrustAll)逃生口,但正式发文请先试默认模式,失败再加,别默认带上。
这些坑在仓库 docs/开发文档与踩坑记录.md 里都有完整的"现象 → 排查铁证 → 修复方案 → 复用配方"记录,另外还有两份按症状索引的 skill 文档:cspub-publish(操作手册)和 cspub-troubleshoot(踩坑速查)。
已知限制,先告诉你
- 表格、脚注、任务列表这些扩展 Markdown 不渲染(原样进段落),内置渲染器只覆盖标题/段落/列表/引用/代码围栏/图片/链接/行内代码/加粗——拿不准就先
cspub html目验; --update只有 CSDN 支持,其他平台改内容得删了重发;- 开源中国"发布成功 ≠ 立刻公开":接口回了 id,公开页可能还在审核(新号首篇大概率人工审核),而且它的 SPA 外壳对任意 id 都回 200,curl 判不出文章死活,要用浏览器渲染判断;
- CSDN 每日 1 篇的限额是平台规则不是工具 bug,错误码
400300012出现就换自然日; - Windows 只验证了交叉编译产物,发文链路没在真机 Windows 上跑过。
最后
cspub 对我来说不只是个效率工具,它也是个用仓颉练手网络编程的真实项目——从 HMAC 签名逐字节对账、TLS 信任、UTF-8 字节语义,到十套平台鉴权,每一处都是真刀真枪踩出来的。开发过程本身是在我的另一个项目 cjh(一个用仓颉写的终端 Coding Agent)里完成的,工具先给自己用上了。
如果你也在一稿多投,欢迎拿去用、提 issue,或者把某个平台的适配改得更顺手。
相关链接:
- 官网(平台矩阵、流水线图、实测数据):yangyongzhen.github.io/cspub/
- 主仓(AtomGit):atomgit.com/qq8864/cspu…
- GitHub 镜像:github.com/yangyongzhe…
- 凭证提取工具 cookie-export:atomgit.com/qq8864/cj-t…
许可证 MIT,随手拿去用。