broodmother整体设计 - novoland/broodmother GitHub Wiki

一. 概述

爬虫可以抽象为一个简单的生产者消费者模型:一个队列保存所有待抓取url,抓取线程poll,下载页面,提取子url并存放到队列。

BroodMother 的核心是 Frontier 和一套由 Handler、Pipeline、Sink等接口定义的责任链机制。它们之间的运作方式如下:

Frontier 是维护 URL 的管理器,可以简单地看成一个大的 Queue,CrawlThread 不停地从 Frontier 拿出URL进行处理。URL 在出队时都将被装配上一个 Pipeline,URL 出队后即进入 Pipeline中流转。

Pipeline 由若干个 Handler 和一个 Sink 组成, 是典型的责任链的实现方式(这里参考了Netty3的设计)。URL 在每个 Handler 中被处理,Handler 在处理完毕后可以选择结束(finish)、前进到下一个Handler(proceed),甚至是跳转到某个 Handler(Jump)。

Pipeline 最末端挂接着一个 Sink,无论什么情况,如 Handler选择终止流程 / 最后一个handler proceed / 处理时出现异常,url 都将被送如这里进行处理。URL 将被送入Sink 做统一处理,比如判断是否出现错误需要重试等等。

Web 抓取的需求千变万化,为了提供最大的可扩展性,Pipeline 的结构完全由用户负责,BroodMother 内部对常见的功能提供了众多 in-box 的 Handler,用户也可根据自己的需求编写自己的版本。

BroodMother 依赖 Spring IOC 组装部件,方便切换核心组件实现,修改 Pipeline 结构。

二. 典型的 CrawlPipeline

现在我们更进一步地考察一个URL被抓取的细节。通常,处理URL的典型过程为:

解析DNS –下载页面 – 抽取链接 – 保存页面 – 抽取的链接送入Frontier

BroodMother 对这些环节都提供了默认的实现。

1. DnsResolver

DnsResolver 用于解析待抓取 URL 的 DNS,并基于 EhCache 缓存,序列化使用 Jackson json。

2. NioFetcher

抓取网页的 Handler 称为Fetcher,目前只有基于 JDK NIO (Reactor模型)的实现。

由于 NIO 每次 Read 都只能拿到一个数据片,需要在数据不完整的时候 pause Pipeline,完整时再 resume。Pipeline 也提供了相应的接口实现这种行为。

NioFetcher 还负责将服务器返回的二进制数据解析成http响应以供后续 handler 使用。

3. ExtractorHttp & ExtractorHTML

从页面提取 URL 的 handler 称为 Extractor,目前有两个实现:

  1. ExtractorHttp 从 http 响应 header 中提取 URL,主要用来处理重定向;
  2. ExtractorHTML 使用 Jsoup 从 HTML 文本中抽取 a、frame、iframe (可配置) 这3种标签的URL。

4. SimpeFileWriter

一个简单的页面写出器。将抓取到的网页分host写入job根目录下的page目录中。

5. CandidateHandler

抽取而得的连接我们称为 Candidate。CandidateHandler 对所有的Candidate 进行过滤、必要的计算,最后送往 Frontier。这个过程也是通过一个 Pipeline 来完成的,称为 Candidate Pipeline,典型的组成如下:

5.1 CandidateJudge

判断某个 Candidate 是否需要丢弃,它的内部使用一个 DecideRule 对 URL 进行test,如果结果为REJECT,则丢弃。用户需要提供自己的 DecideRule,实现自定义的过滤逻辑。

5.2 FrontierPreparer

对 Candidate 进行必要的准备,如计算URL的优先级、计算URL所述的 WorkQueue 的 key、计算 Canonical 化的URL(需要被后续判重器使用)。这些动作均被抽象成了相应的策略并提供了默认实现,用户可以替换自己的实现。

FrontierPreparer 也负责过滤重复 URL。

最后,Candidate Pipeline 最末的 Sink 负责将 Candidate 送入 Frontier。

整个流程如下图:

三. 基于 WorkQueue 的 Frontier

Frontier 保存着所有待爬取的 URL,这些 URL 通常按照 host 的划分为多个 WorkQueue,WorkQueue保存该 host 下所有待抓取的 url。为了防止对 web server 造成过大的负荷 IP,对每个 WorkQueue 的两次抓取之间默认必须等待一定时间间隔(基于 JDK 的 DelayedQueue 实现)。

WorkQueue 内置两种实现:

  1. 基于内存的 MemWorkQueue,适用于小数据量;
  2. 基于 Redis 的 RedisWorkQueue,可持久化,可改造成分布式爬虫。

Frontier 依赖 WorkQueueFactory 生产 WorkQueue,这也是可配置的。

四. URL 判重

CandidatePreparer 依赖 UrlUniqueFilter 对 Candidate URL 判重。

Url判重器的实现可以是多种多样的,比如最简单的基于内存的HashSet,或者使用基于外存的B-tree,或者基于数据库等等。BroodMother提供基于 Bloom Filter 算法的判重器(BloomFilterUrlUniqueFilter)。