kage:用无头浏览器“渲染后封印“网站,彻底告别 JS 幽灵依赖
kage:用无头浏览器"渲染后封印"网站,彻底告别 JS 幽灵依赖
核心问题:你保存的网页,真的是你的吗?
每个人都遇到过这种情况:把某篇文章"另存为",几年后打开是一片空白。原因很清楚——现代网页本质上是一个薄客户端,内容由远端 JavaScript 在运行时注入。你存下来的只是一个壳,它需要不断向别人的服务器打电话,才能变成你看过的样子。这不是保存,是借阅。
kage(日语「影」,意为"影子")正面解决的就是这个问题。它由 Go 编写,MIT 开源,核心思路是:先让页面活一次,再把它钉死。
关键机制:渲染快照 + 外科手术式去脚本
kage 的巧妙之处不在于抓取,而在于介入时机。
传统工具(HTTrack、wget)在 HTTP 层抓取,拿到的是服务器吐出的原始 HTML——对于现代 SPA 或动态加载内容,这基本等于拿到了一个空骨架。而 kage 的流程是:
- 用真实的 Headless Chrome 打开页面;
- 等待页面完全渲染(包括 JS 执行后动态注入的 DOM);
- 快照此刻人类能看到的 DOM;
- 外科手术式移除所有
<script>标签、事件监听、javascript:URL; - 将 CSS、图片、字体等静态资源下载到本地路径。
这个"渲染完再截图"的思路,让 kage 相比传统爬虫有了质的差异:它保存的是渲染结果,而非渲染材料。
历史脉络与同类工具对比
| 工具 | 抓取方式 | 能处理动态内容? | 输出形式 | 是否保留 JS |
|---|---|---|---|---|
| HTTrack / wget | HTTP 层,静态下载 | ✗ 基本不行 | 文件夹 | 是(但 JS 无法执行) |
| 浏览器"另存为" | 当前页面快照 | △ 当前帧可以 | 单 HTML + 文件夹 | 是(且有外链依赖) |
| Monolith(Rust) | HTTP 层下载 + 内联 | ✗ 无浏览器渲染 | 单 HTML 文件 | 是(全部内联) |
| SingleFile(浏览器扩展) | 浏览器内运行 | ✅ 当前页有效 | 单 HTML 文件 | 是(内联) |
| kage | Headless Chrome 渲染 | ✅ 等待 JS 执行完 | 文件夹 / ZIM / 二进制 | 删除 |
这里有一个根本的取舍:Monolith 和 SingleFile 把 JS保留并内联,而 kage 把 JS删除。Monolith 在 GitHub 有 13,000+ star,但它不使用 Headless Browser,官方文档明确注明动态内容是其局限。kage 用更重的方案(启动真实 Chrome)换来了渲染能力,但代价是:删掉 JS 之后,一切交互(搜索、登录、地图、路由、评论)也一起消失了。这是设计选择,不是缺陷,前提是你想要的是内容,不是功能。
输出格式的分层设计:三个用途,三种形态
# 第一层:克隆为本地文件夹(可检查、可浏览) kage clone paulgraham.com --max-pages 50 --scroll # 第二层:打包成 ZIM 归档(开放格式,可用 Kiwix 生态打开) kage pack paulgraham.com kage open paulgraham.com.zim # 第三层:打包成独立可执行文件(无需任何依赖,双击即开) kage pack paulgraham.com --format binary -o paulgraham ./paulgraham # 跨平台构建:在 Mac 上生成 Windows 查看器 kage pack paulgraham.com --format binary --base kage-windows-amd64.exeZIM 是值得单独关注的格式选择。它是 Kiwix 生态的基础格式,支撑着 Wikipedia 离线版、Stack Overflow 离线版在船上、无网络教室里的使用。用开放标准格式意味着:你今天生成的.zim文件,10 年后依然能被任何 ZIM 阅读器打开,你不被 kage 锁定。这个设计决策比功能本身更值得注意。
自包含二进制更激进:把 kage 本身和站点内容打包成一个约 13 MiB + 站点大小的可执行文件,对方什么都不需要安装,运行即可。代价也直接:每个文件都带着一整个 kage,内容很小时极度浪费空间。
交叉验证
信源一:ic.work 独立分析(2026年6月)
ic.work 上的分析文章明确指出,kage 的价值"不是完整复制网站功能,而是冻结可读内容",并指出 ZIM 格式目前不支持全文搜索索引(原文也有此说明),以及跨平台二进制需针对目标平台单独编译——这些局限与原 README 一致,属于认同并补充细节,没有反驳。
信源二:dev.to 对 Monolith 的介绍(2025年6月,13,751 GitHub star)
Monolith 是 kage 最直接的横向竞品,用 Rust 编写,生成单个内联 HTML 文件,但不使用 Headless Browser,官方文档和第三方分析均注明其不处理动态内容。这从侧面验证了原文的立论:在 JS 驱动的现代网页面前,不经过真实浏览器渲染就无法获得完整内容。kage 选择用更重的方案解决这个问题,Monolith 选择不解决。两者适用场景不同,并非 kage 全面优于 Monolith,但在动态内容保存这个维度,原文的判断站得住脚。
诚实的边界
kage 被过度解读的风险在于"克隆整站"这个说法太诱人。以下场景它做不了:
- 登录墙后的内容:kage 本身无法处理需要身份验证才能访问的页面(除非你自行配置 Cookie);
- 前端路由 SPA:React/Vue 构建的单页应用,URL 靠 JS 路由管理,kage 的链接追踪会失效;
- 无限滚动/分页内容:
--scroll可以触发懒加载,但对真正无限分页的平台(Twitter、Instagram)效果有限; - ZIM 全文搜索:打包后无法在 kage 的 ZIM 里做站内关键词搜索,这个功能目前缺失;
- 动态交互完全丧失:删 JS 是设计目标,但站内搜索、过滤器、折叠/展开这类 UI 行为同样消失,可读性取决于站点的内容结构本身。
还有一个实际问题:kage 依赖 Chrome/Chromium,在 CI 环境或轻量服务器上这是一个不轻的依赖。Docker 镜像打包了 Chromium 解决了这个问题,但又引入了容器依赖。
个人启发:该如何实际应用
这个工具最直接的价值场景是技术文档归档:把你依赖的开源项目文档、API 文档、学习资料克隆下来,打成 ZIM。文档类网站通常是静态生成的,JS 交互少,kage 效果最好,且原始内容本身就值得长期保存。
具体可以做的动作:
- 立刻执行一次:选 3 个你最常用但担心消失的技术文档网站(比如
go.dev/doc、某个库的旧版文档),运行kage clone [url] --scope-prefix /doc; - 选 ZIM 而非二进制:优先打包成
.zim,因为格式开放,Kiwix 桌面/移动端都能读,而且你未来某天决定不用 kage 了,内容依然可访问; - 不要对社交媒体站点抱期望:kage 对 SPA 重度依赖的站点效果差,用在内容型网站上。
推演:接下来会怎样
kage 目前处于工具链形成期,不是范式突破,而是把"Headless Browser + 去脚本 + 多格式输出"这条路走通的早期完整实现。可以预见:
- ZIM 全文索引会补上,因为这是 Kiwix 生态的标配,没有它与官方内容包的互操作性就差一截;
- SPA 站点支持会改善,Headless Chrome 的 CDP(Chrome DevTools Protocol)已经足够强大,可以等待特定路由渲染完成,这只是工程实现问题;
- 类似工具会整合 kage 的思路,"先渲染后封印"这条路径会逐渐成为网页归档工具的标准范式,就像 Puppeteer 出现后 SSR 快照方案被广泛采用一样。
延伸思考
"内容归档"与"功能复制"的边界在哪里?kage 选择删 JS 是一个极端但清晰的立场。未来是否存在一种方案,能在删除追踪/网络调用的同时,保留纯前端交互逻辑(比如折叠/展开、本地过滤)?Service Worker 沙箱或许是一个方向。
网页的长期可访问性谁来负责?kage 是个人工具,Kiwix 是社区方案,Internet Archive 是机构方案。三者覆盖不同的时间尺度和内容规模——但对于个人依赖的小众技术文档,没有任何一个机构会主动帮你保存,这件事只能靠自己。
Headless Browser 渲染作为"内容提取基础设施"的边界在哪?kage 用它做离线保存,其他工具用它做测试、截图、SEO 预渲染。当 Chrome 的 CDP 协议成为事实标准后,基于它的工具生态会走向何方——是被 Google 统一管控,还是成为真正的开放基础设施?
📚 参考来源
- GitHub - tamnd/kage: Shadow any website for offline viewing, with the JavaScript stripped out · GitHub
