[转载]Heritrix 提高效率的若干方法

摘自http://blog.sina.com.cn/s/blog_6cc084c90100nf39.html

---------------------------------------------------------------------------------

 

一.利用ELFHash策略多线程抓取网页

    这些天看了其它小组的博客,发现大家用Heritrix抓取所花的时间都比较长,基本都要花上数天的时间才能抓完,名副其实的爬虫…之所以这么慢,一个重要的原因是heritrix在抓取时一般只运行了一个线程。在网上查找原因,得知这是因为在默认的情况下,Heritrix使用HostnameQueueAssignmentPolicy来产生key值,而这个策略是用hostname作为key值的,因此一个域名下的所有链接都会被放到同一个线程中去。如果对Heritrix分配URI时的策略进行改进,利用ELF hash算法把url尽量平均分部到各个队列中去,就能够用较多的线程同时抓取一个域名下的网页,速度将得到大大的提高。

    具体的做法如下:

    1.在org.archive.crawler.frontier下新建一个ELFHashQueueAssignmentPolicy类,这个类要注意继承自 QueueAssignmentPolicy。

    2.在该类下编写代码如下:

 1 public class ELFHashQueueAssignmentPolicy extends QueueAssignmentPolicy{
 2     @Override
 3     public String getClassKey(CrawlController controller, CandidateURI cauri) {
 4         return this.ELFHash(cauri.getUURI().toString(), 50) + "";
 5     }
 6 
 7     public int ELFHash(String str, int number) {
 8         int hash = 0;
 9         long x = 0l;
10         char[] array = str.toCharArray();
11         for (int i = 0; i < array.length; i++) {
12             hash = (hash << 4) + array[i];
13             if ((x = (hash & 0xF0000000L)) != 0) {
14                 hash ^= (x >> 24);
15                 hash &= ~x;
16             }
17         }
18         int result = (hash & 0x7FFFFFFF) % number;
19         return result;
20     }
21 }

 

    3.  修改AbstractFrontier 类的AbstractFrontier方法 :

    关键代码段是:

   String queueStr = System.getProperty(AbstractFrontier.class.getName() +
                 "." + ATTR_QUEUE_ASSIGNMENT_POLICY,
                  ELFHashQueueAssignmentPolicy.class.getName() + " " +

HostnameQueueAssignmentPolicy.class.getName() + " " +

IPQueueAssignmentPolicy.class.getName() + " " +

BucketQueueAssignmentPolicy.class.getName() + " " +

SurtAuthorityQueueAssignmentPolicy.class.getName());

Pattern p = Pattern.compile("\\s*,\\s*|\\s+");

String [] queues = p.split(queueStr);

       其中红色部分是新加的代码。

    4.  修改heritrix.properties 中的配置

 

        #############################################################################
        # F R O N T I E R                                                          
        #############################################################################

        # List here all queue assignment policies you'd have show as a
        # queue-assignment-policy choice in AbstractFrontier derived Frontiers
        # (e.g. BdbFrontier).
        org.archive.crawler.frontier.AbstractFrontier.queue-assignment-policy = \

              org.archive.crawler.frontier.ELFHashQueueAssignmentPolicy \

              org.archive.crawler.frontier.HostnameQueueAssignmentPolicy \

              org.archive.crawler.frontier.IPQueueAssignmentPolicy \

              org.archive.crawler.frontier.BucketQueueAssignmentPolicy \

              org.archive.crawler.frontier.SurtAuthorityQueueAssignmentPolicy \

              org.archive.crawler.frontier.TopmostAssignedSurtQueueAssignmentPolicy

        org.archive.crawler.frontier.BdbFrontier.level = INFO

    红色部分为新加部分。

    添加代码后运行的结果如下图,可见Heritrix已经在开50个线程同时抓取网页。

Heritrix提高抓取效率的若干尝试

    抓取速度得到了很大的提高,1.4G的网页8个多小时就抓好了。在Hosts栏里显示,只抓取了ccer.pku.edn.cn域名下的网页。



Heritrix提高抓取效率的若干尝试

</:O:P>

 

