首页 百科知识 开源项目分析

开源项目分析

时间:2022-02-26 百科知识 版权反馈
【摘要】:本节我们从介绍Lucene工具包开始选取Nutch和Heritrix两个开源项目进行介绍、分析和比较,目的是为了更好地学习优秀开源项目的设计思想,设计自己的网络信息抓取程序。Lucene是一个纯Java实现的成熟、自由、开源的软件项目;它是备受程序员欢迎的开源组织Apache Jakarta的成员项目,基于Apache软件许可协议的授权。
开源项目分析_多语种叙词本体

6.2 开源项目分析

目前已经有了不少开源的网络爬虫项目,如Heritrix、SpiderPy、WebSPHINX、Nutch等,它们都有各自的特点。本节我们从介绍Lucene工具包开始选取Nutch和Heritrix两个开源项目进行介绍、分析和比较,目的是为了更好地学习优秀开源项目的设计思想,设计自己的网络信息抓取程序。

6.2.1  Lucene概述

Lucene是Apache软件基金会Jakarta项目组的一个子项目,它是一个高性能的、可扩展的信息检索工具库。你可以把它融入应用程序中以增加索引和搜索功能。Lucene是一个纯Java实现的成熟、自由、开源的软件项目;它是备受程序员欢迎的开源组织Apache Jakarta的成员项目,基于Apache软件许可协议的授权。因此,近年来,Lucene已经成为最受推崇和青睐的Java开源信息检索工具库。

Lucene提供了一套简单却十分强大的核心API,而使用它们时并不需要用户对全文索引和搜索的机制有很深的理解。若要把Lucene集成到应用程序中,用户只需掌握Lucene中少数的几个类。Lucene能够为文本类型的数据建立索引,如果使用Lucene对自己的文档进行索引和搜索,你只需把要索引的文档格式转化为文本格式。比如你要对HTML文档、PDF文档或者Word文档进行索引,首先要把HTML文档、PDF文档或者Word文档转化成文本格式,这样Lucene就可以对文本格式的文档进行索引,然后把创建好的索引文件保存到磁盘或者内存中,最后根据用户输入的查询条件在索引文件上进行查询。若不指定要索引的文档格式,Lucene几乎能够适用于所有的搜索应用程序。图6-4反映的是搜索应用程序和Lucene之间的关系,也反映了利用Lucene构建搜索应用程序的流程。

img69

图6-4 搜索应用程序和Lucene之间的关系

6.2.2  Nut ch概述

Nutch是一个开放源代码的Web搜索引擎,是以Lucene为基础实现的搜索引擎应用程序,Lucene为Nutch提供了文本索引和查询服务的API,而Nutch在Lucene的基础上实现了网页收集,它提供了我们运行自己的搜索引擎所需的全部功能。Nutch致力于让每个人能很容易地只花费不多的时间就可以配置世界一流的Web搜索引擎。Nutch的功能相当地强大,Nutch可以每个月抓取几十亿网页;为这些网页维护一个索引;对索引文件进行每天上千次的搜索;提供高质量的搜索结果;还能以最小的成本运作。

Nutch的作者是Doug Cutting。他是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,Lucene也是他的作品,后在Excite担任高级系统架构师,目前从事一些Internet底层架构的研究。他贡献出Lucene的目标是为各种中小型应用程序加入全文检索功能。

Nutch的安装分为3个层次:基于本地文件系统,基于局域网,或者基于Internet。不同的安装方式具有不同的特点。之所以受到全世界众多信息检索开发者的青睐,主要是因为Nutch具有3大特点:

(1)透明度高

Nutch是开放源代码的,所以任何人都可以查看它的排序算法是如何工作的。而商业搜索引擎的排序算法是商业秘密,是保密的。某些商业搜索引擎网站为了获取更多的利益,采用竞价排名,这样使得索引结果并不是和站点的内容相关的。用户检索信息的效率受到了一定的影响。而Nutch具有完全的透明度,是Nutch成为学术搜索和政府类站点检索的一个很好选择。因为对学术研究者来说一个公平的排序结果是十分重要的。

(2)扩展性好

如果不喜欢其他搜索引擎提供的搜索结果,我们完全可以自己动手用Nutch建设自己的搜索引擎。Nutch是非常灵活的,它的扩展性不错。Nutch可以很好地被用户定制并集成到用户自己的应用程序中。使用Nutch的插件机制,Nutch可以作为一个搜索不同信息载体的搜索平台。

(3)理解搜索引擎的模板

Nutch的源代码是开放的,作为搜索引擎的研究者与开发者,我们可以将Nutch作为学习搜索引擎不错的模板。通过研究Nutch,可以了解一个大型分布式的搜索引擎是如何工作的。Nutch的魅力还在于它吸引了许多研究者去尝试开发新的搜索算法。

