Browsertrix Crawler 是 Webrecorder 的归档型爬虫:一个 Docker 镜像,通过 Puppeteer 驱动真实的 Chromium,记录浏览器抓取到的所有内容,并将其写入 WARC——Web 存档的标准格式——还可以选择打包成带有索引、页面列表和日志的 WACZ 包。它和那些表面上看起来很像的抓取工具,最大的区别就在于用途不同。抓取工具追求的是数据,拿到字段后页面就可以丢掉;归档工具保存的则是一次访问本身——字节、响应头、到达顺序——这样即使网站后来发生变化甚至消失,页面也还能被重新打开。
Webrecorder 一直维护着这层 Web 基础设施,包括相关格式和回放栈,早在“归档”还不像一个产品类别的时候就已经在做了,而图书馆、新闻编辑室和研究人员至今仍在依赖它。
我在 Docker 中运行了 v1.14.0,并对一个本地测试环境进行验证,该环境包含四类刻意设计不同的端点;同时我也从两侧做了观测:一边是用于归档内容的 WARC 记录,另一边是服务器端的真实请求计数器。真正有用的区分并不只是静态和动态。Browsertrix 捕获到了一个运行时创建的链接和一个页面发出的 fetch(),但它没有请求 JavaScript 函数里两个未被调用的 URL 字面量。
链接到的 app.js 被完整归档了,包括那两个字面路径,但这两个端点都没有产生响应记录、请求记录或服务器命中。因为持有它们的函数根本没有执行。所以这篇文章评估的是 Browsertrix 作为“浏览器会话归档器”的能力,而不是“把源代码里提到的所有 URL 都列出来”的能力。
Browsertrix Crawler 到底是什么
很多人带着“抓取器”的预期来,用完却有些失望。默认流程里不会直接给你一份产品价格 CSV,把它当成抓取工具去期待这种结果,就像指望行车记录仪顺手给你写交通报告一样不现实。它输出的是可回放的浏览会话记录,而后续所有设计都围绕这个目标展开。
我在 2026 年 7 月 27 日测试了 v1.14.0:webrecorder/browsertrix-crawler:latest,digest 为 sha256:9d6800a8…,并通过 crawl --version 确认了构建版本。该项目采用 AGPL-3.0 许可。所有测量都在 macOS arm64 上通过 Colima 运行 Docker,在受控的本地测试环境中完成;服务器端计数器提供了不依赖 Browsertrix 日志和归档解析的独立证据。
AGPL-3.0 值得单独说一句:这是带网络使用条款的强 copyleft。若 Browsertrix Crawler 要放进商业产品里,而不是只作为独立工具运行,正式发布前务必让人把许可证看明白。这只是提醒,不是法律意见。
归档记录的是会话,不是源代码
我的测试环境提供了四类端点,刻意分开,是因为归档器会对它们做完全不同的处理:
- A 类——HTML 里的普通
<a href>。 四个页面加一个三级深度链路。地球上任何爬虫都能找到这些。 - B 类——放在从未执行函数中的 URL 字面量。 两个路径,
/api/js-endpoint-7和/api/js-endpoint-8,作为字符串静静躺在链接到的app.js里一个未调用的loadData()中。 - C 类——运行时生成的链接。 一个
<a href>由 JavaScript 片段拼接而成('endpoint' + (6 * 7)),随后被挂到 DOM 上。连续路径/runtime-only/endpoint42并不存在于任何已服务的字节里。 - D 类——页面确实发出的
fetch()。 路径以同样方式拼接('runtime-xhr-' + (33 * 3)),然后在页面加载时真实请求。
对于 C 类和 D 类,完整路径都没有以连续字符串的形式出现在已服务文件中。因此,它们在服务器端的命中和响应记录,恰好说明这个测试环境里确实执行了运行时构造与请求过程。
哪些被捕获了,哪些没有

