Nutch基础教程(2.3.1版本)--Nutch2.3.X爬虫任务概述

时间:2021-10-17 21:31:45

本文描述Nutch2.X的爬虫任务(流程)

目录

* 介绍
* Generate
*
* Mapper(映射)
* Partitioning(分区)
* Reducer(化简)
* Result(结果集)
* Things for future development(进一步开发)

* Fetch
*
* Mapper
* Partition
* Reduce
* Graphic Overview

* Parse
*
* Mapper

* DbUpdate
*
* Mapper
* Partition
* Reduce

介绍
一个爬虫循环包括4步,每一个都通过Hadoop任务(job)来实现:

1. GerneratorJob
2. FetcherJob
3. ParserJob
4. DbUpdaterJob

如果要在在webtable中填入初始化的行详情,可以使用InjectorJob。
有一个独立的表 webtable ,它用来作为以上任务的输入和输出。表的每一行都是一个url。为了把来自相同*域名和域的url紧密的结合在一起,行键都是用方向主机组件来存储的。这利用了行键用来排序的事实。扫描一个表的子集比扫描整张表的指定行键过滤要快得多。下面是行键的实例:

* com.example.www:http/
* com.example.www:http/about/
* net.sourceforge:http/
* ...
* org.apache.gora:http/
* org.apache.nutch:http/
* org.apache.www:http/
* org.wikipedia.en:http/
* org.wikipedia.nl:http/

Generate
创建一个新的作业(batch)。从webtable中选择url来抓取(或者这么说,标记webtable中需要抓取的url)。

Mapper:
从webtable中读取每一个url。

1. 对之前加入过generate任务的url做跳过处理(以前被抓取过的url会被标记上generated标记(batch ID))。这就允许我们可以同时运行多个generate任务。
2. (可选)标准化url(应用URLNormalizers)并且过滤url(应用URLFilters)取消url的快速生成功能。
3. 判断抓取时间是否合适。
4. 为url计算分值。
5. 输出每一个需要抓取的url,以及它们对应的分数。

Partitioning:
所有的url都是通过域、主机或者IP来分区的。这就意味着所有来自相同域(主机、IP)的url在同一个分区中结束,并且会被相同的reduce任务操作。在每一个分区里,所有的url都是按照打分来排序的。

* (可选项)在分区的过程中标准化URL。取消url的快速生成功能。
* 当使用IP来分区时:这对DNS解析会比较困难。

Reducer:
从分区中读取每一个url。一直选取url,直到选出的url的全部数量达到一个阀值,或者每一个域中选来的url数量都达到了某一个阀值。

1. 当我们获取到 topN / reducers 的url时,停止填入url。这会为所有的reducer一起提供刚刚取出的topN个url。
2. 如果当前域的url数量超过了属性 generate.max.count 所设置的值,就停止填入url。reduce任务使用map持续跟踪上述限制。之所以能起作用,正是因为来自相同域的url会被放在同一个reduce中来操作。应该注意到,如果每一个reduce任务的不同域的数量很大时,那么map的任务数量也会很大。然而,我们总是可以不受限制的增加reducer的数量。
3. 为每一个选择的URL,都做上一个generate标记(作业ID),说明该url已经加入到generate任务中。
4. 输出行内容到webtable中。

Result:
返回被选取的最大topN数量的url,这里的topN是取那些在webtable中标记过generate状态的url。这里没有选择topN个按分数排序的url,也没有选择全部的url中的topN个,原因如下两点:

1. 属性generate.max.count 限制了每一个域中的url数量。所以来自域 abc.com域的某个url可能会被跳过,然而分数相对更低,来自域xyz.com的url可能仍然会被获取到。这是有好处的,因为我们宁愿获取到分数更低的url,也不愿意获取的url都是来自相同的域。
2. 每一个reducer任务可以生成最多topN个urls。但是分区的过程中,不是所有的reducer都可以选择这个数量的url。这是一个实现结果。当然,如果域的数量远多于reducer的数量,那么这就不是什么问题了。而且,我们也获得了一些可扩展性。

示例:
设置:

* topN = 2500
* generate.max.count = 100
* reducers = 5

输入到分区1:

* abc.com: 10 urls
* klm.com: 100 urls
* xyz.com: 1000 urls

从分区1中输出:

