nutch v1.9源码分析(4)——injector分析

1.1  inject

1.1.1  职责

       将文本文件中的种子url初始化到系统存储所有url列表的库(crawldb)中。爬虫系统本质上是图遍历,最终目的是发现整个网络图(只不过nutch的图遍历与传统《数据结构》中的图遍历算法不同之处在于——后者一般是在图拓扑结构已知的情况下,以某种策略访问每个节点,而前者是访问之前图拓扑未知,是在访问的过程中一边发现新的节点,一边遍历的过程)。图遍历需要指定从哪个(些)起点开始遍历。inject相当于为nutch遍历整个网络图指定了入口,由于考虑了并行化,nutch可以指定多个起始位置开始并行遍历整个网络图,所以可以指定多个seed url[2]

1.1.2  Usage

injector <crawldb> <url_dir>,参数解释如下:

1)        crawldb:是injector的输出路径,用来存储所有爬取url数据库(即crawldb)文件夹路径。

       crawldb存储了<url:CrawlDatum>之之间的映射关系,物理上以HadoopSequenceFile文件的形式存在于HDFS上(当然该HDFS不一定是分布式的,单机的话也可以是本能地文件系统)。

2)        url_dir:是injector的输入目录,可以含有子目录,各级目录中的文本文件都会按照如图5格式进行解析:

图5 种子url文本文件的格式

        每个文本文件每行定义一个url和若干可选的元数据,之间以制表符切分,元数据格式为key=value形式,元数据的key除了以下3个保留的key值之外,可以任意取值(但是中间不能有\t):

1)        nutch.score

        该key定义了url的得分(即重要程度),nutch给url计算重要性的具体做法可参见[6],实际是pageRank算法,该算法将网页和网页之间的链接视为源网页对目标网页的一次投票,该算法对一般会对每个网页设置初始重要程度为相等的值,nutch采用的默认值是1.0f。当然,用户为了使得某个网页在inject之后,能够有更大的可能被选中加入到fetchlist,加入到fetchlist就能在接下来的fetch阶段开始该网页的爬取。可以将该值设置的大一些。但是需要注意的时,pageRank算法视链接为投票,一个url的初始重要程度高,并不代表在pageRank收敛时,该url的最终重要程度高,其重要程度主要由指向该url的其他url的重要程度决定的,即inlinks的重要程度决定的。

2)        nutch.fetchInterval

       注入的url爬取周期(fetchInterval),即在多少天之后能够被再次选取加入到fetchlist,无论该url之前是什么状态(如是否被爬取,由于出错重试了一定次数,具体参见[4]),默认是30天。

3)        nutch.fetchInterval.fixed

        为注入的url设置固定爬取周期(fetchInterval),该配置项的优先级比nutch.fetchInterval高,如果注入了该值,爬取周期固定为指定值,nutch.fetchInterval即使指定了也无效,并且nutch执行过程中不会修改该值。

        以上3个metadata对该url决定了该url何时能够被爬取,具体参见5.3。

1.1.3   Configurable Item

表1 inject的可配置项解释

参数

含义

默认值

db.fetch.interval.default

注入url时,默认的爬取周期(fetch.interval)

2592000,单位:秒,=30天,该值的实际含义是:不管该url之前是什么状态,在30天之后,nutch都将重新尝试爬取该网页。

注意:该值只是一个"软期限",实际该网页到了30天之后,是否能被加入到fetchlist还要受fetch阶段的参数控制,具体参见5.3

db.score.injected

注入时url的默认网页得分(重要程度)

1.0f,网页默认的重要程度,默认网页的重要程度都相同,关于网页的Scoring可以参照[6]

db.injector.overwrite

这2者是控制新注入的url在原来的crawldb中已经存在的时候(当该条件不成立的时候,二者都不起作用),nutch如何处理新注入的metadata和旧的metadata之间的关系的,2者共同配合完成如下图6所示逻辑:

 

2者的默认值都为false,表明:在出现待注入的url已经在crawldb中存在的情况下,完全忽略新注入的url以及其附带的metadata