6.2.3  Nutch的基本结构

根据苏晓珂等学者的研究,Nutch的系统结构如图6-5所示,总体上Nutch可以分为两个部分:抓取部分和搜索部分。抓取程序抓取页面并把抓取回来的数据做成倒排索引,Nutch采用了Lucene进行全文索引,同时,Nutch采用了pagerank算法对网页进行排序(进行局域网爬行的时候Nutch自动省略了这一操作)。搜索程序则对倒排索引搜索回答用户的请求。抓取程序和搜索程序的接口是索引。两者都使用索引中的字段,实际上搜索程序和抓取程序可以分别位于不同的机器上。

img70

图6-5 Nutch架构示意图

我们重点分析Nutch的抓取部分,它是由Nutch的抓取工具驱动的。这组工具用来建立和维护几个不同的数据结构: Webdb,segment,index。

(1) Webdb

Webdb是一个用来存储页面和链接两种实体的数据结构,保存被抓取网站数据的结构和属性,只被抓取程序使用。页面实体表示网络上的一个网页,这个网页的URL作为标识被索引,同时建立一个对网页内容的MD5哈希签名,跟网页相关的其他内容也被存储,主要包括:页面中的链接数量(外链接)、页面抓取信息(在页面被重复抓取的情况下)、表示页面级别的分数Score。链接表示从一个网页的链接到其他网页的链接。因此Webdb可以说是一个网络图,页面作为节点,链接是边。

Webdb由以下数据组成:

①爬行数据库(crawdb):包括所有Nutch已知的URL,以及关于此URL的信息,如这个URL是否被爬过,如果被爬过,被爬的时间会被记录在数据库中。

②链接数据库(linkdb):这里面包含的信息是每个URL已知的链接信息。

Webdb里有4个文件,在物理视图上是文件夹:

pagesByURL:按URL排序的page对象数组

pagesByMD5:按MD5排序的page对象数组

linksByURL:按URL排序的Link对象数组

linksByMD5:按MD5排序的Link对象数组

(2) segment

segment是被索引的网页集合。每个segment由fetchlist、fetcher、content、parse_data和parse_text五个文件夹组成,如果建立了索引就是6个文件夹,其中每个文件夹是一个ArrayFile对象。fetchlist文件夹保存的是从Webdb中生成的抓取程序使用的URL列表。fetcher文件夹保存的是抓取状态信息,输出数据是从fetchlist中抓取到的网页。fetcher的输出数据先被索引,索引后的结果存储在segment中。content文件夹保存抓取回来的网页内容,包括http头信息和其他元信息。搜索时,如果选查看缓存页面,就从此处读数据。parse_data文件夹保存的是从网页中解析出来的一些数据,例如元数据。parse_ text文件夹保存从网页中解析出来的文本数据。segment的生命周期是有限制的(默认的重新抓取间隔是30天),当下一轮抓取开始后它就没有用了。因此可以删除超过这个时间期限的segment,而且也可以节省不少磁盘空间。segment的命名是日期加时间,因此很直观地可以看出他们的存活周期。

(3) index

索引系统中所有被抓取的页面,它并不直接从页面产生,而是合并很多小的segment的索引。Nutch使用Lucene来建立索引,但Lucene和Nutch的segment概念是完全不同的。简单来说Lucene的segment是Lucene索引库的一部分,而Nutch的segment是Webdb中被抓取和索引的一部分。

抓取是一个循环的过程:抓取工具从Webdb中生成一个fetchlist集合,根据fetchlist从Web上下载网页内容,若抽取工具发现有新链接就更新Webdb,然后再生成新的fetchlist,周而复始。这个抓取循环又被称为: generate/feteh/update循环。

通常同一域名下的URL链接会被合成到同一个fetchlist。当同时使用多个工具抓取的时候,这样做就不会产生重复抓取的现象。Nutch遵循RobotsExClusion协议,可以用robots. txt定义保护私有网页数据不被抓去。

此抓取工具的组合是Nutch最外层的,也可以直接使用更底层的工具。上述过程分别详述如下:

①创建一个新的Webdb。

②把开始抓取的根URL放入Webdb。

③从Webdb的新segment中生成fetchlist。

④根据fetchlist列表抓取网页的内容。

⑤根据抓取回来的网页链接URL更新Webdb。

⑥重复上面步骤③~⑤直到达到指定的抓取层数。

⑦用计算出来的网页URL权重scores更新segments。

⑧对抓取回来的网页建立索引。

⑨在索引中消除重复的内容和重复的URL。

⑩合并多个索引到一个大索引,为搜索提供索引库。

图6-6是Nutch的工作流程图。

