场景
设计一个网络爬虫系统,用于抓取互联网上的网页内容。
需求点:
- 给定种子 URL,爬取网页及其链接
- 支持可配置的爬取深度和频率
- 遵守 robots.txt 协议
- 避免重复爬取
- 每月爬取 10 亿个网页
估算
- 月爬取量:10 亿页
- 日爬取量:10 亿 / 30 ≈ 3300 万页/天
- QPS:33,000,000 / 86400 ≈ 382 页/秒
- 峰值 QPS:约 800 页/秒
存储估算:
- 平均每页 500KB(HTML + 元数据)
- 月存储量:10 亿 × 500KB = 500TB
- 需要压缩存储和增量更新
设计
整体架构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Seed URLs │────▶│ URL │────▶│ URL │
│ │ │ Frontier │ │ Fetcher │
└─────────────┘ └─────────────┘ └─────────────┘
▲ │
│ ▼
┌─────────────┐ ┌─────────────┐
│ URL │◀────│ Content │
│ Extractor │ │ Parser │
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Storage │
│ (S3/HDFS) │
└─────────────┘
核心组件
URL Frontier(待爬取队列)
管理待爬取的 URL 队列,负责:
- 优先级调度:重要页面优先爬取
- 礼貌性控制:同一域名间隔爬取
- 去重:避免重复添加
数据结构:
┌─────────────────────────────────────────┐
│ URL Frontier │
├─────────────────────────────────────────┤
│ 优先级队列 (Priority Queue) │
│ ├── P1: news.com, tech.com │
│ ├── P2: blog.com, forum.com │
│ └── P3: other sites │
├─────────────────────────────────────────┤
│ 域名队列 (Per-host Queue) │
│ ├── news.com: [url1, url2, ...] │
│ └── tech.com: [url3, url4, ...] │
└─────────────────────────────────────────┘
URL Fetcher(下载器)
- 多线程并发下载
- 支持 HTTP/HTTPS
- 设置合理的超时和重试
- 遵守 robots.txt
Content Parser(内容解析器)
- 解析 HTML,提取文本内容
- 提取页面中的链接
- 处理不同字符编码
URL Extractor(链接提取器)
- 从页面中提取新链接
- URL 规范化(去除锚点、统一格式)
- 过滤无效链接
遍历策略
BFS(广度优先)
从种子页面开始,逐层向外扩展。
优点:适合发现热门页面 适用:搜索引擎爬虫
DFS(深度优先)
沿着链接深入爬取,再回溯。
优点:内存占用小 适用:特定网站深度爬取
URL 去重
方案一:HashSet
Set<String> visited = new HashSet<>();
缺点:内存占用大,10 亿 URL 约需 100GB
方案二:Bloom Filter
BloomFilter<String> filter = BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
1_000_000_000, // 预期元素数
0.01 // 误判率
);
优点:内存占用小(约 1.2GB),查询 O(1) 缺点:有小概率误判(会漏爬)
方案三:分布式去重
使用 Redis 存储已访问 URL 的哈希值。
礼貌性爬取
- 遵守 robots.txt:检查是否允许爬取
- 限制频率:同一域名间隔 1-2 秒
- 设置 User-Agent:标识爬虫身份
- 限制并发:单域名最多 1-2 个并发
# robots.txt 示例
User-agent: *
Disallow: /admin/
Crawl-delay: 2
容错处理
- 超时重试:指数退避重试
- 死链处理:记录失败次数,超过阈值放弃
- 断点续爬:持久化爬取状态
总结
| 组件 | 技术选型 |
|---|---|
| 任务队列 | Kafka / Redis |
| URL 去重 | Bloom Filter / Redis |
| 内容存储 | S3 / HDFS |
| 解析器 | Jsoup / BeautifulSoup |
核心挑战:
- 规模性:海量 URL 的存储和去重
- 礼貌性:避免对目标网站造成压力
- 鲁棒性:处理各种异常和恶意页面