* abc.com: 10 urls
* klm.com: 100 urls
* xyz.com: 100 urls

所以对于这个分区1来说,只有210个url被选取出来进行抓取。

Things for future development(进一步了解):
问题一:
Generate任务如何和抓取过程结合在一起的呢?为什么要把generator标记写回到webtable中,然后再重新全部选取出来,进行相同的分区,再开始抓取数据。为什么不能直接在generate任务过程中就执行抓取呢?
原因如下:

1. 一旦任务失败,就必须从头开始执行generate任务;
2. 无需另外再执行任务(选取,分区……)。

问题二:
真的有必要在一次generate任务中处理全部的url吗?
听起来,从webtable中为每一次generate任务一次又一次的读取url、执行上面的检查(计算url分值)、排序、分区……尤其是当url的数量远大于topN时,这些都相当的无效重复。当然,当抓取作业结束后,webtable也被改变了,包括新的url,url分值,所以,如果真的想要完美的执行这些流程,我们就需要重新完整的检查webtable这张表里的内容,这就是为什么,每一次generate任务,都要把所有的url都处理一遍。

建议:

* 使用region side filtering来检查(url的)generate标记和抓取时间。
* 得分是否不重要:mapper阶段持续跟踪url的数量限制(topN和每一个域)。当每一个mapper任务中已经被选择的url的数量超过topN时,就停止读入新的输入记录。在域和主机模式下(generate.max.count),由于map的输入是按照反向url来排序的,这时mapper阶段运行起来就会相对更加快。(所以打分在这种情况下是十分有用的)

缺点(Cons):

* 得分不被用来进行选取(url)。
* 一个mapper任务的输入中,开始部分的域名更容易被选择。(也就是选取的开始不是随机的)

Fetch
该任务执行抓取,针对所有在generate环节被选择出来并标记上batchID的url。(也可以选择抓取所有的url)

Mapper:
该阶段从webtable表中读取每一个url。

1. 检车generate标记是否能够匹配上。
2. 当匹配上的url同时也有featcher标记时,如果给出了‘continue’参数就跳过这些url,否则就在本批作业中对之前抓取过的url进行重新抓取。
3. 对所有的url进行洗牌,并且给每一个url配上一个随机整数来作为它的键。

注意:这里我们需要再次检查整个webtable表(相对于generate任务而言)。因为会有以下可能存在的改变:

* 使用了region side filters来检查generator标记?
* 使用generator任务的reducer阶段来执行抓取?坏处是:不能对选取的url进行随机排序。
* 使用nutch1.X中的方法,将generator中的输出作为中间结果存放在文件或者表中,然后以此进行后面的抓取。

Partition:
以主机名来进行分区
注意:为何fetcher任务的map阶段使用自己的分区?因为fetcher的reduce阶段支持每一个主机、域以及IP的队列。

Reduce:

1. 将随机的数量的url放到fetch队列中(使用优雅的方式为每个域(主机或者IP)配置一个队列)并且为项目扫描所有的队列进行抓取。
2. 抓取每一个url。如果操作重定向呢(redirect)。
3. 将成功和失败的状态输出到webtable中。注意:抓取时间会被设置成“now“。
4. 如果配置了解析设置:使用ParseUtil类来解析内容,当然会跳过哪些没有被抓取的项目(该类也会被ParseJob任务调用)。

Parse
通过一个给定的作业id(batchID)来解析网页页面。

Mapper:

1. 从webtable中读取每一个行。
2. 检查抓取标记是否匹配。
3. 如果网页页面已经被解析过了,就跳过该页面。
4. 在行上运行解析任务。
5. 将行输出到webtable中。

DbUpdate
更新所有行的内链(反向连接),抓取时间以及正确的分值。

Mapper:

1. 从webtable中读取每一行。
2. 为每一个外链更新分值。
3. 输出每一个外链的分值和锚(连接文本)。
4. 输出带有分值的行键。

Partition:
按照url来分区。按照url的分值来排序。按照url进行分组。这一步保证了内链在reducer阶段是按照分值进行排序的。

Reduce:

1. reducer阶段的键是webtable中的行,而reducer中的值就是行中的内链。
2. 更新抓取时间。
3. 更新内链(内链的上限由‘db.update.max.inlinks’属性来决定)。
4. 根据每一行的内链来更新行中的分值。
5. 将更新后的行输出到webtable中。