在创建了一个新的Webdb后,抓取循环generate/fetch/update就根据第二步指定的根URL在一定周期下自动循环了。抓取循环结束后,生成一个最终的索引(第⑦步到第⑩步)。第⑧步中每个segment的索引都是单独建立的,之后才消重(第⑨步)。第⑩步就大功告成,合并单独的索引到一个大索引库。

img71

图6-6 Nutch工作流程图

Dedup工具可以从segment的索引中去除重复的URL。因为Webdb中不允许重复的URL,也就是说fetehlist中不会有重复的URL,所以不需要对fetchlist执行dedup操作。默认的抓取周期是30天,如果已经生成的旧fetch没有删除,而又生成了新的fetch,还是会出现重复的URL的。当只有一个抓取程序运行的时候是不会发生上述情况的。

在爬虫中,抓取是最主要的操作,其作用就是抓取网页,但抓取的单位不是单个网页,而是同属于一个segment的网页集合。下面我们仔细分析下fetcher类。

Run()函数逐个实例化抓取线程FetcherThread类,然后触发各个线程的start()函数,在其初始化threadCount个线程并等待线程结束后或者出现大的异常后,此函数调用close()结束输入输出流。

FetcherThread是fetcher类的一个内部类,它继承于Java. lang,Thread类,只有一个实体方法run()和三个静态函数: handle-Fetch(),handleNoFetch(),logError()。

FetcherThread. run()将实例化一个名为“fle”新的FetchListEntry实例,然后执行以下循环:

①如果出现错误,记录并退出循环。

②从fetchList中取得下一个URL集,如果取得结果为空,退出循环。

③从FetchListEntry解析出URL。

④如果FetchListEntry没有被标记为“fetch”(未抓取此URL的网页),调用this.handleNoFetch()函数,设置status= 1。然后逐步执行:

取得此URL的MDS摘要;

建立FetcherOutput(fle,hash,status);

建立空的Content、ParseText和ParseData对象;

利用这些对象调用Fetcher.outputPage()函数。

⑤如果标记为“fetch”(已抓取此URL的网页),调用Protoeo1Factory并取得符合此URL的协议和内容对象。

⑥调用this.handleFetch(url,fle,content),然后逐步执行:

调用符合此内容类型的ParserFactory.getParser();

执行parser.getParse(content);

利用新建立的FetcherOutput和URL的MD5摘要,产生的内容对象和已解析的ParseText调用Fetcher.outputPage()函数。

⑦循环100次,在log中记录。

⑧捕捉各种小的异常以及记录写入log文件。

6.2.4  Herit rix概述

Heritrix是一个由Java开发的开源Web网络爬虫,用户可以使用它从网络中抓取需要的资源。可扩展性是Heritrix最大的特色,开发者可以根据自己的抓取逻辑扩展Heritrix的各个组件。如此一来,Heritrix的可用性大大增强。这也是Heritrix受到广大开发爱好者青睐的重要原因。具体地讲,Heritrix的可扩展性表现在以下四个可扩展点:

(1)定制自己的Extractor处理器

Extractor用来解析各种URL的返回内容,分析提取新的URL加入到候选队列中。

(2)扩展FrontierScheduler

FrontierScheduler是用来决定最终哪些类型的URL将被滤掉,哪些将被保留下来。

(3)定制链接制造器BdbFrontier的URL散列算法

由于BdbFrontier链接制造器的数据存储是基于近似哈希Key/ Value方式保存的,它在构造BdbMultipleWorkQueues时会将一连串链接放在一起形成队列,并赋予一个Key。该Key值的计算在默认Heritrix配置中是选择用HostName或IP生成策略的,因而在聚焦于个别网站抓取时,会产生只有一个线程工作的情况。因为每个线程从一个很长的队列中取出头部链接后,该队列进入阻塞状态,要直到此链接处理完后才恢复。所以,实际运用时,需求继承QueueAssignmentPolicy抽象类,实现一个有效的散列算法策略类,并配置到heritrix.properties中去。

(4) robots. txt对个别Processor的影响

遵循robots. txt附加协议的聚焦爬虫不但牺牲了抓取速度,而且可能受到抓取目标站点的限制,因此在实际应用中,根据实际情况把相关的Processor中对robots的处理注释掉。

6.2.5 Heritrix框架

为了更好地分析Heritrix,我们从其框架入手进行详细分析。按照刘运佳等学者的研究,Heritrix的框架如图6-7所示。

图6-7 中的主要组件有:

Web Administrative Console: Web控制台

img72

图6-7 Heritrix框架

CrawlOrder:抓取任务控制器

CrawlController:中央控制器

Frontier:链接制造工厂

CrawlURI:抓取的URI

URIWork Queues: URI工作队列

Already Included URIs:已抓取的URl

Prefetch Chain:网络传输协议预解析链

