crawl 脚本用来实现 nutch 中对于网页的抓取 分析 索引工作。
把工程下下来之后 ,进入到 bin 目录:
两个脚本文件 crawl nutch
crawl 是对nutch 的进一步封装
首先我们看一下他的注释:
# UNLIKE THE NUTCH ALL-IN-ONE-CRAWL COMMAND THIS SCRIPT DOES THE LINK INVERSION AND
# INDEXING FOR EACH SEGMENT
是的,正如注释中所说的,他为每一个segment 做抓取,倒排和索引工作。
略过一般常规性的判断,参数检查,他迎来了他的第一步:
inject
# initial injection
$bin/nutch inject $CRAWL_PATH/crawldb $SEEDDIR
这一步执行了 inject 操作, 咱们看看这一步是干什么的
打开 nutch 找到这么一句话:
elif [ "$COMMAND" = "inject" ] ; then
CLASS=org.apache.nutch.crawl.Injector
这就找到了程序的入口,咱们看一看。
整个项目都是构建在hadoop上的 , 我喜欢。
既然是hadoop,咱们就看看他是如何使用 mapreduce的。
Map中:
用咱们自己手动给出的url的集合作为输入, 拿到这个url的信息. 一些配置信息可能出现在 url的这个文件中,尽可能解析出来, 比如抓取间隔时间。
然后是规范化一些url ,用代码里的单词叫:urlNormalizers.normalize
这里用了责任链来处理这些url,责任链中定义了很多规范化的规则,传入scope参数,指定需要过哪些链。plugin的代码打成了一个jar包放在plugins 目录下,除了解压源代码,我还没找到什么好方法看他们到底干了什么, 以后心情好,可能拿出来讲一讲,今天就算了。咱们继续往下看。
然后是过滤,filters.filter(url); 跟规范化用的是差不多的流程,会过滤掉指定的url,比如拉黑的,一些前缀后缀不合格的等等,也是以插件的形式提供的。
scfilters.injectedScore(value, datum); 把分数的信息插进去,暂时不知道有什么用,先放着。
然后 map 输入了 , key是这个url,value是url的一些信息,比如抓取间隔是多少等等。
第二个Mapper
他的逻辑写的很奇怪,我差点看走眼, 紧接着他是用CrawlDbFilter 作为mapper,又做了个处理。
这个过程又把规范化和 filter过了一遍。个人估计是为了后续重新加入url,已经应对不同的状态做的额外的处理。
Reduce中:
首先会修改这个url的状态, 每个有关的url的信息中会携带一些关于url的状态, 在map中新加的状态是STATUS_INJECTED, 在reduce中会改成STATUS_DB_UNFETCHED ,然后就输出了,输出到crawlDB
总结,就是做了一些 规范化和 过滤。迫不及待的我要看下一步了。
generate
elif [ "$COMMAND" = "generate" ] ; then
CLASS=org.apache.nutch.crawl.Generator
第一个mapred中, 竟然 mapper,partitioner,reducer都是同一个类,Selector.class 看到这里笔者格外兴奋。
当然输入是crawlDB的位置,map的操作是过滤url这个万年不变的操作, 然后矫正抓取时间间隔,如果时间还早着,直接推出,否则,通过一定的filter给这些url打分,输出的竟然是得分,一个封装url和相关信息的对象。
partition 直接把同一个host 或者ip的url分到同一个reduce上,首先,会是得分高的url先弄,这就是为什么要拿score 作为key,作为map的输出,根据各种计算,算出这个url应该输出到哪个segment中,然后输出跟map一样的东西,只不过url相关信息中加入了 segment 的信息,然后通过Comparator 将同一个segment输入到一个文件中。
第二个mapred是更新crawldb, 这样下次就不会去重复抓同样的url了。
操作很简单,最终目标就是把时间信息设置进去,以便于下次判断。
然后重命名老的crawldb, 把更新后的放进去。
fetch
elif [ "$COMMAND" = "fetch" ] ; then
CLASS=org.apache.nutch.fetcher.Fetcher
这个流程比较简单,只有一个map,他实现的是maprunner ,所以我们看他的run方法。
这是一个典型的生产者消费者模式,生产者不断的从segment中读取数据,往队列里放,消费者就从队列里面取了,同样用插件的形式封装访问协议,根据不同的返回码,做不同的处理,目前只讨论成功的情况,我们会把成功内容输出,修改状态码。
总之,感觉这就是做了一个框架,所有细节的内容全在 插件里面。需要做爬虫的同学应该解读的是插件的代码。