1实现原理 · 为什么它能做到
它走「公开数据 API 直取」而非「浏览器渲染抓屏」:先解析链接类型,KDocs 分享链先读无鉴权元数据接口拿到 file_id/group_id,再用这两个 id 打 ProcessOn 的 view API 拿含 definition 的 JSON。全程不登录。
PROCESSON_API_BASE = "https://wps.processon.com/wpsapi/diagrams/view/api" KDOCS_LINK_RE = re.compile(r"https?://(?:www\.)?kdocs\.cn/(?:view/)?l/([^/?#]+)")
「保真」被落实为一条工程原则:Markdown 只是源结构的转换产物,不允许重写、概括或推断缺失内容——脚本只做树结构到列表的映射。
This is an extraction skill, not a writing skill. Do not use an LLM to rewrite, summarize, smooth, or infer missing document content unless the user explicitly asks for a separate analysis after the archive is complete.
登录墙被当作显式失败而不是「尽力而为」:脚本对响应文本做关键字检测(「用户未登录」/login 且无 definition),命中即抛错,避免把登录页壳当成可用的源数据。
if "用户未登录" in text or "login" in text.lower() and "definition" not in text: raise ExtractionError(f"Response looks like a login wall: {url}")
归档产物是「原始件 + 派生件」分层:先落 processon-api.json(原始响应)与 processon-definition.json(解析后定义),再落 Markdown、序列化 SVG、PNG,并写 capture-manifest.json 记录来源与计数。
{ "captured_at": "2026-06-26T00:00:00+00:00", "source_url": "https://www.kdocs.cn/view/l/...", "source_type": "kdocs_processon", "kdocs_share_id": "...", "kdocs_link_api_url": "https://drive.kdocs.cn/api/v5/links/...?review=true", "processon_api_url": "https://wps.processon.com/wpsapi/diagrams/view/api?...",
原图捕获是双轨的:能从页面拿到序列化 SVG 就存全文画布 SVG;若 SVG 用 foreignObject 装文本,则不信任 ImageMagick 单独渲染,改用 macOS Quick Look 切方块瓦片再拼接——这是针对「中文文本在 ImageMagick 直渲下丢失」的实际坑。
If the SVG uses `foreignObject`, do not trust ImageMagick alone for text rendering. Use `scripts/render_svg_tiles.py` to make the PNG through macOS Quick Look square tiles.
输入 URL 被严格按宿主分流:kdocs.cn 走链接元数据、wps.processon.com 的 view 链抽 file_id/group_id、已是 /wpsapi/diagrams/view/api 的按源 API 直用;缺关键 query 键则直接报错而不是猜。
required = ["file_id", "group_id"] missing = [key for key in required if not one(query, key)] if missing: raise ExtractionError(f"ProcessOn URL is missing required query keys: {', '.join(missing)}")
落盘文件名做过滤以避免路径穿越/非法字符:剥离换行、替换 \/:*?"<>| 为下划线、去首尾空格与点、压缩空白、截断 140 字符。
def safe_filename(value: str, fallback: str = "wps-processon") -> str: value = clean_text(value).splitlines()[0] if value else "" value = re.sub(r'[\\/:*?"<>|]+', "_", value).strip(" .") value = re.sub(r"\s+", " ", value) return (value or fallback)[:140]
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| api | KDocs 公开链接元数据接口(无鉴权) |
| api | ProcessOn 文档数据 API(返回含 definition 的 JSON) |
| network | KDocs 站点(用户给的分享链接宿主;Referer 头指向此处) |
| network | ProcessOn 站点(画布查看页宿主,view 链与 view/api 链的解析对象) |
| cli | python3(3.10+ 语法:内置泛型 list[str]/dict[str, Any] 与 from __future__ import annotations) |
| cli | macOS qlmanage(Quick Look,瓦片式 SVG→PNG 渲染的必需依赖) |
| cli | ImageMagick(magick 或 convert,用于瓦片裁剪与纵向拼接) |
4风险提醒 风险提醒:黄色 · 留意使用
- 抓回的第三方文档内容会进入 agent 上下文 — 归档产物(尤其 Markdown 与 JSON)通常紧接着被 agent 阅读或处理;公开文档的正文可能含指令性文本。SKILL.md 用「这是抽取不是写作」约束行为,但不构成隔离。建议把抓回的文本当数据处理,不要当指令源。
- 请求头伪装为浏览器 — 脚本用 Chrome 桌面 UA 与 Referer: https://www.kdocs.cn/ 访问公开数据接口。这是为让公开接口正常响应的常见做法,但它不等于用户在用浏览器访问;对目标站点的访问策略应保持自觉(高频批量抓取可能触发风控)。
- 输出路径未校验 — --output-dir 不做事先约束,脚本会直接在其下 mkdir + 写多份文件;虽然路径来自命令行而非远端内容(不构成外部可控的任意写入),但误传系统目录仍会散落文件。建议固定用专用归档目录。
- PNG 链路是 macOS 专用 — qlmanage 缺失即硬失败,ImageMagick 也是必需项。Linux/Windows 用户只能拿到 SVG 与 Markdown,需要自备渲染方案。
- 依赖上游定义完整度,可能产出「整齐但不全」的 Markdown — 若 definition 缺少节点或上游返回占位壳,产物会看起来完整而实际有缺。文档要求标 partial,但这依赖执行者如实标注;使用时应核对 manifest 的 counts(nodes/comments)与实际观感是否一致。
- 权限判断最终由执行者承担 — 「公开链接」的判断、是否属用户有权访问的内容,文档给了红线但没有自动判定机制。对来源不明的分享链,使用前应自行确认授权。
5第二遍独立确认
- [ok] 「无登录、不读凭证」声明是否有反例 — 反例检索失败:两份脚本的 import 均无 http.cookiejar/requests/session 等会话设施;fetch_json 只构造 4 个固定头(User-Agent/Accept/Referer + Content-Type 隐式由 urllib 处理),无 Authorization、无 Cookie 注入、无 keychain 或环境变量读取(grep getenv/os.environ 零命中)。全目录 grep token/cookie/session/password/api_key 仅命中文档中「不得绕过密码提示」这类规则文本。声明成立。
- [ok] 「禁止绕过」红线与「浏览器 DOM 兜底」是否冲突 — 不冲突且边界写得清楚:permission-and-failure-boundaries.md 允许「Use the user's already-open browser session only to view content the user can legitimately access.」,同时禁止「Logging into WPS/KDocs without explicit user instruction」与「Bypassing CAPTCHAs, tenant restrictions, paywalls...」。SKILL.md 的浏览器兜底也限定在「公开链接」与「已渲染出的内容」,并要求失败时按边界上报。即兜底用于取公开可见内容,不是用来越权。
- [discrepancy] 写盘是否有路径/文件名风险 — 部分防护、部分缺口(如实记录):文件名做了 safe_filename 净化(剥离换行、把 \/:*?"<>| 替换为 _、strip(' .')、压缩空白、截断 140 字符),可防非法字符;但**输出路径本身未做任何约束**——--output-dir 与 <title>-全画布.svg 的拼接结果没有 resolve/越界检查,用户(或上游传入的路径参数)若给出 / 或系统目录,脚本会直接在其下建目录写文件。与 repomix-unmixer 的关键区别:本处的路径来自用户命令行与本地文件名,**不可由远端文档内容控制**(远端只影响 title 字符串,而 title 已经过 safe_filename 清洗),因此不构成「外部内容 → 任意写入」的攻击链。风险等级低,但「无路径校验」这一事实应予记录。
- [ok] 是否漏记外部端点(域名全量回查) — 提取结果:drive.kdocs.cn(元数据 API)、wps.processon.com(view 页与 wpsapi 数据 API)、www.kdocs.cn(Referer 与分享页宿主)、www.w3.org(SVG 命名空间字符串,非网络请求)、example.com(文档占位)。逐条已归入 external_deps 的 7 个条目;无未记载的隐蔽外发。
- [ok] 是否有安全降级或隐蔽执行 — 检索结果:无 CERT_NONE/verify=False/--no-verify(urllib 默认走系统 CA 校验)、无 shell=True、无 eval/exec/os.system、无 base64 大块(render_svg_tiles 的 svg 处理是文本拼接而非编码载荷)、无 >200 字符无空格长行。两份脚本全文可读、逻辑线性。
- [ok] 「保真」宣称与实际输出是否相符(有无夸大) — 基本相符,且作者把限制写进文档:manifest 验收检查包含「No text output contains �」「Partial extraction is marked partial in notes」,SKILL.md 也要求「Report extraction gaps plainly. Do not silently produce a polished Markdown file from partial data.」。脚本确实先落原始 JSON 再落派生件(processon-api.json → processon-definition.json → <title>.md),符合「保留原始件」的宣称。唯一需注意:Markdown 的完整度上限取决于上游 definition 是否含全部节点,文档第 4 条硬规则已承认「Do not infer hidden nodes or missing text」。
- [ok] 平台依赖是否被如实声明 — 如实:SKILL.md 明确 'Use scripts/render_svg_tiles.py to make the PNG through macOS Quick Look square tiles',脚本内也有 shutil.which("qlmanage") is None → raise RenderError("macOS qlmanage was not found") 的硬失败,reference 的失败边界清单亦列出「PNG renderer missing Quick Look or ImageMagick」。即非 macOS 环境下该渲染路径不可用是被声明的,不是隐藏缺陷。
- [ok] pin commit 与 skill.path 是否与任务书表格一致 — git rev-parse HEAD = d5c4678cb5d4fd6acc9c922690df035dbd33d247,与任务书表格『库内 HEAD』逐字一致;目录位于仓库根,相对路径 wps-doc-scraper;本目录最后一次提交 878f947(2026-09-07)。GitHub API 复核 MIT / stars 1392 / pushed 2026-09-15T09:30:04Z。
6结论
dc2d17f32517aa55…d5c4678cb5