处理器链包括以下几种:
1 PreProcessor
2 Fetcher
3Extractor
4 Writer
5PostProcessor
为了很好的表示整个处理器链的逻辑结构,以及它们之间的链式调用关系,Heritrix设计了几个API来表示这种逻辑结构。
org.archive.crawler.framework.Processor
org.archive.crawler.framework.ProcessorChain
org.archive.crawler.framework.ProcessorChainList
下面进行详细讲解。
1.Processor类
该类代表着单个的处理器,所有的处理器都是它的子类。在Processor类中有一个process()
方法,它被标识为final类型的,也就是说,它不可以被它的子类所覆盖。代码如下。
public
final
void
process(CrawlURI curi)
throws
InterruptedException
{
//
设置下一个处理器
curi.setNextProcessor(getDefaultNextProcessor(curi));
try
{
//
判断当前这个处理器是否为enabled
if
(
!
((Boolean) getAttribute(ATTR_ENABLED, curi)).booleanValue()) {
return
;
}
}
catch
(AttributeNotFoundException e) {
logger.severe(e.getMessage());
}
//
如果当前的链接能够通过过滤器
//
则调用innerProcess(curi)方法
//
来进行处理
if
(filtersAccept(curi)) {
innerProcess(curi);
}
//
如果不能通过过滤器检查,则调
//
用innerRejectProcess(curi)来处理
else
{
innerRejectProcess(curi);
}
}
((Boolean) getAttribute(ATTR_ENABLED, curi)).booleanValue()说明上例是将先得到一个curi对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。
更多详解请登录http://crawler.archive.org/apidocs/org/archive/crawler/framework/Processor.html。
方法的含义很简单。即首先检查是否允许这个处理器处理该链接,如果允许,则检查当前处理器所自带的过滤器是否能够接受这个链接。当过滤器的检查也通过后,则调用innerProcess(curi)方法来处理,如果过滤器的检查没有通过,就使用innerRejectProcess(curi)方法处理。
其中innerProcess(curi)和innerRejectProcess(curi)方法都是protected类型的,且本身没有实现任何内容。很明显它们是留在子类中,实现具体的处理逻辑。不过大部分的子类都不会重写innerRejectProcess(curi)方法了,这是因为反正一个链接已经被当前处理器拒绝处理了,就不用再有什么逻辑了,直接跳到下一个处理器继续处理就行了。
2.ProcessorChain类
该类表示一个队列,里面包括了同种类型的几个Processor。例如,可以将一组的Extractor加入到同一个ProcessorChain中去。
在一个ProcessorChain中,有3个private类型的类变量:
private
final
MapType processorMap;
private
ProcessorChain nextChain;
private
Processor firstProcessor;
其中,processorMap中存放的是当前这个ProcessorChain中所有的Processor。nextChain的类型是ProcessorChain,它表示指向下一个处理器链的指针。而firstProcessor则是指向当前队列中的第一个处理器的指针。
3.ProcessorChainList
从名称上看,它保存了Heritrix一次抓取任务中所设定的所有处理器链,将之做为一个列表。正常情况下,一个ProcessorChainList中,应该包括有5个ProcessorChain,分别为PreProcessor链、Fetcher链、Extractor链、Writer链和PostProcessor链,而每个链中又包含有多个的Processor。这样,就将整个处理器结构合理的表示了出来。
那么,在ToeThread的processCrawlUri()方法中,又是如何来将一个链接循环经过这样一组
结构的呢?请看下面的代码:
private
void
processCrawlUri()
throws
InterruptedException {
//
设定当前线程的编号
currentCuri.setThreadNumber(
this
.serialNumber);
//
为当前处理的URI设定下一个ProcessorChain
currentCuri.setNextProcessorChain(controller.getFirstProcessorChain());
//
设定开始时间
lastStartTime
=
System.currentTimeMillis();
try
{
//
如果还有一个处理链没处理完
while
(currentCuri.nextProcessorChain()
!=
null
)
{
setStep(STEP_ABOUT_TO_BEGIN_CHAIN);
//
将下个处理链中的第一个处理器设定为
//
下一个处理当前链接的处理器
currentCuri.setNextProcessor(currentCuri
.nextProcessorChain().getFirstProcessor()
);
//
将再下一个处理器链设定为当前链接的
//
下一个处理器链,因为此时已经相当于
//
把下一个处理器链置为当前处理器链了
currentCuri.setNextProcessorChain(currentCuri
.nextProcessorChain().getNextProcessorCha
in());
//
开始循环处理当前处理器链中的每一个Processor
while
(currentCuri.nextProcessor()
!=
null
)
{
setStep(STEP_ABOUT_TO_BEGIN_PROCESSOR);
Processor currentProcessor
=
getProcessor(currentCuri.nextProcessor());
currentProcessorName
=
currentProcessor.getName();
continueCheck();
//
调用Process方法
currentProcessor.process(currentCuri);
}
}
setStep(STEP_DONE_WITH_PROCESSORS);
currentProcessorName
=
""
;
}
catch
(RuntimeExceptionWrapper e) {
//
如果是Berkeley DB的异常
if
(e.getCause()
==
null
) {
e.initCause(e.getDetail());
}
recoverableProblem(e);
}
catch
(AssertionError ae) {
recoverableProblem(ae);
}
catch
(RuntimeException e) {
recoverableProblem(e);
}
catch
(StackOverflowError err) {
recoverableProblem(err);
}
catch
(Error err) {
seriousError(err);
}
}
SerialNumber 属性
返回十进制序列号,用于唯一标识一个磁盘卷。object.SerialNumber
object 应为 Drive 对象的名称。
代码使用了双重循环来遍历整个处理器链的结构,第一重循环首先遍历所有的处理器链,第二重循环则在链内部遍历每个Processor,然后调用它的process()方法来执行处理逻辑。