16 KiB
Note
本文档由 WeHub 基于上游 README 翻译整理,属于社区翻译,非官方中文文档。
English · 原始项目 · 上游 README
原作者、版权与许可证归属以原始项目及本仓库 LICENSE 文件为准。
kage
kage(影,"shadow")会将网站克隆到一个可离线浏览的文件夹中,并剔除所有脚本。它会在真实的无头 Chrome(headless Chrome)中打开每个页面,等待页面稳定后,快照人类所见到的 DOM,然后删除所有 JavaScript,并将 CSS、图片和字体下载到本地路径。最终落盘的内容看起来与线上站点一致,且不会执行任何代码。
安装 • 快速开始 • 命令 • 克隆 • 打包 • 双击应用 • 原生窗口 • 工作原理
你早已熟悉这个问题。你对想保存的页面点击「另存为」,六个月后打开却发现一片空白、转圈不停,或者副本仍在尝试连接早已不存在的分析服务器。这页面从未真正属于你。它只是别人 JavaScript 的瘦客户端。
kage 走了另一条路。它驱动真实浏览器,让页面完成该做的一切,抓取最终结果,然后从中剥离所有脚本。无追踪、无网络调用、无意外。只有 .html 文件,你可以直接从磁盘打开、交给朋友,或打包成单个文件后十年都不用管。
完整文档与指南见 kage.tamnd.com.
安装
go install github.com/tamnd/kage/cmd/kage@latest
更喜欢预编译二进制?可从 releases. 获取压缩包、.deb/.rpm/.apk 或校验和。也可以交给包管理器:
# Homebrew (macOS)
brew install --cask tamnd/tap/kage
# Scoop (Windows)
scoop bucket add tamnd https://github.com/tamnd/scoop-bucket
scoop install kage
# apt (Debian, Ubuntu)
curl -fsSL https://tamnd.github.io/linux-repo/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/tamnd.gpg
echo "deb [signed-by=/usr/share/keyrings/tamnd.gpg] https://tamnd.github.io/linux-repo/apt stable main" | sudo tee /etc/apt/sources.list.d/tamnd.list
sudo apt update && sudo apt install kage
# dnf (Fedora, RHEL)
sudo dnf config-manager --add-repo https://tamnd.github.io/linux-repo/dnf/tamnd.repo
sudo dnf install kage
或者不必自行安装 Chrome,使用捆绑了 Chromium 的容器镜像:
docker run --rm -v "$PWD/out:/out" ghcr.io/tamnd/kage clone paulgraham.com
kage 驱动真实浏览器,因此主机上需要 Chrome 或 Chromium。它会自动查找系统安装;也可用 --chrome 或 KAGE_CHROME 环境变量指向特定路径。容器无需额外配置。
Shell 补全已内置:kage completion bash|zsh|fish|powershell。
快速开始
我们来镜像 Paul Graham 的文章,这样你可以在飞机上、没有 Wi-Fi 的笔记本上,或在站点终于改版后的 2050 年阅读它们:
# 1. Clone the site into $HOME/data/kage/paulgraham.com/
kage clone paulgraham.com
# 2. Read it back offline in your browser
kage serve $HOME/data/kage/paulgraham.com
# open http://127.0.0.1:8800
这就是完整流程。每篇文章、每张图片、每份样式表,都冻结在你的磁盘上,零网络即可运行。接下来两步可选但很方便:将整个镜像折叠成单个文件,并在独立窗口中打开。
# 3. Squeeze the mirror into a single shareable file
kage pack paulgraham.com # -> paulgraham.com.zim
kage open paulgraham.com.zim
# 4. Or into one executable that *is* the site
kage pack paulgraham.com --format binary -o paulgraham
./paulgraham # serves itself, needs nothing installed
命令
| 命令 | 作用 |
|---|---|
kage clone <url> |
在无头 Chrome 中渲染站点,并写入可浏览、无脚本的镜像 |
kage serve [dir] |
通过本地 HTTP 服务器预览已克隆的文件夹 |
kage pack <mirror-dir> |
将镜像折叠为单个 ZIM 归档、自包含查看器二进制文件或双击应用 |
kage open <file.zim> |
提供已打包 ZIM 的离线阅读服务 |
克隆
# The whole site, into $HOME/data/kage/<host>/
kage clone https://paulgraham.com
# Just the first 50 pages, two links deep, for a quick taste
kage clone paulgraham.com --max-pages 50 --max-depth 2
# Only one section of a bigger site
kage clone go.dev --scope-prefix /doc
# Pull in subdomains too, and scroll each page to trip lazy-loaded images
kage clone example.com --subdomains --scroll
# Come back next month and re-render in place to catch new essays
kage clone paulgraham.com --refresh
克隆是一次礼貌的广度优先爬取。它读取 robots.txt,从 sitemap.xml 播种,除非你另行指定,否则始终停留在种子主机上。它还固执地保持幂等:每个页面以其写入的文件为键,因此同一篇文章无论通过 http 还是 https 访问、有无尾部斜杠,都只会抓取一次。按 Ctrl-C 会在退出时保存进度;再次运行会从停止处继续。--refresh 会原地重新渲染,--force 会清除该主机并从头开始。
你实际会用到的标志:
| 标志 | 默认值 | 含义 |
|---|---|---|
-o, --out |
$HOME/data/kage |
输出根目录;镜像落在 <out>/<host>/ |
-p, --max-pages |
0 |
抓取 N 页后停止(0 = 无限制) |
-d, --max-depth |
0 |
跟随链接的深度(0 = 无限制) |
--scope-prefix |
仅爬取以此前缀开头的路径 | |
--subdomains |
false |
将种子主机的子域视为在范围内 |
--exclude |
要跳过的路径前缀(可重复) | |
--scroll |
false |
自动滚动每页以触发懒加载 |
--workers |
4 |
同时渲染的页面数 |
--no-robots |
false |
忽略 robots.txt(礼貌爬取) |
--crawl-delay |
0s |
覆盖 robots.txt;Crawl-delay 为页面启动间隔 |
-f, --force |
false |
先删除该主机已有的镜像 |
--chrome |
Chrome/Chromium 二进制文件路径 |
kage clone --help 包含其余选项,包括渲染时序、并发与资源大小调节。
提供访问
kage serve 会在已克隆文件夹上运行轻量静态文件服务器,使链接与资源像真实主机上一样解析:
kage serve $HOME/data/kage/paulgraham.com
# open http://127.0.0.1:8800
打包成单个文件
镜像是文件夹,便于浏览,却不便于搬运。复制成千上万个小文件很慢,「给你这个目录」也不是好送的礼物。kage pack 将整个镜像折叠为一个制品,形状由你选择:开放的 ZIM 归档,或本身就是站点的单个可执行文件。
单个 ZIM 文件
kage pack paulgraham.com # -> paulgraham.com.zim
kage open paulgraham.com.zim
ZIM 是专为此而生的开放文件格式:将整个网站(或整本维基百科)压缩、索引后塞进一个只读文件。kage 将整个镜像写入其中,文本经 zstd 压缩,媒体原样存储。它是 Kiwix, 背后的格式——人们用这一离线内容项目把维基百科、Stack Overflow 和 Project Gutenberg 带上船、带进没有互联网的教室、装进手机度过长途飞行。由于该格式是 documented standard(有文档的标准),而非 kage 的发明,你今天制作的 paulgraham.com.zim 多年后仍可在任何 ZIM 阅读器中打开。
所以你并不被锁定在 kage 上。kage open 是重新接入的最快方式,但同一个文件也适用于更广泛的 Kiwix 生态系统:
kage open paulgraham.com.zim # read it back with kage
kiwix-serve paulgraham.com.zim # or serve it with Kiwix at http://localhost
你也可以在 Kiwix 桌面应用, 中双击该文件,或在 Android 或 iOS 版 Kiwix 上加载它,在手机上阅读你的镜像。有一点需要注意:kage 会写入结构合法、带有标准元数据的归档文件,但不会构建 Kiwix 官方包自带的全文搜索索引,因此浏览和点击在各处都能正常工作,但阅读器内的搜索功能会受限。
打包是确定性的。同一镜像始终会生成字节级完全相同的文件,归档 UUID 由内容派生而非随机生成,因此包可以安全地进行校验和与缓存。裸主机名会相对于默认输出目录解析,因此 kage pack paulgraham.com 在 kage clone paulgraham.com 之后就能直接使用。
自包含二进制文件
--format binary 将归档粘接到 kage 的副本上,生成单个可执行文件,运行后即可离线提供该站点。你把它发给任何人都无需安装任何东西:不需要 kage,不需要 ZIM 阅读器,什么都不需要。
kage pack paulgraham.com --format binary -o paulgraham
./paulgraham
附加的归档与平台无关;只有基础可执行文件携带架构信息。默认情况下 kage 会追加到自身,因此你会得到适用于运行它的那台机器的查看器。将 --base 指向为其他操作系统构建的 kage(从 release; 获取,每个平台都会提供)即可在你自己的机器上生成适用于该平台的查看器。kage 会读取基础可执行文件的头部以确定目标平台,因此 Windows 查看器会自动获得 .exe 名称:
# Sitting on a Mac, build a Windows viewer
kage pack paulgraham.com --format binary --base kage-windows-amd64.exe # -> paulgraham.exe
代价是体积。该二进制文件包含完整的 kage,因此无论镜像多小,体积都约为 13 MiB 加上站点内容。如果你只需要内容,ZIM 要精简得多。
双击即可运行的应用
裸二进制文件在终端中很好用,但在文件管理器中双击时体验不佳:macOS 会在站点后面打开一个 Terminal 窗口,Windows 则会在旁边闪现一个控制台。添加 --app,kage 会将同一查看器封装为正规的桌面应用,双击即可打开站点,无需终端,并使用镜像自身的 favicon 作为图标。
在 macOS 上你会得到一个真正的 .app 包:
kage pack paulgraham.com --app # -> paulgraham.app
open paulgraham.app # or double-click it in Finder
在 Linux 上,将 --base 指向 Linux 版 kage,你会得到一个带 .desktop 启动器(Terminal=false,因此没有控制台)的 AppImage-style .AppDir。如果已安装 appimagetool,kage 会将其合并为单个可双击的 .AppImage:
kage pack paulgraham.com --app --base kage-linux-amd64 # -> paulgraham.AppDir (+ .AppImage)
kage 会从镜像中提取 favicon 来查找图标(优先使用较大的 apple-touch-icon.png,否则回退到 favicon.ico);传入 --icon some.png 可覆盖它。将 --app 与 webview 基础(见下文)配合使用,双击会打开原生窗口而非浏览器,这才是完整的「像一个应用」效果。
Windows 不需要打包,因为单个 .exe 本身就是应用。问题在于控制台窗口。发布版提供了一个 kage_<version>_windows-gui_<arch>.zip,其二进制链接到 GUI 子系统,因此打包在其上的查看器打开时背后不会出现控制台:
# Build a console-free Windows viewer (from any OS)
kage pack paulgraham.com --format binary --base kage-windows-gui-amd64.exe # -> paulgraham.exe
真正的窗口,而非浏览器标签页
默认情况下,打包后的二进制文件会打开系统浏览器,这意味着站点会作为又一个标签页出现,地址栏一应俱全,挤在你已经打开的 47 个标签旁边。使用 webview 标签构建 kage,它会在自己的窗口中打开站点,底层使用操作系统的 WebView(macOS 上为 WKWebView,Windows 上为 WebView2,Linux 上为 WebKitGTK)。Paul Graham 的文章,离线阅读,放在一个看起来、用起来都像真正应用的东西里:
make build-webview # or: CGO_ENABLED=1 go build -tags webview ./cmd/kage
kage pack paulgraham.com --format binary --base bin/kage -o paulgraham
./paulgraham # opens a window, no browser in sight
此构建需要 cgo 并链接平台 WebView,因此仍为可选。默认构建为纯 Go(CGO_ENABLED=0),预构建的发布二进制文件会打开浏览器,这使交叉编译的发布保持简单。kage open 遵循同一标签,因此使用 -tags webview 构建时,也会在原生窗口中显示 ZIM。
工作原理
seed URL ─▶ headless Chrome ─▶ final DOM ─▶ strip JS ─▶ localise assets ─▶ disk
(render) (snapshot) (sanitize) (rewrite links)
一组 Chrome 标签页负责渲染页面;另一组通过纯 HTTP 获取资源。每个 URL 都会确定性地映射到本地路径,因此在它们指向的资源下载完成之前,链接就已经被重写。输出如下所示:
paulgraham.com/
├── index.html # the home page, scripts stripped
├── greatwork.html # /greatwork.html, an essay
├── _kage/ # reserved: assets and crawl state
│ ├── paulgraham.com/site.css # localised stylesheet (url() rewritten)
│ ├── paulgraham.com/pg.png
│ └── state.json # visited set, for resuming
└── ...
pack 沿用同一思路:镜像中的链接已经是镜像相对路径,这些路径与归档内容条目一一对应,因此在已提供服务的页面中点击会直接命中正确的条目,完全无需重写。
从源码构建
git clone https://github.com/tamnd/kage
cd kage
make build # -> bin/kage (pure Go, opens the browser)
make build-webview # -> bin/kage with the native-window viewer (needs cgo)
make test # full suite, including the Chrome-driven end-to-end tests
make test-short # skip the tests that launch a browser
仓库按职责划分:
cmd/kage/ thin main: pins the main thread, then hands off to cli.Execute
cli/ the cobra command tree and flag wiring
clone/ the crawl: frontier, render workers, asset workers, resume state
browser/ headless Chrome control and DOM snapshotting
sanitize/ strip scripts, handlers, and javascript: URLs from the DOM
asset/ download and localise CSS, images, and fonts
urlx/ the deterministic URL-to-path mapping
zim/ a pure-Go ZIM reader and writer
pack/ mirror to ZIM or self-contained binary, and the offline HTTP handler
viewer/ present a served site: system browser, or native window (webview tag)
docs/ the tago documentation site
发布
推送版本标签后,GitHub Actions 会运行 GoReleaser,构建归档文件、.deb/.rpm/.apk 包、捆绑 Chromium 的多架构 GHCR 镜像、校验和、SBOM 以及 cosign 签名:
git tag v0.1.1
git push --tags
镜像标签不带 v 前缀(ghcr.io/tamnd/kage:0.1.1)。Homebrew 和 Scoop 步骤会在其 token 存在之前自动禁用,因此首次发布无需额外密钥即可工作。
许可证
MIT。参见 LICENSE。