db.injector.update


图6 injector的configurable item中update和override参数的作用

1.1.4  相关数据结构分析

1.1.4.1       CrawlDatum

存储了url在整个爬取过程中的状态信息,该状态信息跨越多个阶段,该状态信息的不同取值影响着nutch运行时行为的如下内容:

1)        url是否会被爬取。

2)        何时能被爬取。

3)        在爬取成功后什么时候再开始下次爬取。

具体的,状态信息包含:

1)        byte类型的status:存储了从inject、fetch、parse等阶段的状态,具体如图 7所示:

7  crawlDatum中url的status取值范围(为简化图形省略了前缀STATUS_)

通过图 7可以看出,nutch为了将不同的status状态取值划分成了3个不同的范围,从上到下依次为“crawldb中的状态”、“fetch阶段的状态”和“其他阶段的状态”,为了便于进行逻辑运算(例如:CrawlDatum.hasDbStatus(CrawlDatum)和hasFetchStatus(CrawlDatum)方法),nutch在各个阶段中间设置了各个阶段的上限取值DB_MAX和FETCH_MAX。

不同的status的取值会在不同的阶段之间进行变迁,可以如下图所示的状态机[4]来表示:

8 nutch的CrawlDatum状态机[4]

2)        long类型的(实际是Date,存储是微秒)fetchtime:上次(或者下次)爬取时间,初始化的crawlDatum的时候,nutch将其初始化为系统当前时间,该时间会被nutch的FetchSchedule(爬取调度器)来使用以决定该crawlDatum对应的url是否会加入到fetchlist。

3)        byte类型的retries,在fetch阶段,由于各种原因(最常见的是源网站连接不上),url无法访问,对该url的fetch会在一定的时间内超时,此时会retries次数会自增,retries超过一定的次数,该url会被认为GONE状态。retries与status的关系见图 8。

4)        fetchInterval:爬取周期(间隔),表示每隔多长时间该url具有被选取加入fetchlist的资格。

5)        score:该url的重要程度,影响该url爬取的优先级,越高越会被优先爬取。

6)        signature:url对应的html内容的md5摘要签名。用于验证2个网页的内容是否相同。

7)        moidfiedTime:网页内容的变化时间,当该url再次被爬取的时候,通过计算器内容的signatrue,与上次进行比较,如果不相同,则认为网页内容发生了变化,记录moidfiedTime为当前时间。

8)        metaData,一个持久化的map结构存储key:value对。用于存储爬取过程中该url其他的元信息,例如:图片分辨率,url所在的segment文件夹名称等。

1.1.4.2       Crawldb

Crawldb是逻辑概念,存储<url:CrawlDatum>之间的映射关系,物理上是HDFS上的一个SequenceFile格式的文件。该文件中典型的一条记录如图 9所示:


图9 CrawlDb的记录示例

1.1.5  MapReduce Job分析

1.1.5.1       sortJob

10 injector的程序流程图

injector的input有两种文件类型,一种是待注入的文本文件,二是旧的sequenceFile类型的crawldb文件。由于2种文件类型不同,无法发送给同一个Reducer进行处理,nutch采用了2个MapReduce Job来处理,这2个job就是图 10中的2个“子流程”。其中sortJob主要完成从种子文件文本转换成与现有crawldb同样格式的<url:CrawlDatum>的预处理工作,以方便mergeJob采用统一的的InjectReducer进行处理,sortJob的解析如图 11所示。


11 injector sortJobMapReduce过程解析

sortJob的map阶段由InjectMapper进行处理,主要的程序流程图如图 12所示。


12  InjectMapper的程序流程图

需要注意的是,sortJob在mapper阶段执行完毕之后,会判断crawldb是否已经存在,如果已经存在,sortJob不会执行reducer阶段,而是等到mergeJob来一并处理。如果存在crawldb不存在,则sortJob会执行InjectReducer来进行处理。