Fetch Chain:网络传输协议解析链

Extractor Chain:内容解析链

Writer Chain:内容记录器链

Postprocess Chain:结束处理器链

6.2.5.1 抓取任务Cr awl Or de r

CrawlOrder类是整个抓取工作的起点,一次抓取任务包括许多的属性,建立一个任务的方式有很多种,最简单的一种就是根据默认的order.xml来配置。在内存中,order使用CrawlOrder这个类来进行表示。在其API文档中CrawlOrder的继承关系如下:

Class CrawlOrder

java. lang.Object

--javax.management.Attribute

--org.archive. crawler.settings.Type

--org.archive. crawler.settings.ComplexType

--org.archive. crawler.settings.ModuleType

--org.archive. crawler.datamodel.CrawlOrder

由以上继承关系可知,CrawlOrder继承自一系列与属性设置相关的基类。它的最顶层基类是一个JMX中的类: javax. management.Attribute,这个类可以动态反映出Java容器内某个Bean的属性变化情况。

在Heritrix的org.archive. crawler.settings包下有一个XMLSettingsHandler类,很好地支持了对order.xml的读取。

public XMLSettingsHandler(File orderFile) throws

InvalidAttributeValueException

在XMLSettingsHandler的构造函数中,其所传入的参数order-File是一个经过对象封装的order.xml的File。这样,就可以创建一个读取order.xml的XMLSettingsHandle实例。当一个XMLSettings-Handler的实例被创建后,可以通过getOrder()方法来获取CrawlOrder的实例,这样也就可以进行下一步的工作了。

6.2.5.2 中央控制器Crawl Cont roller

中央控制器是一次抓取任务中的核心组件,决定整个抓取任务的开始和结束。CrawlController位于org.archive. crawler. framework中,通过它的Field声明,可以看出,它定义了以下几个组件:

CrawlOrder:它保存了对该次抓取任务中,order.xml的属性配置。

CrawlScope:这是决定当前的抓取范围的一个组件。

ProcessorChainList:从名称上很明显就能看出,它表示了处理器链。

Frontier:一次抓取任务需要设定一个Frontier,以此来不断为其每个线程提供URI。

ToePool:这是一个线程池,它管理了所有该抓取任务所创建的子线程。

ServerCache:这是一个缓存,它保存了所有在当前任务中,抓取过的Host名称和Server名称。

以上组件应该是完成一次正常的抓取过程中所必需的几项,它们各自的任务独立,分工明确,但在后台运行过程中,它们之间相互联系、相辅相成,彼此互相作为构造函数或初始化的参数传入。

我们可以通过CrawlController中的构造函数来构造一个Crawl-Controller实例。在构造实例并进行抓取任务时,其步骤如下:

①构造一个XMLSettingHandler对象,将order.xml内的属性信息装入。

②调用CrawlController的构造函数,构造一个CrawlController实例。

③调用CrawlController的intialize(SettingHandler)方法,初始化CrawlController实例,将第一步构造的XMLSettingHandler实例作为参数传入。

④开始运行,调用CrawlController的requestCrawlStart()方法,就可以启动线程池和Frontier,然后就开始不断地抓取网页。

上述过程如图6-8表示。

在CrawlController的initialize()方法中,Heritrix主要做了以下几件事:

①从XMLSettingsHandler中取出Order。

②检查了用户设定的UserAgent等信息,看是否符合格式。

img73

图6-8构造CrawlContronller实例并启动抓取任务

③设定了开始抓取后保存文件信息的目录结构。

④初始化了日志信息的记录工具。

⑤初始化了使用Berkley DB的一些工具。

⑥初始化了Scope、Frontier以及ProcessorChain。

⑦最后实例化了线程池。

在正常情况下,CrawlController的初始化执行顺序不能随意改动,因为后一项功能的初始化很有可能需要前几项功能初始化的结果。

6.2.5.3 Frontier链接制造工厂

Frontier表示一种为线程提供链接的工具,它通过一些特定的算法来决定哪个链接被送入处理器链中,并负责一定的日志和状态报告功能。Frontier的部分源代码如下:

……

List pendingURIs= new ArrayList();

List prerequisites= new ArrayList();

Map alreadyIncluded= new HashMap();

CrawlController controller;

boolean uriInProcess= false;

……

public synchronized void schedule(CandidateURI caURI){

if(! alreadyIncluded. containsKey( caURI.getURIString())){

if(caURI.needsImmediateScheduling()){

prerequisites.add(cdURI);

}else{

pendingURIs.add(caURI);

}

AlreadyIncluded.put(caURI.getURIString(),caURI);

}

}

……

