原文链接:http://www.cnblogs.com/MichaelYin/archive/2011/08/25/2153767.html
上一篇Blog中说到了抓取任务的核心类CrawlController,这次我们来说说Frontier.Frontier是每次抓取时必须要用到的组件,每个Frontier知道哪个URI它正在处理,ToeThreads向Frontier请求URI,然后通过处理链对其进行处理,并将发现的连接通过scheduled返回到Frontier中,Frontier本身管理一个关于uri的列表,这个列表中的uri将被抓取。这就意味着Frontier控制了抓取的整体进度。
Heritrix把Frontier抽象成了interface,创建新的Frontier只需要实现这个接口就行。里面两个很重要的方法一个是schedule(),该方法接受一个URI并存起来以后进行抓取,另外一个是next,改方法返回一个URI供抓取,另外finished这个方法用来处理一个已经处理完毕的uri
对于待抓取的URI需要进行一个排序以决定被抓取URI的先后顺序,所以又必要在Frontier中实现一种策略在避免对于一个主机进行过度抓取,在Heritrix中使用了一种称为queue的概念,Frontier中有着若干个queue,将待抓取的uri根据一定的算法算出一个key ,然后根据这个key将url放入到queue中去,每一个queuq中的url的key都是相同的当一个queue中有uri正在抓取的时候,其他toethread是不能从中获取uri的,这样就有效的防止了对一个queue的过渡的抓取,我们可以配置heritrix使用的策略,比如根据hostname进行区分,或者IP地址等等。默认情况下,使用的是hostname。这个根据url得到key的类具体是由Queue-assignment-policy来管理的。
下面来具体的说说Frontier中queue的实现,WorkQueueFrontier这个类实现了关于queue的一些逻辑,里面提供了一些方法,我们只需要对其进行覆写就可以,BdbFrontier对部分方法进行了覆写,使用了Berkeley DB数据库来存存储信息,有效的避免了内存溢出,WorkQueue是队列的一个抽象类,也是提供了队列所需要的基本的方法,子类只需要对其中部分方法进行覆写即可,具体的实现可以看BdbWorkQueue这个类的实现
我们先来看看WorkQueueFrontier中的schedule方法,这个方法是将uri放入到frontier中去以供以后抓取,首先会通过UriUniqFilter进行一次判断,查看这个uri是否被抓取过,如果被抓取过则不会将其插入到队列中如果不是则调用receive方法将其插入到队列中去
在队列中的uri会在next方法中被toethread取出然后进行处理,处理完之后会调用finished方法通知已经处理完毕
0 | WorkQueue readyQ = null ; |
1 | Object key = readyClassQueues.poll(DEFAULT_WAIT,TimeUnit.MILLISECONDS); |
2 | if (key != null ) { |
3 | readyQ = (WorkQueue) this .allQueues. get (key); |
4 | } |
上面是next方法中的一段代码,可用的queue的key会放到readyClassQueues这个BlockingQueue<String>中去,如果一个queue在处理的话就不会被其他的toethread同时占用,这样有效的保证了politeness
另外这里的queue还有几种状态active snooze inactive,现在来简单说说这几种队列的区别,运行过程中一般情况下的queue都是active的,回到finished方法中,如果我不想爬虫对一个网站过度抓取,那么我需要对其抓取时间进行判断,如果一个uri的抓取时间太短的话,那么我需要暂时让这个queue小睡一下,这就是snooze的来历,调用snoozeQueue方法可以让queue进入snooze状态,在过一定时间后进行唤醒,重新进入active状态,进入activae状态的queue可以重新将其中包含的uri提供给toethread进行抓取。处于active状态的queue的个数是定的,如果超出预先设定的一些参数,则把其设置成inactive,当系统条件允许的时候重新进行activate
下面接着来说说QueueAssignmentPolicy,前面已经说过QueueAssignmentPolicy针对每个uri都会生成一个key,根据这个key来将key相同的uri组成一个队列,这样的话就能保证politeness,但是这样在有些情况下会产生一些问题,比如对一个网站进行定向抓取的时候,由于hostname是一个,那么极有可能造成只有一个toethread实际进行抓取,这样的抓取效率是很低的。因此,网上流传很广的解决方法就是使用ELF hash算法将url进行散列,使得uri分散到各个对垒中去,这样在实际中确实能显著的提高抓取速度。但是这个地方也会带来一些问题,网站的管理员有可能会封你的IP