c0ecd55c48
ci / test (macos-latest) (push) Has been cancelled
ci / test (ubuntu-latest) (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / govulncheck (push) Has been cancelled
ci / tidy (push) Has been cancelled
ci / webview (push) Has been cancelled
ci / windows-gui (push) Has been cancelled
release / check (push) Has been cancelled
release / release (push) Has been cancelled
291 lines
16 KiB
Markdown
291 lines
16 KiB
Markdown
<!-- WEHUB_ZH_README -->
|
||
> [!NOTE]
|
||
> 本文档由 WeHub 基于上游 README 翻译整理,属于社区翻译,非官方中文文档。
|
||
> [English](./README.en.md) · [原始项目](https://gitea-dev.autobee.pro/frostbyte_neo/kage) · [上游 README](https://gitea-dev.autobee.pro/frostbyte_neo/kage/blob/HEAD/README.md)
|
||
> 原作者、版权与许可证归属以原始项目及本仓库 LICENSE 文件为准。
|
||
|
||
# kage
|
||
|
||
[](https://github.com/tamnd/kage/actions/workflows/ci.yml)
|
||
[](https://github.com/tamnd/kage/releases/latest)
|
||
[](https://pkg.go.dev/github.com/tamnd/kage)
|
||
[](https://goreportcard.com/report/github.com/tamnd/kage)
|
||
[](./LICENSE)
|
||
|
||
**kage**(影,"shadow")会将网站克隆到一个可离线浏览的文件夹中,并剔除所有脚本。它会在真实的无头 Chrome(headless Chrome)中打开每个页面,等待页面稳定后,快照人类所见到的 DOM,然后删除所有 JavaScript,并将 CSS、图片和字体下载到本地路径。最终落盘的内容看起来与线上站点一致,且不会执行任何代码。
|
||
|
||
[安装](#install) • [快速开始](#quick-start) • [命令](#commands) • [克隆](#clone) • [打包](#pack-it-into-one-file) • [双击应用](#a-double-click-app) • [原生窗口](#a-real-window-not-a-browser-tab) • [工作原理](#how-it-works)
|
||
|
||

|
||
|
||
你早已熟悉这个问题。你对想保存的页面点击「另存为」,六个月后打开却发现一片空白、转圈不停,或者副本仍在尝试连接早已不存在的分析服务器。这页面从未真正属于你。它只是别人 JavaScript 的瘦客户端。
|
||
|
||
kage 走了另一条路。它驱动真实浏览器,让页面完成该做的一切,抓取最终结果,然后从中剥离所有脚本。无追踪、无网络调用、无意外。只有 `.html` 文件,你可以直接从磁盘打开、交给朋友,或打包成单个文件后十年都不用管。
|
||
|
||
完整文档与指南见 **[kage.tamnd.com](https://kage.tamnd.com)**.
|
||
|
||
## 安装
|
||
|
||
```bash
|
||
go install github.com/tamnd/kage/cmd/kage@latest
|
||
```
|
||
|
||
更喜欢预编译二进制?可从 [releases](https://github.com/tamnd/kage/releases). 获取压缩包、`.deb`/`.rpm`/`.apk` 或校验和。也可以交给包管理器:
|
||
|
||
```bash
|
||
# 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 的容器镜像:
|
||
|
||
```bash
|
||
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 年阅读它们:
|
||
|
||
```bash
|
||
# 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
|
||
```
|
||
|
||
这就是完整流程。每篇文章、每张图片、每份样式表,都冻结在你的磁盘上,零网络即可运行。接下来两步可选但很方便:将整个镜像折叠成单个文件,并在独立窗口中打开。
|
||
|
||
```bash
|
||
# 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 的离线阅读服务 |
|
||
|
||
## 克隆
|
||
|
||
```bash
|
||
# 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` 会在已克隆文件夹上运行轻量静态文件服务器,使链接与资源像真实主机上一样解析:
|
||
|
||
```bash
|
||
kage serve $HOME/data/kage/paulgraham.com
|
||
# open http://127.0.0.1:8800
|
||
```
|
||
|
||
## 打包成单个文件
|
||
|
||
镜像是文件夹,便于浏览,却不便于搬运。复制成千上万个小文件很慢,「给你这个目录」也不是好送的礼物。`kage pack` 将整个镜像折叠为一个制品,形状由你选择:开放的 ZIM 归档,或本身就是站点的单个可执行文件。
|
||
|
||
### 单个 ZIM 文件
|
||
|
||
```bash
|
||
kage pack paulgraham.com # -> paulgraham.com.zim
|
||
kage open paulgraham.com.zim
|
||
```
|
||
|
||
ZIM 是专为此而生的开放文件格式:将整个网站(或整本维基百科)压缩、索引后塞进一个只读文件。kage 将整个镜像写入其中,文本经 zstd 压缩,媒体原样存储。它是 [Kiwix](https://kiwix.org), 背后的格式——人们用这一离线内容项目把维基百科、Stack Overflow 和 Project Gutenberg 带上船、带进没有互联网的教室、装进手机度过长途飞行。由于该格式是 documented standard(有文档的标准),而非 kage 的发明,你今天制作的 `paulgraham.com.zim` 多年后仍可在任何 ZIM 阅读器中打开。
|
||
|
||
所以你并不被锁定在 kage 上。`kage open` 是重新接入的最快方式,但同一个文件也适用于更广泛的 Kiwix 生态系统:
|
||
|
||
```bash
|
||
kage open paulgraham.com.zim # read it back with kage
|
||
kiwix-serve paulgraham.com.zim # or serve it with Kiwix at http://localhost
|
||
```
|
||
|
||
你也可以在 [Kiwix 桌面应用](https://kiwix.org/en/applications/), 中双击该文件,或在 [Android 或 iOS 版 Kiwix](https://kiwix.org/en/applications/) 上加载它,在手机上阅读你的镜像。有一点需要注意:kage 会写入结构合法、带有标准元数据的归档文件,但不会构建 Kiwix 官方包自带的全文搜索索引,因此浏览和点击在各处都能正常工作,但阅读器内的搜索功能会受限。
|
||
|
||
打包是确定性的。同一镜像始终会生成字节级完全相同的文件,归档 UUID 由内容派生而非随机生成,因此包可以安全地进行校验和与缓存。裸主机名会相对于默认输出目录解析,因此 `kage pack paulgraham.com` 在 `kage clone paulgraham.com` 之后就能直接使用。
|
||
|
||
### 自包含二进制文件
|
||
|
||
`--format binary` 将归档粘接到 kage 的副本上,生成单个可执行文件,运行后即可离线提供该站点。你把它发给任何人都无需安装任何东西:不需要 kage,不需要 ZIM 阅读器,什么都不需要。
|
||
|
||
```bash
|
||
kage pack paulgraham.com --format binary -o paulgraham
|
||
./paulgraham
|
||
```
|
||
|
||
附加的归档与平台无关;只有基础可执行文件携带架构信息。默认情况下 kage 会追加到自身,因此你会得到适用于运行它的那台机器的查看器。将 `--base` 指向为其他操作系统构建的 kage(从 [release](https://github.com/tamnd/kage/releases); 获取,每个平台都会提供)即可在你自己的机器上生成适用于该平台的查看器。kage 会读取基础可执行文件的头部以确定目标平台,因此 Windows 查看器会自动获得 `.exe` 名称:
|
||
|
||
```bash
|
||
# 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` 包:
|
||
|
||
```bash
|
||
kage pack paulgraham.com --app # -> paulgraham.app
|
||
open paulgraham.app # or double-click it in Finder
|
||
```
|
||
|
||
在 Linux 上,将 `--base` 指向 Linux 版 kage,你会得到一个带 `.desktop` 启动器(`Terminal=false`,因此没有控制台)的 [AppImage](https://appimage.org)-style `.AppDir`。如果已安装 [`appimagetool`](https://github.com/AppImage/appimagetool),kage 会将其合并为单个可双击的 `.AppImage`:
|
||
|
||
```bash
|
||
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 子系统,因此打包在其上的查看器打开时背后不会出现控制台:
|
||
|
||
```bash
|
||
# 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 的文章,离线阅读,放在一个看起来、用起来都像真正应用的东西里:
|
||
|
||

|
||
|
||
```bash
|
||
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` 沿用同一思路:镜像中的链接已经是镜像相对路径,这些路径与归档内容条目一一对应,因此在已提供服务的页面中点击会直接命中正确的条目,完全无需重写。
|
||
|
||
## 从源码构建
|
||
|
||
```bash
|
||
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 签名:
|
||
|
||
```bash
|
||
git tag v0.1.1
|
||
git push --tags
|
||
```
|
||
|
||
镜像标签不带 `v` 前缀(`ghcr.io/tamnd/kage:0.1.1`)。Homebrew 和 Scoop 步骤会在其 token 存在之前自动禁用,因此首次发布无需额外密钥即可工作。
|
||
|
||
## 许可证
|
||
|
||
MIT。参见 [LICENSE](LICENSE)。
|