首先,Frontier使用了两个ArrayList来保存链接,第一个pendingURIs保存的是等待处理的链接,第二个prerequisites中保存的是优先级高于pendingURIs里的链接。通常,在prerequisites中保存的都是如DNS之类的链接,只有当这些链接被首先解析后,其后续的链接才能够被解析。

除了这两个ArrayList外,在Frontier中还有一个名称为already-Included的HashMap。它用于记录已经被处理过的链接。当调用Frontier的schedule()方法来加入一个新的链接时,Frontier先检查这个链接是不是已经被处理过。同时,在schedule()方法中还加入了一些逻辑,用于判断当前要进入队列的链接是否属于需要优先处理的,如果是,则置入prerequisites队列中,否则,就简单地加入pendingURIs中即可。

Frontier中还有两个由抓取的线程来完成的方法,next()和finished()。Next()方法是从等待队列中取出一个链接并返回,然后抓取线程会在它自己的run()方法中完成对这个链接的处理。而finished()方法则是把整个处理过程中解析出的新的链接加入队列中,并且在处理完当前链接后,将之加入alreadyIncluded这个HashMap中去。

6.2.5.4 多线程ToeThr ead和ToePool

Heritrix提供了一个标准的线程池ToePool,它用于管理所有的抓取线程。ToePool和ToeThread都位于org.archive. crwaler. framework包中。ToePool是在CrawlController的initialize()方法中完成初始化的,以下是这段代码及其解析。

//构造函数

toePool= new ToePool(this);

//根据order.xml配置,实例化并启动进程

toePool.setSize(order.getMaxToes());

//ToePool的构造函数

public ToePool(CrawlController c){

super(“ToeThreads”);

this.controller= c;

}

通过调用父类java. lang.ThreadGroup的构造函数,同时将CrawlController赋给类变量。建立一个线程池的实例后,通过调用setSize(int)方法建立工作线程。

public void setSize(int newsize){

targetSize= newsize;

int difference= newsize.getToeCount(); if(difference>0){

for(int i=1;i<= difference;i++){ startNewThread();

}

}else{

int retainedToes= targetSize;

Thread[]toes= this.getToes();

for(int i=0;i<toes. length;i++){

if(!(toes[i]instanceof ToeThread)){

continue;

}

retainedToes--;

if(retainedToes>= 0){

continue;

}

ToeThread tt=(ToeThread)toes[i];

tt. retire();

}

}

}

private Thread[]getToes(){

Thread[]toes= new Thread[activeCount()+10];

This.enumerate(toes);

Return toes;

}

private synchronized void startNewThread(){

ToeThread newThread= new ToeThread(this,nextSerialNumber++);

newThread.setPriority(DEFAULT_TOE_PRIORITY);

newThread.start();

}

我们可以看出,线程池在创建的时候,并没有任何活动的线程实例,只有当它的setSize方法被调用时,才有可能创建新线程;如果当setSize方法被调用多次而传入不同的参数时,线程池会根据参数里所设定的值的大小,来决定池中所管理线程数量的增减。

当线程被启动后,执行run()方法来处理从Frontier中获得的链接,其代码如下:

public void run(){

Sting name= controller.getOrder().getCracwlOrderName();

Logger. fine(getName()+"started for order"+"name+");

try{

while(run)

{

ContinueCheck();

setStep(STEP_ABOUT_TO_GET_URI);

CrawlURI curi= controller.getFrontier().next();

synchronized(this){

continueCheck();

setCurrentCuri(curi);

}

processCrawlUri();

setStep(STEP_ABOUT_TO_RETURN_URI); continueCheck();

Synchronized(this){

Controller.getFrontier(). finished(currentCuri);

SetCurrentCuri(null);

}

setStep(STEP_FINISH ING_PROCESS);

lastFinishTime= System.currentTimeMillis();

Controller. releaseContinuePermission();

if(shouldRetire){

break;

}

}

}catch(EndedException e){

}catch(Exception e){

logger. log(Level.SEVERE,"Fatal exception in"+ get-Name(),e);

}catch(OutOfMenoryError err){

seriousError(err);

} finally{

controller. releaseContinuePermission();

}

setCurrentCuri(null);

this.httpRecorder.closeRecorders();

this.httpRecorder= null;

localProcessors= null;

logger. fine(getName()+"finished for order"+"name+");

setStep(STEP_FINISHED);

controller. toeEnded();

controller= null;

}

通过以上方法,工作线程就从Frontier中取得下一个待处理链接并对其进行处理,然后调用Frontier的finished方法完成收尾工作,如释放链接、清理缓存等,同时生成了一些日志来记录每次抓取的不同状态。

6.2.5.5 处理链和Pr ocesso r

Heritrix的处理链有: PreProcessor、Fetcher、Extractor、Writer、PostProcessor。Heritrix为了表示清楚整个处理器链的逻辑结构,设计了几个类来表示这种逻辑结构。

