站点文档 → 资料库入库
把在线文档站(帮助中心、使用手册、课程中心)批量抓取为 Markdown,再按层级建成资料库文档树。
0. 前置判断
- 用户说「存到资料库/知识库」且未点名外部产品 → 目标就是 WorkBuddy 原生资料库,不反问存哪里。
- 先跑
runtime_context.py判断模式:mode=client时,所有网络脚本前必须ToolSearch(connect_open_platform)→DeferExecuteTool({"skill_id":"library"})换票。 - token 有效期 30 分钟。长任务中途过期要重新换票,不要缓存到文件。
- 个人空间(
category=personal)的写入由用户原始请求直接授权,无需二次确认;团队空间必须先展示方案等确认。
1. 先探测站点渲染方式(决定抓取手段)
| 类型 | 判定 | 抓法 |
|---|---|---|
| 服务端渲染 | curl 拿到的 HTML 里能 grep 到正文关键词 |
curl + 脚本解析,不占上下文 |
| SPA | curl 到的 HTML 没有正文,但有 window.settings = {...} / window.__INITIAL_STATE__ |
正则提取内嵌 JSON,脚本解析 |
| 纯 SPA 无内嵌数据 | 两者都没有 | 只能 WebFetch(内容会进上下文,注意控制页数) |
探测命令:
curl -s -L -A "Mozilla/5.0 ... Chrome/120.0 Safari/537.36" "<URL>" -o /tmp/t.html && wc -c /tmp/t.html
grep -c '正文关键词' /tmp/t.html # 有结果 = 服务端渲染
grep -oE 'window\.[A-Za-z_]+\s*=\s*\{' /tmp/t.html | sort -u # 找内嵌 JSON
提取内嵌 JSON:
m = re.search(r'window\.settings\s*=\s*(\{.*?\});\s*\n', html, re.S)
data = json.loads(m.group(1))
务必先抓一篇看结构再批量,不要一上来就写循环。
2. HTML → Markdown
用 html.parser 手写转换器(不依赖三方库),常见标签映射:
h1→# h2→## h3→### p→段落 ul/li→- ol/li→1. a→[](url) br→占位符
坑位:
- <br> 不能简单转空格。中文文档常用 <li>小标题<br>说明</li>,转空格会粘成一句。
做法:转成占位符,收尾时判断前一字符——已是 :: 直接拼接,是句末标点补空格,否则补中文冒号。
- 图片可能是 background-image,不在 <img> 里:
background-image: url(//cdn...) → 补 https: 前缀写成 。
- 相对链接要拼成绝对 URL。
写完必须校验(残留标签、乱码、H1 唯一性):
PROBLEM = [r'<(div|span|p|ul|li|section|br|em)\b[^>]*>', r'&(nbsp|amp|lt|gt);',
r'\]\(\s*\)', r'[\ufffd]|â€|Ã', r'\n{4,}']
3. 入库(关键:控制单篇大小)
# 新建文档(支持 --parent-id 建层级)
printf '%s' "$TOKEN" | python3 "${SKILL}/doc/create_doc.py" --token-stdin \
--title "标题" --content-file "x.md" --parent-id "<nodeId>"
# 建层级:先建目录页文档拿 nodeId,再把子文档挂到它下面
# 列子节点 / 移动 / 重命名
python3 "${SKILL}/space_api.py" space.workspace.list-node --token-stdin --space-id "<spaceId>" --parent-node-id "<nodeId>"
python3 "${SKILL}/space_api.py" space.workspace.move-node --token-stdin --node-id "<id>" --target-parent-id "<pid>"
python3 "${SKILL}/space_api.py" space.workspace.rename-node --token-stdin --node-id "<id>" --title "新标题"
create_doc.py 成功输出:KS_DOC_CREATE\t<nodeId>\t<kind>\t<url>\t<failedCount>\t<fatalCount>
(两个计数非 0 = 内容可能不完整,要提示用户核对)
⚠️ HTTP_524 陷阱(必看)
单篇超过约 1 万字会返回 {"error":"创建文档失败: code=HTTP_524"}(网关超时),
但服务端其实已经建了文档——空的,只有标题。直接重试会产生一堆空壳,删不掉。
正确做法:
1. 生成前就按章节/讲数拆成 3000~5000 字的小块,命名 XXX(1/3)。
2. 已经产生空壳的,读 get_doc_reviews.py 看字节数确认(空壳约 40~60 字节,只有 frontmatter),
再建一个回收容器文档,用 move-node 把它们集中归拢,保持主目录干净。
3. 资料库没有删除 API,move 是唯一清理手段。
给目录页加链接导航
create_doc.py 只吃 Markdown;编辑已有文档必须用组件语法:
printf '%s\n%s' "$TOKEN" "$(cat actions.json)" | python3 "${SKILL}/doc/submit_doc_edit.py" \
--token-stdin --page-id "<nodeId>" --actions-stdin
[{"type":"insert_after","id":"","content":"<Heading level=\"2\">导航</Heading>\n\n<BulletedList><Link href=\"https://...\">标题</Link></BulletedList>"}]
id:""= 文末追加;多个顶层块用空行分隔。- 组件必须单行紧凑写;禁止手写
id、禁止 Markdown 行内样式(用<Mark bold>)。 - 先
--dry-run校验再正式提交。
4. 易错点清单
- 遍历本地目录上传时,别用
startswith("0")之类宽匹配选分组,容易把别的目录也扫进来(本次把07_学习园地误判进使用手册分组)。按显式白名单列目录。 - 上传脚本从
os.environ读 token,不要把 token 写进任何文件、日志或 stdout。 - 大批量上传放后台跑(几十篇要几分钟),中途按用户要求汇报进度。
- 上传完用
list-node递归核对目录树,别只信脚本打印的成功数。