适用人群
把技术文档迁移到 Markdown,省掉清理这一步
迁移文档通常是先把 HTML 转换一遍,再花更长的时间删掉转换器留下的东西。Clean Web Clipper 正是围绕这一步设计的,也是经过测量的部分:512 个页面中残留的 HTML 标签为零。
转换之后的清理
转换本身只要一秒。真正花掉一周的是跟着一起来的东西:每个文件顶部重复出现的左侧导航、版本切换器、“本页目录”栏、反馈小部件、面包屑,还有八列页脚。乘以两百个页面,迁移就变成了一个查找替换项目,而且每个来源网站都要换一套选择器。
接着,你最需要的部分到手时已经受损。代码块丢了语言标记,没有语法高亮,读者分不清哪段是 shell 命令、哪段是 JSON 请求体。单元格里带列表的表格直接散架。提示块(callout)变成孤零零的段落,看不出哪条是警告。这些靠查找替换修不好,只能把每一页重新读一遍。
没人提前规划的是事后的核查。迁移进行到第六周,有人问新站里的某段话是旧站原有的,还是迁移时新写的。如果转换出来的文件从来没记录来源地址,这个问题只能靠找回原页面来回答,而文档站一旦下线,就什么也找不到了。每个导入文件里加一行 source 只占一行,就能解决这个问题;再加一行 date,就能说明导入的是哪个版本。
哪些内容能干净地对应过来
- 标题、列表、表格、代码块和脚注都对应为标准 Markdown。
- 代码块保留语言标记:在技术语料上,156 个代码块中保留了 73 个,对比的引擎分别为 20 个和 0 个。
- 残留的 HTML 标签为零,共测量 512 个页面。
- 导航侧栏、版本切换器和“本页目录”栏被直接剪掉,而不是被转换进文件。
- 提示块(callout)变成引用块,因为 Markdown 没有对应的标准语法。
- 文件名模板和子文件夹让导入的文件集在增长时依然有条理。
- 每个文件都记录来源地址,审阅时任何一段导入的文字都能追溯到原页面。
- 脚注在清理程序删除它们所依赖的元素 id 之前就被收集起来:这个先后顺序决定了脚注引用链接能不能保留下来。
> **参见** > 对于底层的路径字符串操作,你也可以使用 `os.path` 模块。 ## 相关工具 以下是一个映射了 `os` 与 `PurePath`/`Path` 对应相同的函数的表。 | `os` 和 `os.path` | `pathlib` | | --- | --- | | `os.path.dirname()` | `PurePath.parent` | | `os.path.basename()` | `PurePath.name` | | `os.path.splitext()` | `PurePath.stem`, `PurePath.suffix` |
为迁移做好设置
两项设置、一个文件夹,外加一条关于提交的工作规则。正是这条提交规则,让之后的审阅成为可能。
- 在文档仓库里建一个
import/目录,和最终页面所在的位置分开。迁移进行期间,原始转换结果和编辑过的页面绝不能放在同一个文件夹里。 - 从扩展程序图标打开 Options,把保存位置指向
import/,并把点击图标时的操作设为“保存到文件夹”。三十个页面就是三十次按键,不应该再加上三十个窗口。 - 把文件名模板设为
{domain}-{title}。从两三个来源网站导入时,标题经常重名,有了域名,“概述”才不会变成概述-3。 - 在 frontmatter 中打开
title、source和extraction。source用来回答六周后的核查问题;extraction告诉你哪些页面的正文来自结构化数据,而不是来自渲染出的页面。 - 图片保留为链接,不要选择跳过。这些地址你最终不会保留,但链接本身就是清单:它记录了这一页有一张图,规划资源迁移时需要的正是这份清单。
- 剪藏带标签页或折叠面板的页面之前,先把它们打开。扩展程序转换的是浏览器已经渲染的内容,点击后才插入的标签页内容,在点击之前并不在 DOM 中。
- 把原始导入作为一次提交,之后的结构调整放在后续提交里。这样以后每个 diff 显示的都是你的编辑改动,而不是编辑和转换混在一起。
导入用的设置
这些值是为一批要由人逐页审阅、随后再编辑的文件选的,而不是为一个读一次就扔在一边的文件夹。
| 设置 | 取值 | 为什么这里这样设 |
|---|---|---|
| 点击图标 | 保存到文件夹 | 三十个页面应该是三十次按键,而且不弹窗口 |
| 保存位置 | 文档仓库里的 `import/` | 迁移期间,原始转换结果和编辑过的页面不能共用一个文件夹 |
| 文件名模板 | `{domain}-{title}` | 多来源导入时标题会重名,域名是唯一可靠的区分依据 |
| Frontmatter | 打开 `title`、`source`、`extraction` | `source` 回答核查问题;`extraction` 标出值得复查的页面 |
| 图片 | 保留为链接 | 这些链接就是资源清单,尽管你最后会把它们全部替换掉 |
| 按网站规则 | 每个来源网站 → 各自的子文件夹 | 审阅按来源进行,因为每个网站的标记各有各的问题 |
| 提交 | 先提交原始导入,再提交编辑 | 之后每个 diff 显示的是编辑改动,而不是转换带来的噪音 |
## 搭建第一个 Vite 项目 npm ```bash $ npm create vite@latest ``` Yarn ```bash $ yarn create vite ``` pnpm ```bash $ pnpm create vite ``` > **兼容性注意** > Vite 需要 Node.js 版本 20.19+ 或 22.12+。然而,有些模板需要依赖更高的 Node 版本才能正常运行,当你的包管理器发出警告时,请注意升级你的 Node 版本。 页面上的标签(npm、Yarn、pnpm、Bun、Deno)都已渲染在 DOM 中,所以依次全部保留下来,这里只列出前三个。 只有点击后才渲染的标签页不会出现在剪藏里。
三次迁移
三十页供应商文档
合作方的 API 参考文档要放进你们的文档站。你把三十个页面剪藏进 import/,每页一次按键。代码块带着语言标记到达:在技术语料上,156 个代码块中有 73 个保留了语言标记,对比的引擎分别为 20 个和 0 个;而左侧导航、版本切换器和反馈小部件根本没有出现。
接下来你编辑的是正文和结构。你不用处理的,是直接转换 HTML 时每个文件顶部都会堆上的两百行侧栏,而这正是迁移要花几周的真正原因。
一份靠提示块撑起来的指南
源指南大量使用警告、注意和提示三类提示块。Markdown 对这三类都没有标准语法,所以它们都变成引用块:内容保留,三种类型之间的区别不保留。
这一点最好在开始前知道,而不是事后才发现,因为按类型重新标注是一轮手工工作,工作量取决于来源。在原始导入里查找引用块,就能估出这一轮的规模,这只是一次 grep,不需要逐页阅读。
网站下线前的抢救
某个产品即将退役,它的文档月底下线。这里没有爬虫,只能一页一页来,但每个页面都会变成一个文件头里写着原始地址的文件,正是这一点让抢救下来的内容以后还有用。
图片需要单独规划。它们仍然是指向一个即将消失的网站的链接,所以重要页面的示意图必须在截止日期前手动保存;导入文件里的链接清单就是这项工作的核对表。
与其他转换方式对比
一次迁移往往会同时用到其中两种。诚实的比较不是看有没有手工工作,而是看手工工作落在哪里。
| 现在的做法 | 能得到什么 | 代价是什么 |
|---|---|---|
| 命令行 HTML 转换器 | 批量转换,可以写脚本 | 转换整个页面:导航、页脚和小部件都成了每个文件的一部分 |
| 爬虫加转换器 | 整站自动处理,无需值守 | 每个来源网站一套选择器,网站改版时规则文件还得跟着维护 |
| 向供应商要源文件 | 如果存在,就是真正的 Markdown | 经常被拒绝,经常过时,而且格式常常绑定在对方的站点生成器上 |
| 逐页复制粘贴 | 要什么、不要什么完全由你决定 | 代码块丢失语言标记,单元格里带列表的表格会散架 |
| Clean Web Clipper | 干净的单页 Markdown,并记录来源地址 | 一次一页,不改写链接,不下载资源文件 |
导入之后需要修什么
为什么所有提示块现在看起来都一样?
警告、注意和提示都变成引用块,因为 Markdown 没有标准的提示块语法可以对应。文字完整,行首的强调标记通常也会保留,所以以加粗的“警告:”开头的内容仍然写着“警告”。按类型重新标注是一轮需要事先规划的工作,它的规模只要对引用块做一次 grep 就能知道。
为什么有个页面导入后顶部没有标题?
空标题会被删掉,而在很多文档站上,页面上看到的标题根本不是标题元素,而是文章正文之外的导航元素。标题仍然通过 frontmatter 的 title 字段进入文件,它取自页面自己的元数据。把它提升为一级标题是一个机械步骤,可以用脚本对整批导入一次处理。
为什么图片链接还指向旧网站?
这是必然的。扩展程序不下载任何二进制资源,所以每张图片都是指向原位置的链接,导入的文件集并不自成一体。把这些链接当作清单而不是结果:它们告诉你哪些页面有资源、有多少,这正是资源迁移需要的列表。
为什么有个页面记录的是 jsonld-articlebody,读起来和网站上不一样?
这个页面把正文放在结构化数据里,却没有完成渲染,所以正文取自结构化数据。两者并不总是一致:如果网站更新渲染页面比更新结构化数据更勤,那块数据里留下的就是旧版本。提交之前,应该把这些页面和原页面对照着读一遍。
它不是什么
它不是迁移工具:没有爬虫,不改写链接,不生成重定向表,也不下载资源文件。图片仍然是指向原网站的链接,所以导入的文件集并不自成一体。Markdown 无法表达的内容(带标签页的代码块、include、提示块类型、自定义组件)会被压平成最接近的普通写法,内容保留,样式不保留。另外,它转换的是页面已经渲染出来的内容,没展开的折叠面板里的文字不在 DOM 中,也就不会被剪藏。
常见问题
迁移后标题层级还准确吗?
文档里的提示块(callout)会变成什么?
能一次把整个文档站抓下来吗?
标签页或折叠面板里的内容怎么办?
图片会一起保存吗?
frontmatter 能直接用于我的站点生成器吗?
title、source、author、date、extraction),在哪里都能解析,但字段名是扩展程序的,而不是你的生成器的。在整批导入上用一行脚本就能完成映射,不需要的字段也可以在剪藏前关掉。导入页面之间的互相链接会被改写吗?
source 行就是做表的依据。标题锚点会保留吗?
source 行能告诉你原来的锚点是什么。能给供应商的文档留一份离线副本吗?
文件会记录它来自哪个版本的文档吗?
source 行按地址栏里的原样保存地址,所以从 /v4/ 路径剪藏的页面会写明这一点,这比导入的正文本身能告诉你的更多。