org.archive.crawler. framework.Processor

org.archive.crawler. framework.ProcessorChain

org.archive.crawler. framework.ProcessorChainList

Processor类代表单个的处理器,所有其他的处理器都是它的子类。在Processor类中有一个final类型的Process()方法,它不能被它的子类所覆盖。该方法首先检查是否允许这个处理器处理该链接,如果允许,则检查当前处理器所自带的过滤器是否能够接受这个链接。当过滤器的检查也通过后,则调用innerProcess(curi)方法来处理,如果过滤器的检查没有通过,就使用inflerRejectProcess(curi)方法处理。

其中innerProcess(curi)和innerRejectProcess(curi)方法都是Protected类型的,且本身没有实现任何内容。很明显它们是留在子类中,实现具体的处理逻辑。不过大部分的子类都不会重写innerRejectProcess(curi)方法,这是因为如果一个链接已经被当前处理器拒绝处理了,就不用再有什么逻辑了,直接跳到下一个处理器继续处理就行了。

ProcessorChain类表示一个队列,里面包括同类型的几个Pro-cessor。在一个ProcessorChain中,有3个私有类型的类变量:

private final MapType ProcessorMap;

private ProcessorChain nextChain;

private Processor firstProcessor;

其中,ProcessorMap中存放的是当前这个ProcessorChain中所有的Processor。nextChain的类型是ProcessorChain,它表示指向下一个处理器链的指针。而firstProcessor类是指向当前队列中的第一个处理器的指针。

ProcessorChainList类保存了Heritrix一次抓取任务中所设定的所有处理器链,形成一个列表。一个ProcessorChainList中,一般包括5个ProcessorChain,即PreProcessor链、Fetcher链、Extractor链、Writer链和PostProcessor链,而每个链中都包含多个Processor。如此一来,整个处理器就很好的被表示出来。

6.2.6  Nut ch与Her i t r ix比较

在前面两小节我们详细分析了Nutch和Heritrix的基本结构与重要组件、工作流程及特点。为了更好地比较这两个网络爬虫系统,下面将分别运用Nutch和Heritrix完成一次网络信息的爬取过程,然后再分析比较它们的共同点和差异,为我们构建自己的资源下载器获取灵感。

6.2.6.1 Nu t ch实践

要运行Nutch,首先要在Windows平台上用Eclipse调试Nutch。我们需要以下五个软件: JDK,Eclipse,Nutch,Cygwin,Tomcat。在完成解压Nutch的tar包和安装Cygwin之后,我们将Nutch的文件夹放到Cygwin的目录下,然后需要修改Nutch目录下conf子目录下的两个文件。第一个文件是nutch-site.xml文件,在configuration下增加一个http.agent.name节点。

<configuration>

<property>

<name>http.agent.name</name>

<value>HD nutch agent</value>

</property>

<property>

<name>http.agent.version</name>

<value>1.0</value>

</property>

</configuration>

第二个文件是crawl-urlfilter. txt文件,在+^http://([a-z0-9]*\.)*后加上需要爬行的网站,我们就加whu.edu. cn。然后在nutch根目录下新建seeds目录,再在里面建一个文本文件: seed. txt,在seed. txt中输入需要抓取的网址,必须是完整的网址,如图6-9所示。我们输入的网址是: http://www.whu.edu.cn/。

img74

图6-9 seed. txt

然后,打开Cygwin,执行命令行: cd/nutch-0.9/bin并回车,即进入nutch-0.9/bin的路径下,再输入命令: sh nutch crawl seedsdirmycrawl-depth 3-topN 50,如图6-10所示。

其中,Crawl命令是通知nutch. jar,执行Crawl的main方法,seeds中存放了我们已经定义的需要爬行的seeds. txt文件的目录,-dirmycrawl使爬行后文件保存在指定的位置,即nutch-0.9/bin文件夹下的mycrawl文件夹中。-depth 4指爬行次数,或者是深度,-topN 50指一个网站保存的最大页面数。当我们按下Enter键,执行以上命令,Nutch就开始工作,不断地爬取网页,并下载到本地,如图6-11所示。我们清楚地看到Nutch正在抓取的网页的url。

img75

图6-10 执行抓取

img76

图6-11 fetch

当爬行完成之后,我们需要查询爬行的结果。我们使用tomcat搜索nutch抓取的结果。首先,打开tomcat的根目录,将nutch-0.9.war文件复制进入tomcat\webapps下,如果tomcat处于启动状态就会生成一个新的项目文件夹nutch-0.9,如图6-12所示。

img77

图6-12 nutch-0.9.war

然后修改webapps/nutch/WEB-INF/classes/nutch-site.xml文件内容如下:

<property>

