在大规模爬虫领域,Michael Nielsen 2012 年的爬取实验长期作为基准参考。然而过去十余年间,硬件能力、网络带宽、Web 内容形态都发生了根本性变化。近期 Andrew Chan 的实验重新回答了这个问题:在 24 小时时限、数百美元预算约束下,能否爬取十亿级页面?实验结果为 1.005B 页面、25.5 小时、462 美元。这一数字背后隐藏着多个值得深入探讨的工程决策,涉及架构选择、瓶颈分析、存储成本等多个维度。
架构选择: colocated node 为何优于 disaggregated design
传统爬虫架构倾向于将功能模块彻底解耦 —— fetcher 池、parser 池、数据存储各司其职,通过消息队列或分布式存储通信。这种设计的优势在于各组件可独立扩展,但在小团队、低预算场景下带来了显著的协调开销与资源闲置问题。Andrew Chan 最终采用的是一种 colocated node 架构:每个节点自包含完整的爬虫功能栈,包括一个 Redis 实例、多个 fetcher 进程和 parser 进程,节点之间仅通过域名前缀分片,不存在跨节点通信。
这种选择的核心动机在于实验阶段的成本约束。Chan 从单节点优化起步,逐步扩展到 12 台 i7i.4xlarge 实例(每台 16 vCPU、128GB RAM、10Gbps 网络、3.75TB 实例存储),每台机器运行 1 个 Redis 进程、9 个 fetcher 进程和 6 个 parser 进程。值得注意的是,他在尝试垂直扩展单台 i7i.48xlarge 时遭遇了软件层面的瓶颈 —— 尽管硬件聚合了 12 台 4xlarge 的资源,但跨进程资源竞争导致吞吐量远低于预期,最终回退到水平扩展方案。
对于工程团队的启示是:在预算有限且追求极致吞吐的场景下,colocated 架构的简单性可能优于教科书式的 disaggregated 设计。尤其当爬取任务可以在域名前缀层面完成分片时,去中心化的无共享架构能显著降低系统复杂度。
解析成为新的 CPU 瓶颈
一个反直觉的发现是:在现代爬虫中,解析 HTML 的 CPU 开销已经超越了网络 IO 成为主要瓶颈。这一结论与传统的带宽优先认知形成鲜明对比,根源在于 Web 内容的体积变化。2012 年 Nielsen 估计的平均页面大小约为 51KB,而 Chan 的实测数据显示 P50 为 138KB,均值达到 242KB—— 增长近 5 倍。
更关键的变化在于解析库的选型。初始方案使用 lxml,在测试中 2 个 parser 才能勉强跟上 1 个 fetcher(1000 workers, 55 pages/sec)。切换到基于 Lexbor 的 selectolax 后,单 parser 进程吞吐量提升至约 160 pages/sec。Chan 还采取了 250KB 内容截断策略 —— 这一阈值高于均值且接近 2 倍 P50,保留了绝大多数页面的完整结构,对于链接提取等下游任务已足够。
对于构建爬虫系统的团队,建议的 parser 配置参数为:每个 parser 进程配置 80 个异步 worker(相比 fetcher 的 6000-7000 worker 明显更低,因为解析是 CPU 密集型任务),整体 fetcher 与 parser 进程比例可设为 3:2 左右。具体数值需根据页面大小分布进行实测调优,但核心原则是确保 parser 不会成为阻塞点。
SSL 握手税:现代 Web 的 CPU 成本
网络层面同样发生了结构性变化。尽管 DNS 解析不再是瓶颈(这要归功于大规模域名的缓存和高效解析),但 SSL/TLS 握手计算占据了约 25% 的总 CPU 时间。这一比例远超大多数工程师的预期 —— 在 10Gbps 网络远未饱和的情况下(实测约 8Gbps),CPU 先于网络带宽达到上限。
这是 Let's Encrypt 推动 HTTPS 全面普及的直接后果。截至 2025 年,Firefox 中加载的 SSL 页面比例已超过 80%。对于大规模爬虫而言,这意味着每个 HTTP 请求都隐含了一次 CPU 密集型的加密握手。工程应对策略包括:维护长连接池以减少握手次数、对高流量域名启用会话复用、在 fetcher 进程中对 robots.txt 和域信息配置 LRU 缓存以减少 Redis 往返。
如果追求极致性价比,一个可行的方向是使用支持硬件加速 TLS 的实例类型,或者在集群规模极大时考虑将 SSL 终止代理前置 —— 但这些优化通常只对超大规模爬虫有意义。
热域名前沿问题与容错设计
在 12 节点水平扩展时,Chan 遭遇了一个典型但少被提及的问题:部分热门域名的 frontier(待爬取队列)膨胀至数十 GB。Yahoo、Wikipedia 等链接密集型站点产生了数亿甚至数十亿条 URL,导致对应节点的内存迅速耗尽并触发 OOM。
这一问题的本质是 链接结构的幂律分布与礼貌爬取策略的冲突。为了遵守 70 秒同域名最小间隔的约束,frontier 必须保留大量待爬 URL,而高链接密度站点会产生不成比例的队列长度。Chan 的解决方案包括:手动将这些热域名加入排除列表、在运行中途重启受影响节点、依赖 Redis 持久化实现断点恢复。
对于工程实践,建议的前瞻性设计包括:为单个域名的 frontier 大小设置硬性上限(如 10GB),超出阈值后触发抽样或延迟策略;实现自动热域名检测与降级机制;将 visited 集合与 frontier 分离并使用 Bloom Filter 加速去重(允许少量假阳性以换取内存效率)。
存储经济学:实例存储相对于 S3 的成本优势
一个常被忽视的决策点是持久化存储选型。教科书方案通常推荐 S3,但针对 250TB 级别的 24 小时临时存储,S3 成本极高:标准层估算约 $5,183 / 月(仅存储,不含请求费用),快速层也需约 $2,046。
Chan 最终选择了 EC2 实例存储(i7i 系列),利用即付即用的临时盘特性实现了接近零的增量存储成本。这一选择在他的场景下完全合理 —— 数据仅需保留至爬取完成,且不存在多区域访问需求。但对于需要长期保留或跨服务共享数据的生产系统,S3 仍是更优选择,只是团队需要明确认知这一成本临界点。
可落地参数清单
综合上述分析,为计划构建或优化分布式爬虫的团队提取以下关键参数:
- 机器配置:i7i.4xlarge 或同级别存储优化实例,16 vCPU + 128GB RAM
- 进程配比:每节点 1 Redis + 9 fetcher + 6 parser
- Fetcher 并发:6000-7000 异步 worker(IO 密集型)
- Parser 并发:80 异步 worker(CPU 密集型)
- 同域名间隔:≥70 秒(礼貌爬取基准)
- 内容截断阈值:250KB(保留均值以上内容)
- Frontier 上限:单域名建议 ≤10GB,超出触发抽样
- 存储策略:短期大规模爬取用实例存储,长期归档用 S3
资料来源:本文核心数据与架构描述基于 Andrew Chan 的技术博客文章《Crawling a billion web pages in just over 24 hours》(andrewkchan.dev/posts/crawler.html)。