从实际nutch对sortJob的实现来看,sortJob应该命名为filterJob更合适一些,因为其大部分都是在进行url的“过滤(filter)”和“规范化(normalization)”工作,真正“sort”的是依赖于mapReduce框架的shuffle过程完成的。

1.1.5.1       mergeJob
mergeJob,顾名思义,主要完成相同url的CrawlDatum的“合并”工作。它将sortJob的输出和现有的crawldb二者作为输入,如果存在相同的key(为url),由于key为url,在shuffle阶段,相同url的CrawlDatum会被发送到同一个InjectReducer,所以无论是新注入的url的CrawlDatum还是原来crawldb中已经存在的CrawlDatum(url与新注入的url相同)都会被同一个InjectReducer处理,所以InjectReducer可以判断是否新注入的url与现有的crawldb中的url存在重复的情况。具体重复之后如何处理,要受到injector的update和overwrite参数的控制(参见图 6)。 mergeJob的MapReduce过程分析如图 13。

 

13 injector mergeJob MapReduce过程解析

 

1.1.5.1       相关Design Pattern
1.1.5.1.1      Composite模式

14  URLNormalizer及其子类和URLNormalizer采用Composite模式

图 14中角色在nutch中的对应关系如下:

1)        抽象“组件”:URLNormalizer。

2)        具体“组件”:BasicURLNormalizer、PassURLNormalizer、RegexURLNormalizer,HostURLNormalizer和QueryStringURLNormalizer,其中前三者是nutch默认启用的。

3)        组合“组件”:URLNormalizers。

4)        Client: nutch中充当该Composite模式的Client的类有:Injector、Generator、LinkDb、LinkDbFliter、URLParitioner。

虽然,nutch在实现时,URLNormalizers并没有implements URLNormalizer接口,但是它实现了与接口同名的方法,实际上是符合了Composite模式的特征。

其实不仅仅是URLNormalizer一个插件实现了Composite模式,nutch以下插件也都实现了Composite模式:

1)        URLFilter及其子类和URLFilters

2)        ScoringFilter及其子类和ScoringFilters

3)        IndexWriter及其子类和IndexWriters

4)        IndexingFilter及其子类和IndexingFilters

5)        HtmlParseFilter及其子类和HtmlParseFilters

实际上,各种Filter都实现了共同的接口pluggable,pluggable是一个空接口,起标示作用(Markable Interface)。pluggable及其子类的类图如下:

15 pluggable接口和子类以及其相关类形成的Design Pattern

从图 15可以看出,pluggable子类下半部分实现了Composite模式,上半部分实现了简单工厂模式。


1.1.6     FAQ

1.1.6.1       inject阶段只能被执行一次吗?

不是,inject可以被执行多次。但是需要注意避免对crawldb的写冲突,例如updatedb阶段也会写crawldb,或者是多实例爬取环境下的执行多个inject。

1.1.6.2       同一个url多次inject会在crawldb中生成重复记录吗?

不会,因为crawldb的key为url,相同的url在crawldb中只有一条记录,无论重复注入多少次。

1.1.6.3       如果在inject时指定了nutch.fetchInterval.fixed值,即为某url设置了固定爬取周期,是否能更改?

可以,两种方法,一是再次执行inject,为该url指定不同的nutch.fetchInterval.fixed值;二是二次开发,直接修改crawldb中对应的crawldatum的metadata的key为nutch.fetchInterval.fixed的值。

1.1.6.4       如果限制了只爬取与种子url文件在相同domian下的url,如何让nutch在运行时爬取新的domain下的url?

该问题不是一个FAQ,应该算作一个trick,可以使用inject注入新的domain下的某个url到crawldb中(注意:要避免对crawldb的写冲突,即在inject时有其他阶段正在写crawldb,例如updatedb)。

1.1.6.5     在种子文件中含有重复的url(即一个url出现2次以上),injector如何处理?

InjectReducer处理了这种情况,对于相同url的iterator<CrawlDatum>,最后的一个CrawlDatum生效。

参考文献

[1].    http://wiki.apache.org/nutch/CrawlDatumStates



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值