<name>searcher.dir</name>

<value>C:\\cygwin\\nutch-0.9\\bin\\mycrawl</value></property>

其中value值是我的项目文件夹mycrawl的路径。

配置完之后,在浏览器中查看nutch搜索的结果,如图6-13所示。

img78

图6-13 Nutch界面

Nutch为我们提供了多语种的界面选择,我们选择英文界面(中文界面有时出错)。我们现在查询结果,在输入框中输入关键字,以“news”为例,点击“search”,出现搜索结果,如图6-14所示。这样我们利用Nutch进行抓取和查询过程就完成了。

img79

图6-14 搜索结果

6.2.6.2 Her i t r ix实践

上一小节,我们详细地展示了Nutch的整个安装和使用过程,下面我们将进行Heritrix的实践。打开tomcat服务器,在Eclipse中配置完成后,我们就能方便地使用Heritrix了。

首先,在Eclipse中运行“heritrix-src-org.archive. crawler”下的Heritrix. java文件,如图6-15所示。

当我们运行Heritrix. java文件后,在Eclipse的控制台中,我们可以看到以下信息,如图6-16所示。

在图6-17中,我们看到控制台信息的第二行中显示的是,Heritrix的WebUI的登录地址,即127.0.0.1: 8080。其中127.0.0.1是本机地址,8080是端口号。于是,我们打开浏览器,输入此地址http://127.0.0.1: 8080/login. jsp,进入Heritrix的登录界面,如图6-17所示。

img80

图6-15 Heritrix. java文件

img81

图6-16 Eclipse的控制台

img82

图6-17 Heritrix登录界面

然后在输入框中,分别输入用户名和密码,点击Login按钮,我们就能成功登录WebUI,初始界面如图6-18所示。

img83

图6-18 Heritrix登录后界面

在图6-18中,我们看到Heritrix的控制台的显示是比较完备的,有当前任务的运行和完成情况,内存使用情况的重要信息。我们要新建一个爬虫任务,我们需要点击第一排导航条中的“Jobs”链接,开始建立一个抓取任务,如图6-19所示。

img84

图6-19 Create New Job界面

在图6-19中,我们可以看出创建一个新的任务,有四种选择。它们分别是: Based on existing job、Based on a recovery、Based on a profile和With defaults。我们选中第三种“Based on a profile”链接,如图6-20所示。

img85

图6-20 Basedonaprofile界面

点击图6-20所示的default链接,此时,我们才真正地开始配置新的一个新建的抓取任务,如图6-21所示。

img86

图6-21 default

在图6-21中,在“Name of a new job”中,需要填写此任务的名称,这个名称可以任意选取,我们把名称定为WHUCrawl; Description是关于这个Job的描述,用来为以后抓取提供参考; Seeds是指要抓取的网站的种子页面,以该Seeds为起始抓取点,在这里至少填写一个URL,也可以填写多个,如果填写多个URL的话,要保证每个URL在一行。我们只填写一个URL,即http:// www.whu.edu. cn。如图6-22所示。

img87

图6-22 WHUCrawl

然后,可点击“Modules”按钮,可对本次抓取任务的处理链进行,打开页面如图6-23所示。

我们使用的Heritrix-1.12.1版本,在新建一个抓取任务时,设置处理链页面已默认给定了一种配置,可以根据具体需求进行详细配置,如果没有特殊需要,也可以不进行调整,直接点击“Settings”链接。配置抓取任务的处理链,可以配置如下几项:

img88

图6-23 Modules

Select Crawl Scope

Select URI Frontier

Select Pre Processors

Select Fetchers

Select Extractors

SelectWriters

Select Post Processors

Select Statistics Tracking

在我们的项目中没有特殊的配置需求,所以我们直接点击“Settings”链接,进入配置运行时参数页面如图6-24所示。

图6-24中显示的只是参数设置的一部分,虽然需要设置的参数项很多,但是大部分参数有了默认值,我们可以不必修改。其中,我们需要填写的最重要的一项是http-header选项,其默认配置如图6-25所示。

点击左侧的“?”查看该项的提示信息。这里我要需要根据自己的具体情况进行配置。

User-agent主要是指运行Heritrix的用户的配置,即使用者的机器配置。首先是选择浏览器,这里可以使用Mozilla/5.0,也可以使用IE/7.0。后面的数字指的是浏览器的版本。我们使用默认设置。括号中的“compatible; heritrix/1.12.1”作为默认设置,不必更改。但是“PROJECT_ URL_ HERE”是指本机地址,需要根据实际情况更改。

img89

图6-24 Settings

img90

图6-25 http-headers

from是指一个Email联系方式,在这里必须填入正规的Email地址。当我们填写完这个设置之后,可以点击此页面最底下的导航条中的“Submit job”链接,如图6-26所示。

