Heritrix

作为Java语言开发的、功能强大的网络爬虫Heritrix功能极其强大,且扩展性良好,深受热爱搜索技术的盆友们的喜爱,但它配置较为复杂,且源码不好理解,最近又使劲看了下,结合自己的学习和理解,跟大家分享Heritrix的点点滴滴。

Heritrix的下载(http://sourceforge.net/projects/archive-crawler/)安装、配置,就不罗嗦了,可以自己找找资料看看哦,很丰富的。

 

1HeritrixJob

HeritrixWebUI菜单栏上“Jobs”标签,即为Heritrix的爬取任务,可以通过4种创建方式来生成一个任务

Based on existing job:以一个已经有的抓取任务为模板,创建所有抓取属性和抓取起始URL的列表。

Based on a recovery:在以前的某个任务中,可能设置过一些状态点,新的任务将从这个设置的状态点开始。

Based on a profile:专门为不同的任务设置了一些模板,新建的任务将按照模板来生成。

With defaults:这个最简单,表示按默认的配置来生成一个任务。

Heritrix中,一个任务对应一个描述文件。这个描述文件的默认的名称为order.xml。每次创建一个新任务时,都相当于生成了一个order.xml的文件。order.xml中详细记录了Heritrix在运行时需要的所有信息。例如,它包括该用户所选择的Processor类、Frontier类、Fetcher类、抓取时线程的最大数量、连接超时的最大等待时间等信息。上面所说的4种创建抓取任务的方式,其实都是在生成一个order.xml文件。其中,第4With defaults,则是直接拷贝默认的order.xml文件。

下面是通过order.xml控制爬虫的一段简单的代码,还是比较好理解的:

public static void main(String[] args) {

try {

//order.xml的路径

String crawlOrderFile = "D:\\XX\\order.xml";

CrawlStatusListener listener = null;

File file = new File(crawlOrderFile);

XMLSettingsHandler handler = new     XMLSettingsHandler(file);

        handler.initialize();

        CrawlController controller = new CrawlController();

        controller.initialize(handler);

        if (listener != null) {

            controller.addCrawlStatusListener(listener);

        }

        //这里会启动N个线程去抓取页面,而程序也会在同时往下走

        controller.requestCrawlStart();

        while ( true ) {

          if ( false == controller.isRunning() ) {

         System.out.println( "Finished crawing!" );

         break;

          }

          Thread.sleep( 1000 );

        }

        // 必须调用此方面,释放内存

        controller.requestCrawlStop();

        System.out.println( "OK!" );

} catch ( Exception e ) {

e.printStackTrace();

}

}

order.xml的细节都可以在HeritrixWebUI中反映出来,可以装一个看看^_^

 

2Heritrix的架构

 

抓取任务CrawlOrder整个抓取工作的起点org.archive.crawler.settings包下有一个XMLSettingsHandler类,它可以用来帮助读取order.xml

public XMLSettingsHandler(File orderFile) throws InvalidAttributeValueException

XMLSettingsHandler的构造函数中,其所传入的参数orderFile正是一个经过对象封装的order.xmlFile。这样,就可以直接调用其构造函数,来创建一个XMLSettingsHandler的实例,以此做为一个读取order.xml的工具。当一个XMLSettingsHandler的实例被创建后,可以通过getOrder()方法来获取CrawlOrder的实例,可以参看上面简单的代码。

 

中央控制器CrawlController中央控制器是一次抓取任务中的核心组件。它将决定整个抓取任务的开始和结束。CrawlController位于org.archive.crawler.frameworkCrawlController有一个不带参数的构造函数,开发者可以直接通过它的构造函数来构造一个CrawlController的实例。但是值得注意的一点,在构造一个实例并进行抓取任务时,有几个步骤需要完成:

1)首先构造一个XMLSettingsHandler对象,将order.xml内的属性信息装入。

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

3)调用CrawlControllerintialize(SettingsHandler)方法,初始化CrawlController实例。其中,传入的参数是在第一步是构造的XMLSettingsHandler实例。

4)当上述3步完成后,CrawlController就已经具备运行的条件,可以开始运行了。此时,只需调用它的requestCrawlStart()方法,就可以启运线程池和Frontier,然后就可以开始不断的抓取网页了。

CrawlControllerinitialize()方法中,Heritrix主要做了以下几件事:

1)从XMLSettingsHandler中取出Order

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

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

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

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

6)初始化了ScopeFrontier以及ProcessorChain

7)最后实例化了线程池。

 

 

Frontier链接制造工厂Heritrix中,Frontier一种为线程提供链接的工具。它通过一些特定的算法来决定哪个链接将接下来被送入处理器链中,同时,它本身也负责一定的日志和状态报告功能。

