从零开发一个掘金自动发布 Skill,并上架 Clawhub

作者:小巫debug日记日期:2026/3/10

从零开发一个掘金自动发布 Skill,并上架 Clawhub

本文记录了一次完整的 Skill 开发旅程:从一句「帮我创建一个可以自动发布文章到掘金的 skill」开始,到最终成功上架 Clawhub,全程真实还原每一个关键决策和踩坑过程。


背景:为什么要做这个 Skill?

我日常运营一个 AI 资讯账号,每天需要将 Markdown 格式的文章发布到多个平台,包括微信公众号、小红书、掘金等。其中微信公众号和小红书已经有现成的 Skill 可以用,但掘金没有

每次发布掘金都要:

  1. 打开浏览器,登录掘金
  2. 进入编辑器,粘贴 Markdown 内容
  3. 手动设置分类、标签、摘要、封面
  4. 点击发布

这套流程重复且机械,非常适合自动化。于是决定自己动手,开发一个 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 后即为 slug
  • license必填,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
AI6809637773935378440
工具6809637771511070734
Android6809635626879549454
iOS6809635627209637895

总结与思考

这次 Skill 开发的整体流程:

1需求分析  抓包研究 API  脚本实现  Skill 规范封装  上架 Clawhub
2

几个关键经验:

  1. 掘金 API 不难逆向:浏览器抓包 + 观察请求体,基本上两个接口就能搞定发布全流程
  2. 摘要长度是坑brief_content 必须 50-100 字,太短或太长都会报错,自动生成摘要时要处理边界情况
  3. 零依赖设计更健壮:用 Python 标准库的 urllib 代替 requests,避免在不同环境中出现依赖缺失的问题
  4. Clawhub 发布需要 license 字段SKILL.md frontmatter 中必须声明 license,否则服务端校验不通过
  5. Skill 的价值在于可复用:封装好的 Skill 不仅自己用,还能分享给社区,一次开发多人受益

相关链接

如果你也在做内容自动化,欢迎交流 👋


从零开发一个掘金自动发布 Skill,并上架 Clawhub》 是转载文章,点击查看原文


相关推荐


Word 中 MathType 启动慢、卡顿、卡死 | 由于某种原因,PowerPoint 无法加载MathType……
斐夷所非2026/3/2

注:本文为 “office 中 MathType 启动、加载异常” 相关合辑。 图片清晰度受引文原图所限。 略作重排,如有内容异常,请看原文。 Word 2013 中 MathType 窗口启动延迟问题分析与解决方案 香蕉君达 发布于 2026-02-19 12:12 1 现象描述 通过快捷键或功能区按钮在 Word 2013 中插入公式时,编辑窗口启动延迟时长约为 3~4 秒,对文档编辑流程造成干扰。 测试表明,若系统中已存在至少一个处于打开状态的 MathType 窗口,后续公式


SpringBoot多环境配置实战指南
北极的代码2026/2/22

前言:在之前的开发环境中要跟改配置,测试环境也要改,每次切换环境都要手动修改配置文件 常常发生"我们在本地能运行,怎么部署到服务器就报错"的情况,一不小心就把测试环境的配置提交到代码库。因此我们提出了多环境开发配置。 多环境开发配置: 在SpringBoot中,多环境配置的管理核心是利用Profile机制,它允许我们为不同的运行环境(开发,测试,生产)定义独立的配置,并在应用启动时动态的激活,从而实现配置等隔离与灵活切换。 核心实现方式:Profile 特定配置文件 总之就


聊一聊 CLI:为什么真正的工程能力,都藏在命令行里?
G探险者2026/2/14

大家好,我是G探险者! 今天我们来聊一聊CLI。 在很多人眼里,命令行(CLI,Command Line Interface)是“黑框 + 英文命令”的代名词。 对普通用户来说,它晦涩、难记、不友好。 但对工程师来说—— CLI 是系统可编排能力的起点,是自动化的基础设施,是 DevOps 的地基。 今天我们不从“怎么用命令”讲起,而是聊一聊: CLI 是怎么诞生的? 为什么它没有被 GUI 取代? 为什么所有现代基础设施几乎都优先设计 CLI? 为什么 CLI 是工程能力的分水岭?


你这一生到底该如何赚钱?
袁庭新2026/2/5