这里用了两个彼此校验的工具:WARC 响应记录(归档里有什么)和测试环境服务器端的命中计数器(到底请求了什么)。两者在每一项上都一致。
| 端点类别 | WARC 中是否有响应记录 | 实际是否被抓取(服务器端) | 结论 |
|---|---|---|---|
A — HTML <a href> | 4/4 | 4/4 | 已捕获 |
| A — 深度链路(3 层) | 3/3 | 3/3 | 已捕获 |
| B — 未执行 JS 中的 URL 字面量 | 0/2 | 0/2 | 未捕获 |
| C — 运行时注入的链接 | 是 | 是 | 已捕获 |
D — 运行时 fetch() | 是 | 是 | 已捕获 |
C 类之所以重要,是因为它区分了真正的浏览器和静态爬取。默认的链接提取会读取渲染后的 DOM(根据 common options 文档,即 a[href]->href),所以只有 JavaScript 执行后才出现的链接,仍然会被加入队列、抓取并归档。D 类之所以被捕获,是另一个原因:页面自己发起了请求,而归档器正好位于网络路径上,把经过的流量都记录了下来。
关于 C 类有一个必须说明的边界:我的链接是在页面加载时同步注入的。那些在 Browsertrix behaviors 运行期间才出现的链接,属于另一个场景,而且针对这一点还有一个开放 issue——#723, "Links on pages that are discovered during behaviors are not extracted"。我没有测试那个场景,所以也不会对它下结论。
文件被归档了,端点却没有
要确认 B 类为什么漏掉,不能只看汇总数字,而得逐条查看 WARC 记录。
app.js 确实在归档里——一条响应记录,222 字节的 JavaScript 正文——而且 B 类的两个字面量都原封不动地出现在其中。与此同时,整个文件里没有任何一条记录把 /api/js-endpoint-7 或 /api/js-endpoint-8 作为目标 URI:零响应记录,零请求记录。这两个字面字符串在整个归档里各只出现了一次,而这两次都位于 app.js 被保存的正文中。
这就排除了一个最无聊的解释:"app.js 压根没被抓到。" 归档器保存了引用这些端点的文件,却没有对它们发起请求,因为 loadData() 从未被调用。Browsertrix 默认 behaviors 是开启的——autoplay、autofetch、autoscroll、siteSpecific——但 autofetch 也没把它们救回来;这很合理,因为只要读一下 autofetch 的说明,你就会发现它抓的是 img 的 srcset、样式表和 data-* URL,而不是藏在函数体里的字符串字面量。
为了做一个同场对照,我也运行了 Katana v1.6.1,命令是 katana -u <seed> -jc -silent -nc -d 4。它的原始 discovery summary 在这两个刻意构造的 JavaScript 类别上记录了相反的结果:
| 你想找的内容 | Browsertrix v1.14.0 | Katana v1.6.1,标准 -jc |
|---|---|---|
| 已服务 HTML 中的链接 | 找到 | 找到 |
| 运行时注入到 DOM 的链接 | 找到(通过渲染后 DOM 提取) | 在没有 headless 模式时会漏掉 |
页面实际发出的 fetch() | 找到(作为流量记录) | 漏掉——因为没有执行 |
| 从未执行的 JS 中的 URL 字面量 | 漏掉(0/2) | 找到(同一测试环境下 2/2) |
| 包含这些字面量的 JS 文件 | 完整归档 | 被解析,但没有保留 |
两个命令使用的是同一套测试环境和同一批端点名称。这个表并不是在给浏览器爬虫和静态爬虫做泛化排名;它只是说明,端点清单和会话保留需要的是不同的覆盖测试。
“是真浏览器,所以 JavaScript 做的事它都能抓到”——这句你会在很多文章里看到。但这说得过头了。它抓到的是已经执行并产生的流量。代码里如果只是引用了某个 URL,却从未真正调用它,就不会有流量,也就不会有记录。
回放正文都在,但有一件事我没验证
对于两个运行时生成的端点,我把 WARC 里的 HTTP 响应正文取出来确认过,里面就是服务端返回的 JSON:运行时注入链接的目标是 206 字节,运行时 fetch() 的目标是 201 字节。所以它们不是只指向空内容的索引占位符——内容本身就在归档里,而这正是回放系统能够把它们展示出来的前提。
但我没有启动 pywb 或 replayweb.page 去真正渲染这个归档。归档里有正文,与能否正确回放,是两件不同的事;这次测试只覆盖了前者。回放行为、真实性控制、证据链保全以及证据可采性,都需要单独验证。
生产级采集需要更宽的验收测试
这个测试环境回答的是一个很窄的问题:同步运行时链接和页面发出的请求,是否真的变成了网络流量和归档记录?而生产级的保存任务,通常还有很多方式会出错,即便最后仍然生成了一个有效的 WACZ。
先从回放开始。用实际要使用的回放系统打开这个包,把一组固定页面与采集时的参考结果进行对比。检查渲染文本、图片、样式、导航,以及对记录有意义的交互。再看回放浏览器的网络面板,确认有没有缺失的子资源。WARC 里可以有响应正文,但回放仍可能失败,因为重写、索引、时序、源站或依赖项对不上。这篇评测没有跨过这条边界。
动态行为也应该单独准备测试集。这里的 C 类链接是在页面加载时同步出现的;真实应用可能是在定时器、滚动、同意弹窗关闭、路由变化、自定义元素或一长串 API 调用之后才出现内容。把每一种你依赖的行为后面都放一个已知目标,并同时验证服务器命中和归档正文。Browsertrix 默认 behaviors 对这个测试是有用的输入,但它们并不能证明所有延迟状态都真的到达了。尤其是当链接是在 behaviors 期间而不是初始页面执行时出现的,issue #723 就更值得关注。
带认证的采集还会引入会话问题。要确认登录状态进入了浏览器配置文件,能否撑过所需的跳转,并且不会泄漏到本应隔离的采集任务中。要跑通 token 刷新和登出路径。如果归档里包含私密或个人信息,还要把访问控制和保留策略一起纳入同一套验收计划。一次技术上完整的采集,后续也可能被处理得一团糟。
如果服务工作线程、流媒体、WebSocket、下载、跨域 frame、签名 URL 对你的目标很重要,那每一种都应该找一个代表页面测试。这 11 页测试环境并不能说明它们的表现。它也没有说明爬虫在页面保持活动数分钟、在常规稳定窗口之后继续发请求、或者需要用户手势时会怎样。不要把“真实 Chromium”理解成一揽子覆盖承诺;应当明确你要保留哪些浏览器行为,并让每一种都能被观察到。
最后,要保留足够的证据来排查漏抓。保存准确的镜像 digest 和命令、Browsertrix 日志、页面列表、索引、WARC/WACZ 校验和、可获得的服务器端请求证据,以及一份小型的 ground-truth 清单。对于重复采集,还要把时间、配置和环境与产物一起记录下来。这些记录不会自动产生法律可采性,但它们能让技术结论可复现,并能暴露后续差异到底来自目标站点、爬虫还是回放栈。
这个小体积测试环境消耗了多少字节
这个测试环境每页只提供几百字节内容,所以它的开销比例不应该被拿去套到大资源网站上。在这个狭窄范围内,我测的是归档的组成,而不只是最终大小:
| WARC 记录类型 | 数量 | 内容字节数 | 占比 |
|---|---|---|---|
request | 14 | 6,912 | 40.6% |
response(实际页面内容) | 13 | 5,339 | 31.4% |
resource(每页一个 urn:pageinfo: JSON) | 11 | 4,527 | 26.6% |
revisit(去重的失效链接) | 1 | 154 | 0.9% |
warcinfo | 1 | 92 | 0.5% |
| 记录内容合计 | 40 | 17,024 | 100% |
各类型数量和字节总量来自公开的
capture-summary.json;占比以 17,024 字节的记录内容总量为分母。
在这个小体积测试里,请求记录比响应内容还多,而且“请求 + urn:pageinfo:”的字节数大约是响应负载的 2.1 倍。这描述的是这个测试环境的记录构成,不是通用的 WARC 比例。
在磁盘上,经过三次独立运行后:
| 指标 | 最小值 | 中位数 | 最大值 |
|---|---|---|---|
| 爬取墙钟时间(秒) | 28.22 | 29.75 | 30.27 |
| WARC.gz 字节数 | 24,174 | 24,250 | 24,262 |
| WACZ 字节数 | 53,446 | 53,523 | 53,533 |
| 已捕获响应负载(字节) | 5,339 | 5,339 | 5,339 |
按中位数来算,可以得到四个比例:
| 推导指标(中位数) | 数值 |
|---|---|
| 压缩后的 WARC / 已捕获响应负载 | 4.5× |
| WACZ / 已捕获响应负载 | 10× |
| 每页 WARC | ~2.2 KB |
| 每页 WACZ | ~4.9 KB |
而在 WACZ 包内部:
| WACZ 组件 | 在整个包中的占比 |
|---|---|
| WARC | 45% |
| CDX 索引 | 16% |
| 爬取日志 | 30% |
最后这一行最让我意外。对于这么小的一次爬取,归档包里将近三分之一装的不是网页,而是“爬取过程本身”的记录。
这个测量很稳定:三次运行里响应正文都完全一致(每次都是 5,339 B),而 WARC.gz 和 WACZ 的波动都低于 0.4%。
这些比例不能直接套到带图片、字体和大型脚本包的真实页面上。但结构上的结论成立:请求记录和 page-info 记录会带来与负载大小无关的额外开销。在规划生产存储前,要先拿一批具有代表性的样本做测量;不要拿测试环境里 10× 的比例去简单乘以整个语料库。
安装与应提前预留的磁盘空间
只要 Docker 或 Colima 已经安装并运行,Browsertrix 的安装流程就是 docker pull webrecorder/browsertrix-crawler:latest,然后执行 docker run … crawl --url … --generateWACZ。镜像里自带 Chromium,因此不需要另外准备浏览器或 Python 环境。
而这份便利的代价,在我测到的几次运行里是这样的:
| 你需要预算的项目 | 实测 |
|---|---|
| 镜像下载 | ~1 GB |
| 镜像解压后占用磁盘 | 3.51 GB |
在一个提供几 KB 内容的测试环境上,经过若干次 11 页运行后,crawls/ 目录(WARC、WACZ、浏览器配置文件数据) | ~116 MB |
| 11 页测试环境一次爬取的墙钟时间 | 28–30 秒 |
容器是摩擦成本的主要来源,而下载和解压这一项,正是把浏览器打包进去的代价。我运行时设置了 --shm-size 1g;因为测试环境在宿主机上,而爬取是在容器里执行的,所以我还需要 --add-host:host.docker.internal:host-gateway,并把测试环境绑定到 0.0.0.0 而不是 loopback。如果你爬的是公网,这一步网络配置可以省掉;但如果你要归档的是自己机器或内部测试主机上的内容,最好预留半天来调这些。
输出目录是最容易被低估的那一项。把这种增长趋势外推到真实爬取任务时,请在开始前就规划好存储,而不是等到凌晨三点磁盘满了才补救。
仅凭这 11 页测试,浏览器启动本身大概率就占了相当一部分 28–30 秒的总时长,但我没有把启动、导航和打包时间拆开。因此,这个运行结果无法推出单页吞吐量结论。
不要拿这个测试环境的比例乱做容量规划
更可靠的容量规划方式是经验测量。选取能够代表目标站点真实分布的页面:轻量应用壳、图片很多的落地页、文档下载、长文章,以及如果范围包括认证内容,还要加上登录后的视图。按预期的 behaviors 和打包设置分别采集每一类。测量响应负载、WARC、WACZ、索引、日志、浏览器配置文件残留,以及运行过程中保留的任何临时工作区。如果打包过程会短暂保留多份副本,峰值磁盘使用量和最终包大小一样重要。
要把固定成本和可变成本分开。3.51 GB 的容器镜像是部署开销,在同一 worker 上可以被很多次采集共享。请求记录、page-info 记录、索引和页面列表会随爬取活动增长。响应正文高度依赖目标站点,而日志则依赖运行时长和日志详细程度。保留和复制又会在爬取行为之外,独立放大最终集合的体积。把这些都混成“每页多少字节”的容量模型,会非常脆弱。
压缩和去重也需要代表性内容。这个测试环境的响应正文在三次运行里完全一致,但这并不代表带变化广告、时间戳、个性化响应或防缓存资产 URL 的页面。若重复采集是项目的一部分,就要测同一批页面的连续采集,并查看 revisit 记录,而不是想当然地认为“看起来没变”的页面一定很好去重。同样,也要测试日志和索引是否以与保存负载相同的复制级别保留。
从运维角度看,最好在采集开始前就设置告警阈值。监控可用空间、每个集合的增长、打包失败,以及浏览器配置文件或临时目录的大小。要做一次从已存 WACZ 恢复的演练,而不仅仅是跑一遍校验和。上面的比例之所以有用,是因为它们揭示了系统里有哪些组成部分;而代表性样本才会告诉你,它们在你的站点上会有多大。
把这些假设写在容量估算旁边,并在试点爬取后重新审视。
通过两个控制项测试范围纪律
会乱跑的归档爬虫是现实中的运维风险——你可能一次性得到法律麻烦和存储账单。我的首页链接到了 http://outofscope.test:<port>/page/out,这是指向同一测试环境的不同主机名,所以带着那个 Host 头的命中,能证明发生了越范围请求,而且与真实互联网流量无关。
| 配置 | 是否抓取到范围外主机? | 服务器端命中数 |
|---|---|---|
--scopeType prefix(默认) | 否 | 0 |
--scopeType any | 是 | 2 |
第二行之所以重要,是因为它让第一行有了意义。在 any 模式下,这个链接被访问了两次,所以它确实可达——默认 prefix 范围下的 0 次,说明的是真正的范围约束,而不是爬虫没看见这个链接。上游还有一个关于其他配置下越范围访问的开放报告,#788,但我没有在这个测试环境的默认 prefix 范围下复现出来。知道它存在就够了;我不会声称自己见到了它。
鲁棒性方面,表现得好得很平常:一个返回 HTTP 500 的路由和一个失效链接都被请求到了,爬取过程干净退出,并生成了有效的 WARC 和 WACZ,失效链接则被保存为去重后的 revisit 记录,没有把流程搞崩。
优点和缺点
优点
- 能捕获运行时注入的 DOM 链接和页面发出的
fetch()调用——在归档里和服务器端都已确认,而且这些路径在任何源文件里都不存在字面量。 - 静态 HTML 和深度遍历都完整:4/4 链接、3/3 深度链路,没有漏项。
- 在 Docker/Colima 运行起来之后,一条
docker run就能产出 WARC 和 WACZ;Chromium 已经包含在镜像内。 - 默认
prefix范围下没有任何越范围抓取;any会按文档扩大范围,所以这个开关确实按说明工作。 - 输出是基于标准的归档(WARC,并打包成带 CDX 索引和页面列表的 WACZ),不是某种私有格式的黑箱。
- 归档几乎可确定:三次运行的负载字节完全一致,磁盘大小波动低于 0.4%。
- 失败处理干净:500 路由和坏链接都没有中断爬取。
缺点
- 未执行的 JavaScript 中的 URL 字面量就是不会被发现(0/2),即使包含它们的文件被完整归档了也一样。如果你的目标是端点发现,这虽然符合设计,但仍然是真实的覆盖缺口。
- 体积重:镜像拉取约 1 GB,落盘后 3.51 GB,而且输出目录增长很快。
- 在小页面上字节开销很明显——请求记录加 pageinfo 记录甚至超过了实际负载,WACZ 的约 30% 还是爬取日志。
- AGPL-3.0 意味着商业嵌入需要认真做合规工作。
- 它不是结构化数据工具。没有 schema,没有字段映射,最后也不会直接给你整齐的表格行。
- 每页吞吐量是有意放慢的,因为每一页都得经过真实浏览器。
适合谁,不适合谁
Browsertrix 适合那些需要的不是“提取后的行”,而是“浏览器抓取资源的归档”的团队。在这个测试环境里,运行时抓取到的响应正文已经进入了 WARC,并被打包进 WACZ。图书馆、新闻机构和研究人员都是合理的用户,但如果要进入生产环境,还应该单独测试回放、认证、服务工作线程、同意流程、延迟行为、流媒体资源、保留控制,以及任何证据处理要求。
如果你真正要的是数据,就别用它。若目标是“把这 400 个页面里的每个产品和价格都给我做成表格”,归档器会是一个很绕的路径——你会先归档出几 GB 数据,然后还得自己针对 WARC 文件写提取代码。若你是在摸一个应用的 API 面,也别用它,因为 B 类结果已经很明确地说明:静态 JavaScript 解析器能找到 Browsertrix 根本碰不到的端点。还有,如果你天生排斥 Docker,或者你所在的环境连 3.5 GB 镜像都成问题,那它也不会为你改变。
授权与保留
范围控制并不等于授权。开始爬取之前,就要定义允许访问的主机、保留期限和归档访问权限,尤其当持久化采集可能包含个人数据时。prefix/any 的测试说明的是配置会改变网络可达范围;它并不能说明某一次特定采集在法律上是否允许。
相关阅读:网页抓取与归档的法律影响。
按所需输出选择替代方案
按产物来选。Browsertrix 面向的是 WARC/WACZ 保存。像 Playwright 这样的浏览器自动化库能提供可编程页面,但捕获和打包要你自己处理。端点发现型爬虫负责枚举 URL,而提取工具则返回文本或结构化记录。它们可以共享浏览器,却解决的是不同的问题。
相关阅读:Heritrix 评测。
披露: Thunderbit 是发布方自家产品,但没有在这个 Browsertrix 测试环境中测试。它属于托管式提取工具,输出的是页面文本或结构化数据,而不是标准化归档。此评测只支持关于输出边界的判断,不构成性能或能力对比结论。
结论
如果你需要的输出是 WARC/WACZ 归档,而且容器化 Chromium 也符合你的部署方式,那就用 Browsertrix Crawler。在这个测试环境里,归档记录和服务器命中在同步运行时 DOM 与页面发出的 fetch 请求上是一致的,默认 prefix 范围成功排除了第二个主机,失败路由也没有阻止有效归档的生成。
在生产使用前,请验证回放、延迟行为、带认证会话、服务工作线程、代表性页面上的存储构成,以及许可证义务。本次测试覆盖的边界更窄:从未执行的代码里引用的 URL,即使对应脚本已经被保存,也不会为它们产生请求或归档记录。
试用 Thunderbit 进行网页数据提取 Get Started Free
常见问题
这里的 WARC 和 WACZ 有什么区别? WARC 里存的是捕获到的请求、响应以及相关记录。WACZ 则把 WARC 与索引、页面列表、元数据和日志一起打包,方便分发和回放工具使用。本文评测了这两种包,但没有实际渲染回放结果。
应该怎样估算存储空间? 要测代表性页面,并把样本中的完整 WACZ 组成部分都保留下来,包括日志和索引。本文中的比例来自响应正文非常小的特殊测试环境,不适合直接乘以生产环境的 URL 数量。
它会不会跑出我指定的网站?
根据我的测试,默认设置下不会。使用 --scopeType prefix 时,指向不同主机名的链接一次都没被抓取;切换到 --scopeType any 后,该链接被抓取了两次,说明它确实可达,也说明默认设置下的 0 次抓取是真正的范围约束。上游有一个关于其他配置下越范围访问的开放报告,我没有在默认模式下复现,因此还是要检查你自己的范围设置,不要想当然。
如果我要证明回放保真度,必须测试什么? 把 WACZ 加载到你打算使用的回放系统里,把渲染页面、交互和必需的子资源与线上或参考采集结果进行对比。WARC 里有正文是必要条件,但这本身不能证明回放渲染正确。
Browsertrix 会不会把归档页面直接变成结构化表格? 不会。它的输出是归档包,不是筛选好的字段表。如果交付物是产品、价格、联系人或其他 schema,你仍然需要在采集后再做一次提取,或者改用一种以结构化数据为主要输出的工具。