一些分析:

     1)添加的ELFHash算法程序并不算很复杂。ELFhash算法的基本思想是:将一个字符串的数组中的每个元素依次按前四位与上一个元素的低四位相与,组成一个长整形,如果长整的高四位大于零,那么就将它折回再与长整的低四位相异或,这样最后得到的长整对HASH表长取余,得到在HASH中的位置。

     2)ELFHash函数将输入的字符串进行哈希计算,输出算出的整数型哈希值。getClassKey函数中调用了ELFHash函数计算出哈希值,转换为字符串型返回上一层。之所以取模100是因为一般情况下Heritrix开100个线程,对应100个不同的URI处理队列。 

     3)  QueueAssignmentPolicy类源程序里的说明:

        * Establishes a mapping from CrawlURIs to String keys (queue names).

        * Get the String key (name) of the queue to which the

        * CrawlURI should be assigned.

        *

        * Note that changes to the CrawlURI, or its associated

        * components (such as CrawlServer), may change its queue

        * assignment.

    可知该类建立抓取到的URI和抓取队列名之间的映射。这个类是个抽象类,不同的策略由不同的子类实现,如根据域名、IP等。

    4)AbstractFrontier类是调度器基本实现类,是一个非常复杂的类,没有仔细研究。这里加在里面的程序作用大概是将ELFHashQueueAssignmentPolicy这个策略加入到运行时所使用的URI分配策略中。在heritrix.properties中的修改也同样为这个目的。

    5)由上可见使用这个策略后,速度有了非常大的提高。但抓下来的1.4G数据相比之前抓下来的有点小,大概是max-retries值设置得太低(原来是30,改为5),导致不少东西没有抓下来。

 

二.只抓取HTML对象

    由上面的图可以知道抓取的内容中有一些不需要用到的文件类型,比如pdf,jpeg等等。如何用Heritrix只抓特定的对象,比如只抓HTML型的。Heritrix的官方文档”Heritrix User Manual”中A.3节给出了一个解决方案:

    1)You would first need to create a job with the single seed http://foo.org/bar/. You'll need to add the MirrorWriterProcessor on the Modules screen and delete the ARCWriterProcessor. This will store your files in a directory structure that matches the crawled URIs, and the files will be stored in the crawl job's mirror directory.

    2)Your job should use the DecidingScope with the following set of DecideRules:

        RejectDecideRule

        SurtPrefixedDecideRule

        TooManyHopsDecideRule

        PathologicalPathDecideRule

        TooManyPathSegmentsDecideRule

        NotMatchesFilePatternDecideRule

        PrerequisiteAcceptDecideRule

    We are using the NotMatchesFilePatternDecideRule so we can eliminate crawling any URIs that don't end with .html. It's important that this DecideRule be placed immediately before PrerequisiteAcceptDecideRule; otherwise the DNS and robots.txt prerequisites will be rejected since they won't match the regexp.

    3)On the Setting screen, you'll want to set the following for the NotMatchesFilePatternDecideRule:

        decision: REJECT

        use-preset-pattern: CUSTOM

        regexp: .*(/|\.html)$

    根据需要,将正则表达式进行修改以满足需要,在这里更改为:

        (.*(/|\.(html|htm|xml|asp))$)|(.*\.asp\?.*)
    抓取的效果如下图所示:

Heritrix提高抓取效率的若干尝试
 

三.取消Robots.txt的限制

    Robots.txt是一种专门用于搜索引擎网络爬虫的文件,当构造一个网站时,如果作者希望该网站的内容被搜索引擎收录,就可以在网站中创建一个纯文本文件robots.txt,在这个文件中,声明该网站不想被robot访问的部分。这样,该网站的部分或全部内容就可以不被搜索引擎收录了,或者指定搜索引擎只收录指定的内容。因为大部分的网站并不会放置一个robots.txt文件以供搜索引擎读取,所以 Heritrix爬虫在抓取网页时会花费过多的时间去判断该Robots.txt文件是否存在,从而增加了抓取时间。好在这个协议本身是一种附加协议,完全可以不遵守。

Heritrix中,对robots.txt文件的处理是处于PreconditionEnforcer这个Processor中的。PreconditionEnforcer是一个Prefetcher,当处理时,总是需要考虑一下当前这个链接是否有什么先决条件要先被满足的,而对robots.txt的访问则正好是其中之一。在PreconditionEnforcer中,有一个private类型的函数,函数声明为: private boolean considerRobotsPreconditions(CrawlURI curi)  。该函数的含义为:在进行对参数所表示的链接的抓取前,看一下是否存在一个由robots.txt所决定的先决条件。该函数返回true时的含义为需要考虑robots.txt文件,返回false时则表示不需要考虑robots.txt文件,可以继续将链接传递给后面的处理器。所以,最简单的修改办法就是将这个方法整个注释掉,只返回一个false值。

网上声称使用这种办法可以提高抓取速度一半以上,由于抓取所花时间比较多,没有进行对比比较。以上的抓取都是在去除robots.txt情况下进行的。

posted @ 2015-11-25 09:30  张安  阅读(266)  评论(0编辑  收藏  举报