大家好,我是袁庭新。 赚钱是每个成年人每天的头等大事,那你有没有认真思考过:你这一辈子到底应该如何赚钱?根据这几年的总结,我认为赚钱的方式无非以下三种: 用时间赚钱 用金钱赚钱 用金钱和时间一起赚钱 这三种赚钱方式的回报是不一样的,它们依次越来越大,最牛的就是用“时间+金钱”赚钱。 我们绝大多数人一生摆脱不了“用时间赚钱”这种模式,想要获得更多回报就低拼命上班加班。但,用时间赚钱的方式是可以改良的,最核心的策略就是“想尽一切办法把自己的同一份时间出售很多次”,举几个例子吧,比如:讲课、写书


爷爷你关注的前端博主复活了!! 他学python去了??
jinzunqinjiu2026/1/27

如何配置python环境。 hello,兄弟们马上过年了,想死你们了。转眼间就已经毕业半年。也工作了快一年了。从实习生一路跌跌撞撞,从刚开始连react的状态依赖都老是写死循环到现在已经经历过很多项目了。说来这一年也有很多成长,参与了公司很多的项目,看过各种代码。最终在ai的加持下已经能够独挡一面。但是最近公司开始掀起了一股ai风,以及网上ai全栈的兴起,我想我是坐不住了。深耕前端 or 技术转型。 小孩子才做选择,前端为主ai为辅,所以我要开始学习python逐渐开始学习ai应用了。正好我也没


【我与2025】裁员、旅游、找工作、媳妇没跑
修己xj2026/1/18

现在是2026年1月下旬。以往的年终总结总被搁置,今年却有些不同——家里添了新成员,自己的心态也悄然变化。于是决定写下这些文字,既是回顾我的2025,也是一次认真的复盘。 裁员 2021年6月,我加入上一家公司,一待就是四年。2025年收到的第一份“礼物”,竟是公司的裁员通知。我负责的是运营业务系统,因为常有线上问题需要处理,所以即便下班后、节假日也离不开电脑。几年来,我几乎没出省旅行过,每次回家都随身带着电脑,随时待命。 刚入职时,公司正处于扩张期,盈利状况很好。没过多久,就搬进了自购的整层


Incremark Solid 版本上线:Vue/React/Svelte/Solid 四大框架,统一体验
king王一帅2026/1/10

Incremark 现已支持 Solid,至此完成了对 Vue、React、Svelte、Solid 四大主流前端框架的全面覆盖。 为什么要做框架无关 市面上大多数 Markdown 渲染库都是针对特定框架开发的。这带来几个问题: 重复造轮子:每个框架社区都在独立实现相似的功能 能力不一致:不同框架的实现质量参差不齐 团队切换成本:换框架意味着重新学习新的 API Incremark 采用不同的思路:核心逻辑与 UI 框架完全解耦。 @incremark/core 负责所有解析、转换、增量更


机器学习数据集完全指南:从公开资源到Sklearn实战
郝学胜-神的一滴2026/1/1

机器学习数据集完全指南:从公开资源到Sklearn实战 1. 引言:为什么数据集如此重要?2. 机器学习公开数据集大全2.1 综合型数据集平台2.2 领域特定数据集 3. Sklearn内置数据集详解3.1 小型玩具数据集3.2 大型真实世界数据集3.3 完整列表 4. Sklearn数据集加载实战4.1 基本加载方法4.2 数据集对象结构4.3 转换为Pandas DataFrame 5. Sklearn数据集处理API大全5.1 数据分割5.2 特征缩放5.3 特征编码5.4


Gradle 基础篇之基础知识的介绍和使用
一线大码2025/12/23

1. 项目结构 目录介绍: build.gradle:项目编译时要读取的配置文件,build.gradle有两个,一个是全局的,一个是在模块里面。全局的build.gradle主要设置的是声明仓库源,gradle的版本号说明等。 gradlew:linux下的gradle环境脚本。可以执行gradle指令,比如./greadle build。 gradlew.bat:windows下的gradle环境脚本。可以执行gradle指令。 settings.gradle:包含一些必要设置,例如,任


人工智能入门概念介绍
Shawn_Shawn2025/12/15

最近公司正在推荐Ai相关项目,目前主要是大模型相关的应用层面开发,但自己还是希望能够基础入手,全方位了解一下机器学习,深度学习,强化学习,自然语言,大模型等Ai相关的知识点,仅了解相关概念,不去深入了解算法实现。本文主要介绍一下机器学习的基本概念。 什么是机器学习 引用周志华带佬的机器学习一书提到的案例,我们在生活中挑选西瓜的时候,经常会假嘛若鬼地敲一下西瓜,听一听声音,如果发出 “嘭嘭” 的闷声,说明西瓜成熟度好,果肉饱满。若发出 “当当” 的清脆声,可能西瓜还未成熟;若发出 “噗噗” 声,则

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客