Frontier中还有两个关键的方法,next()finished(),这两个方法都是要交由抓取的线程来完成的。Next()方法的主要功能是:从等待队列中取出一个链接并返回,然后抓取线程会在它自己的run()方法中完成对这个链接的处理。而finished()方法则是在线程完成对链接的抓取和后续的一切动作后(如将链接传递经过处理器链)要执行的。它把整个处理过程中解析出的新的链接加入队列中,并且在处理完当前链接后,将之加入alreadyIncluded这个HashMap中去

 

         处理链和ProcessorHeritrix处理器链包括以下几种:PreProcessorFetcherExtractorWriterPostProcessor为了很好的表示整个处理器链的逻辑结构,以及它们之间的链式调用关系,Heritrix设计了几个API来表示这种逻辑结构。

org.archive.crawler.framework.Processor

该类代表着单个的处理器,所有的处理器都是它的子类。

org.archive.crawler.framework.ProcessorChain

该类表示一个队列,里面包括了同种类型的几个Processor。例如,可以将一组的Extractor加入到同一个ProcessorChain中去。

org.archive.crawler.framework.ProcessorChainList

正常情况下,一个ProcessorChainList中,应该包括有5ProcessorChain,分别为PreProcessor链、Fetcher链、Extractor链、Writer链和PostProcessor链,而每个链中又包含有多个的Processor

      

3Heritrix的多线程

         Heritrix的多线程ToeThreadToePool想要更有效更快速的抓取网页内容,则必须采用多线程。Heritrix中提供了一个标准的线程池ToePool,它用于管理所有的抓取线程。

ToePoolToeThread都位于org.archive.crawler.framework包中。前面已经说过,ToePool的初始化,是在CrawlControllerinitialize()方法中完成的。

ToePool以及ToeThread是如何被初始化的。以下代码是在CrawlController中用于对ToePool进行初始化的。

// 构造函数

toePool = new ToePool(this);

// order.xml中的配置,实例化并启动线程

toePool.setSize(order.getMaxToes());

ToePool的构造函数很简单,如下所示:

public ToePool(CrawlController c) {

    super("ToeThreads");

    this.controller = c;

}

它仅仅是调用了父类java.lang.ThreadGroup的构造函数,同时,将注入的CrawlController赋给类变量。这样,便建立起了一个线程池的实例了,当线程被启动后,所执行的是其run()方法中的片段。

 

4Heritrix的扩展

Heritrix中添加自己的ExtractorHeritrix所提供的大众化的Extractor只能够将所有信息全部抓取下来。在这种情况下,就无法控制Heritrix到底该抓哪些内容,不该抓哪些内容,进而造成镜象信息太复杂,不好建立索引。

编写过滤器扩展Extractor时要做的几件事:

1)写一个类,继承Extractor的基类。

2)在构造函数中,调用父类的构造函数,以形成完整的家族对象。

3)继承extract(curi)方法。

public class MyExtractor extends Extractor {

// 构造函数

public MyExtractor(String name) {

this(name, "Sohu News Extractor");

}

// 构造函数

public MyExtractor(String name, String description) {

super(name, description);

}

// 第一个正则式,用于匹配SOHU新闻的格式

public static final String PATTERN_URLS ="<a href=\"http://127.0.0.1:8080/*\">";

// 第二个正则式,用于匹配所有的<a href="xxx">

public static final String PATTERN_A_HREF = "<a href=\"*\">";

// 继承的方法

protected void extract(CrawlURI curi) {

    //编写自己的代码控制逻辑

}

}

扩展FrontierScheduler来抓取特定的内容FrontierScheduler是一个PostProcessor,它的作用是将在Extractor中所分析得出的链接加入到Frontier中,以待继续处理。

比如,要去除所有的扩展名为.zip.exe.rar.pdf.doc的链接(其实也就是不想下载这类文件)。可以通过继承FrontierScheduler,并重写内部的schedule方法来达到我们的需要。以下是一个示例。

protected void schedule(CandidateURI caUri) {

    String url = caUri.toString();

    if (url.endsWith(".zip") 

         || url.endsWith(".rar")

         || url.endsWith(".exe")

         || url.endsWith(".pdf")

         || url.endsWith(".doc")

         || url.endsWith(".xls")) {

        return;

    }

    getController().getFrontier().schedule(caUri);

}

这样,每当Heritrix在执行任务时,遇到这样的文件,就会跳过抓取,从而达到了对URL链接进行筛选的目的。

以上均为学习总结,希望能和对爬虫感兴趣的童鞋共同探讨Heritrix

<!--EndFragment-->

猜你喜欢

转载自bill-chen.iteye.com/blog/966248
今日推荐