当我们提交一个抓取任务之后,页面会跳转到如图6-27所示页面。

img91

图6-26 Submit Job

img92

图6-27 Job create

在图6-27中,我们可以看到图的上方的粗体的“Job create”提示信息,在“Pending Jobs”下,显示的就是待处理的任务的信息,在此处仍可以对已创建的任务进行修改、删除的操作。这时,我们的抓取任务已经创建成功,但还没启动任务。

接下来,我们要启动这个名为WHUCrawl的抓取任务。点击导航条中的“Console”链接,回到WebUI控制台,如图6-28所示。

img93

图6-28 Holding Jobs

从图6-28中,我们看到该页面显示当前Heritrix抓取器的状态: Holding Jobs,即挂起状态。同时还显示了Jobs、Memory、Alert等信息。我们点击“Start”链接,抓取任务就启动了。页面如图6-29所示。

我们可以点击“Refresh”链接看此次任务实时抓取的信息。如图6-29所示,这些信息包括Rates(速率)、Load(加载)、Time(时间)、Total(总进度)等。需要注意的是,这里的实时进度的显示并不完全准确,它是一个近似的百分比。由于大部分设置都是默认值,所以我们抓取的范围是比较广的,所以对指定的一个种子页面,抓取的数据量可能会比较大,速度也较慢。特别是,当我们选择的种子页面是一个很宽泛的种子页面时,如http:// www.baidu. com,抓取后该种子页面的子页面和相关联的页面非常多,这样我们进行抓取任务的时间会很长。所以,我们在过一段时间后,需要再点击“Refresh”链接可以看某一时刻的抓取进度,如图6-30所示。

img94

图6-29 Running

img95

图6-30 Refresh

在重新点击“Refresh”之后,我们看到抓取任务接近完成了68%,已下载了339个页面到本地,还需请求下载156个。

当我们启动一个抓取任务后,可以在Heritrix的工作目录D:\workspace\ heritrix下面看到有一个Jobs文件夹,打开Jobs文件夹可以看到一个新的文件夹: WHUCrawl-20091202113525593,这个就是我们的那个创建的抓取任务的名称。打开该目录,可以看到一些配置文件,如图6-31所示。

img96

图6-31 Jobs文件夹

当一次抓取任务完成之后,我们将看到如图6-32所示的控制台界面。

图6-32显示: 0 pending,1 completed,即目前没有等待任务,一个任务已经完成。需注意的是,若一个任务出错,同样会出现图6-19所示界面,不同的是,Alert后的数字会增加。我们再点击导航条中的“Jobs”链接,如图6-33所示。

在图6-33中,Completed Jobs下显示的是刚才完成的抓取任务,这个人的UID是20091202113525593,名称是WHUCrawl,状态是Finished,后面还有一些选项,这些选项信息有助于我们更好地分析抓取的结果。点击“Crawl report”链接,我们将看到Heritrix对我们的抓取结果的分析,如图6-34所示。

img97

图6-32 Completed

img98

图6-33 Completed Jobs

这些报告有利于我们对整个抓取结果进行详细分析,如HTTP报告中显示:一共爬过563个链接,有519个,约92.2%正常,其余的链接因为3种不同原因出错。同时,这份报告对爬过的链接类型进行分析,我们清晰地看到在我们下载的文件中,image/jpeg、text/html和image/gif占绝大多数。当然,报告中还包含对Host及DNS相关信息的分析。

img99

图6-34 Crawl reports

如果点击“Logs”链接,我们可以看到此次抓取任务的日志文件,如图6-35所示。

在日志文件中,我们清楚了看到此次抓取任务爬取网页的全过程,包括抓取网页的时间和路径。

以上就是一次利用Heritrix完成抓取任务并获得分析结果的全过程。

img100

图6-35 Logs

6.2.6.3 比较分析

通过前面详细的介绍和实践,可以发现无论是Nutch还是Heritrix具备强大的抓取功能。但是它们还是有不少区别的。

Nutch不仅包含了抓取网页等信息,还包含了对内容进行索引;而Heritrix则只是对网页进行抓取,并力求保存页面的原貌;为了更高效的对网页进行索引,因此Nutch可以修剪内容,或者对内容格式进行转换; Nutch保存内容为数据库优化格式便于以后的索引,刷新并替换旧的内容,而Heritrix不会替换旧的内容,而是在旧的内容后追加新的内容; Nutch的运行和控制一般是通过命令行来执行,要在Windows系统中运行,还需要其他的插件,而Heritrix有Web控制管理界面; Nutch是一个成型的搜索引擎,因此其定制能力不够强,而Heritrix的可控制参数较多。因此对于垂直搜索来说,Heritrix为更好的选择。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