从零开发一个掘金自动发布 Skill,并上架 Clawhub
本文记录了一次完整的 Skill 开发旅程:从一句「帮我创建一个可以自动发布文章到掘金的 skill」开始,到最终成功上架 Clawhub,全程真实还原每一个关键决策和踩坑过程。
背景:为什么要做这个 Skill?
我日常运营一个 AI 资讯账号,每天需要将 Markdown 格式的文章发布到多个平台,包括微信公众号、小红书、掘金等。其中微信公众号和小红书已经有现成的 Skill 可以用,但掘金没有。
每次发布掘金都要:
- 打开浏览器,登录掘金
- 进入编辑器,粘贴 Markdown 内容
- 手动设置分类、标签、摘要、封面
- 点击发布
这套流程重复且机械,非常适合自动化。于是决定自己动手,开发一个 juejin-publisher Skill。
第一步:研究掘金 API
开发自动发布工具,首先要搞清楚掘金的发布接口。掘金没有公开的开发者 API 文档,但可以通过浏览器抓包来分析。
抓包分析
打开掘金编辑器,按 F12 进入开发者工具,切换到 Network 标签,然后正常发布一篇文章,观察请求:
发现关键的两个接口:
① 创建草稿
1POST https://api.juejin.cn/content_api/v1/article_draft/create 2
请求体(JSON):
1{ 2 "category_id": "6809637773935378440", 3 "tag_ids": ["6809640445233070098"], 4 "title": "文章标题", 5 "brief_content": "摘要,50-100字", 6 "edit_type": 10, 7 "mark_content": "# Markdown 正文...", 8 "cover_image": "", 9 "html_content": "deprecated", 10 "link_url": "", 11 "theme_ids": [] 12} 13
② 发布草稿
1POST https://api.juejin.cn/content_api/v1/article/publish 2
请求体:
1{ 2 "draft_id": "7xxxxxxxxxxxxxx", 3 "sync_to_org": false, 4 "column_ids": [], 5 "theme_ids": [] 6} 7
鉴权方式
掘金使用 Cookie 鉴权,只需在请求头中带上登录后的 Cookie 即可。Cookie 有效期约 30 天,其中最关键的字段是:
1sessionid=69c4b5312172d146beea98ddfabf5bd6 2
注意事项
edit_type: 10代表 Markdown 模式(富文本模式为0)brief_content必须在 50-100 字之间,否则接口报错tag_ids是数组,category_id是字符串
第二步:设计 Skill 结构
参考已有的 wechat-mp-publisher Skill 的目录规范,设计如下结构:
1skills/juejin-publisher/ 2├── SKILL.md # Skill 主文档(含 frontmatter 元数据) 3├── README.md # 快速说明 4├── _meta.json # 本地元数据 5├── example.md # 文章格式示例 6├── juejin.env.example # 配置文件模板 7├── scripts/ 8│ ├── publish.py # 核心发布脚本 9│ └── query_tags.py # 标签 ID 查询工具 10└── references/ 11 ├── category_ids.md # 常用分类 ID 参考 12 └── tag_ids.md # 常用标签 ID 参考 13
设计原则:
- 核心逻辑用 Python 标准库实现,零依赖安装
- 支持 Markdown frontmatter,让文章自带元数据
- 提供 配置文件模板,降低上手门槛
- 附带 标签查询工具,解决 tag_id 难以记忆的问题
第三步:编写核心发布脚本
3.1 配置加载
从 juejin.env 文件读取 Cookie 和默认配置:
1def load_config(): 2 """从 juejin.env 加载配置""" 3 config = {} 4 with open(CONFIG_FILE, "r", encoding="utf-8") as f: 5 for line in f: 6 line = line.strip() 7 if line.startswith("#") or "=" not in line: 8 continue 9 # 支持 export KEY="VALUE" 和 KEY="VALUE" 两种格式 10 line = line.removeprefix("export").strip() 11 key, _, val = line.partition("=") 12 val = val.strip().strip('"').strip("'") 13 config[key.strip()] = val 14 return config 15
3.2 Markdown 解析
支持从文章头部的 YAML frontmatter 中读取标题、摘要、封面、分类、标签:
1def parse_markdown(filepath): 2 """解析 Markdown 文件,提取 frontmatter 和正文""" 3 with open(filepath, "r", encoding="utf-8") as f: 4 content = f.read() 5 6 meta = {} 7 body = content 8 9 # 提取 YAML frontmatter 10 fm_match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL) 11 if fm_match: 12 fm_text = fm_match.group(1) 13 body = content[fm_match.end():] 14 for line in fm_text.splitlines(): 15 if ":" in line: 16 k, _, v = line.partition(":") 17 meta[k.strip()] = v.strip().strip('"').strip("'") 18 19 # 从正文提取标题(取第一个 # 标题) 20 if "title" not in meta: 21 title_match = re.search(r"^#\s+(.+)$", body, re.MULTILINE) 22 if title_match: 23 meta["title"] = title_match.group(1).strip() 24 25 return meta, body.strip() 26
3.3 自动生成摘要
掘金要求摘要 50-100 字,脚本自动处理:
1def generate_brief(meta, body, min_len=50, max_len=100): 2 """生成符合掘金要求的摘要(50-100字)""" 3 if "description" in meta: 4 brief = meta["description"] 5 if min_len <= len(brief) <= max_len: 6 return brief 7 if len(brief) > max_len: 8 return brief[:max_len] 9 10 # 从正文提取纯文本生成摘要 11 plain = re.sub(r"```.*?```", "", body, flags=re.DOTALL) 12 plain = re.sub(r"[#*`>\[\]!]", "", plain) 13 plain = re.sub(r"\s+", " ", plain).strip() 14 return plain[:max_len] 15
3.4 API 调用封装
使用 Python 内置的 urllib 实现 HTTP 请求,无需安装 requests:
1def api_post(path, data, cookie): 2 """发送 POST 请求到掘金 API""" 3 url = f"https://api.juejin.cn{path}" 4 payload = json.dumps(data).encode("utf-8") 5 headers = { 6 "Content-Type": "application/json; charset=utf-8", 7 "Cookie": cookie, 8 "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...", 9 "Referer": "https://juejin.cn/", 10 "Origin": "https://juejin.cn", 11 } 12 req = urllib.request.Request(url, data=payload, headers=headers, method="POST") 13 with urllib.request.urlopen(req, timeout=30) as resp: 14 return json.loads(resp.read().decode("utf-8")) 15
3.5 完整发布流程
1def main(): 2 # 1. 解析命令行参数 3 args = parse_args() 4 5 # 2. 加载 Cookie 配置 6 config = load_config() 7 cookie = config["JUEJIN_COOKIE"] 8 9 # 3. 解析 Markdown 文件 10 meta, body = parse_markdown(args.file) 11 title = meta.get("title", "无标题") 12 brief = generate_brief(meta, body) 13 14 # 4. 创建草稿 15 draft_id = create_draft(title, body, brief, category_id, tag_ids, cover_image, cookie) 16 17 # 5. 发布(除非指定 --draft-only) 18 if not args.draft_only: 19 article_id = publish_draft(draft_id, cookie) 20 print(f"🎉 文章已发布: https://juejin.cn/post/{article_id}") 21
使用示例
Markdown 文章格式(支持 frontmatter):
1--- 2title: 我的技术文章标题 3description: 这里是文章摘要,建议 50-100 字,会显示在文章列表中 4cover: https://example.com/cover.jpg 5category_id: "6809637773935378440" 6tag_ids: "6809640445233070098,6809640408797167623" 7--- 8 9# 正文内容开始 10 11这里是 Markdown 正文... 12
命令行调用:
1# 基本发布 2python3 scripts/publish.py article.md 3 4# 指定分类和标签 5python3 scripts/publish.py article.md \ 6 --category "6809637773935378440" \ 7 --tags "6809640445233070098" 8 9# 仅创建草稿 10python3 scripts/publish.py article.md --draft-only 11
第四步:编写 SKILL.md 规范文件
Skill 的核心是 SKILL.md,它既是使用文档,也是 AI 理解该 Skill 的"说明书"。文件头部需要包含 frontmatter 元数据:
1--- 2name: juejin-publisher 3version: 1.0.0 4license: MIT 5description: 掘金文章自动发布技能。通过掘金官方 API(Cookie 鉴权), 6 支持将 Markdown 文章一键发布到稀土掘金平台,支持设置分类、标签、摘要和封面图。 7metadata: 8 openclaw: 9 emoji: "⛏️" 10 category: publishing 11 clawdbot: 12 emoji: "⛏️" 13 requires: 14 bins: ["python3", "curl"] 15 install: [] 16--- 17
关键字段说明:
name:Skill 的唯一标识,发布到 Clawhub 后即为 sluglicense:必填,Clawhub 发布时服务端会校验此字段(踩坑记录见下方)metadata.clawdbot.requires.bins:声明运行时依赖的系统命令
第五步:发布到 Clawhub
5.1 安装 clawhub CLI
1npm install -g clawhub 2
安装完成后验证:
1clawhub --version 2# 0.7.0 3
5.2 登录
由于是在无 UI 的 Linux 服务器上操作,使用 Token 登录模式:
1clawhub login --token clh_xxxxxxxxxxxxxxxxxx 2
Token 获取方式:登录 clawhub.ai → Settings → API Tokens → 创建新 Token。
5.3 发布 Skill
1clawhub publish /data/workspace/skills/juejin-publisher --version 1.0.0 2
5.4 踩坑:acceptLicenseTerms 报错
第一次发布时遇到报错:
1Error: acceptLicenseTerms is required 2
原因:Clawhub 服务端要求 payload 中包含 acceptLicenseTerms: true 字段,但 CLI(v0.7.0)的 publish.js 脚本并没有在 payload 中传递这个字段。
解决方案: 修改 CLI 源码,在 payload 中补充该字段:
1# 定位 publish.js 文件 2find /usr/local/node/lib/node_modules/clawhub/dist/cli/commands/ -name "publish.js" 3 4# 使用 sed 在 payload 对象中插入字段 5sed -i 's/form.set('\''payload'\'', JSON.stringify({/form.set('\''payload'\'', JSON.stringify({ acceptLicenseTerms: true,/' \ 6 /usr/local/node/lib/node_modules/clawhub/dist/cli/commands/publish.js 7
修改后重新发布,成功!
另一个解决方案(更优雅):在 SKILL.md frontmatter 中添加 license: MIT 字段,部分版本的 CLI 会据此自动处理。
5.5 发布成功
1✅ Skill published successfully! 2 Name: juejin-publisher 3 Version: 1.0.0 4 Version ID: k97d5hg42cnk2yhzgjswhx8jc982n3nv 5 Publisher: @devilWwj 6
现在任何人都可以通过以下命令安装使用:
1clawhub install juejin-publisher 2
完整目录结构
1skills/juejin-publisher/ 2├── SKILL.md # Skill 主文档 + AI 使用说明 3├── README.md # 快速说明 4├── _meta.json # 本地元数据 5├── example.md # 文章格式示例(含 frontmatter) 6├── juejin.env.example # 配置文件模板(需复制为 juejin.env) 7├── scripts/ 8│ ├── publish.py # 核心发布脚本(~200行,纯标准库) 9│ └── query_tags.py # 标签 ID 查询工具 10└── references/ 11 ├── category_ids.md # 常用分类 ID 速查表 12 └── tag_ids.md # 常用标签 ID 速查表 13
常用分类 ID 速查
| 分类名称 | category_id |
|---|---|
| 前端 | 6809637767543259144 |
| 后端 | 6809637769959178254 |
| AI | 6809637773935378440 |
| 工具 | 6809637771511070734 |
| Android | 6809635626879549454 |
| iOS | 6809635627209637895 |
总结与思考
这次 Skill 开发的整体流程:
1需求分析 → 抓包研究 API → 脚本实现 → Skill 规范封装 → 上架 Clawhub 2
几个关键经验:
- 掘金 API 不难逆向:浏览器抓包 + 观察请求体,基本上两个接口就能搞定发布全流程
- 摘要长度是坑:
brief_content必须 50-100 字,太短或太长都会报错,自动生成摘要时要处理边界情况 - 零依赖设计更健壮:用 Python 标准库的
urllib代替requests,避免在不同环境中出现依赖缺失的问题 - Clawhub 发布需要
license字段:SKILL.mdfrontmatter 中必须声明license,否则服务端校验不通过 - Skill 的价值在于可复用:封装好的 Skill 不仅自己用,还能分享给社区,一次开发多人受益
相关链接
- 掘金编辑器:juejin.cn/editor/draf…
- Clawhub:clawhub.ai
- juejin-publisher Skill:
clawhub install juejin-publisher
如果你也在做内容自动化,欢迎交流 👋
《从零开发一个掘金自动发布 Skill,并上架 Clawhub》 是转载文章,点击查看原文。