Frontier链接制造工厂

Frontier在英文中的意思是“前线,领域”,在Heritrix中,它表示一种具。它通过一些特定的算法来决定哪个链接将接下来被送入处理器链中,同时,它本身也负责一定的日志和状态报告功能。
事实上,要写出一个合格并且真正能够使用的Frontier绝非一件简单的事情,尽管有了Frontier接口,其中的方法约束了Frontier的行为,也给编码带来了一定的指示。但是其中还存在着很多问题,需要很好的设计和处理才可以解决。 在Heritrix的官方文档上,有一个Frontier的例子,在此拿出来进行一下讲解,以此来向读者说明一个最简单的Frontier都能够做什么事。以下就是这个Frontier的代码。
public class MyFrontier extends ModuleType implements Frontier, 
        FetchStatusCodes { 
……
}

声明各种变量;

// 列表中保存了还未被抓取的链接 
    List pendingURIs = new ArrayList(); 
     
    
// 这个列表中保存了一系列的链接,它们的优先级 
    
// 要高于pendingURIs那个List中的任何一个链接 
    
// 表中的链接表示一些需要被满足的先决条件 
    List prerequisites = new ArrayList(); 
     
    
// 一个HashMap,用于存储那些已经抓取过的链接 
    Map alreadyIncluded = new HashMap(); 
     
    
// CrawlController对象 
    CrawlController controller; 
 
    
// 用于标识是否一个链接正在被处理 
    boolean uriInProcess = false
     
    
// 成功下载的数量 
    long successCount = 0
    
// 失败的数量 
    long failedCount = 0
    
// 抛弃掉链接的数量 
    long disregardedCount = 0
    
// 总共下载的字节数 
    long totalProcessedBytes = 0
构造函数:
 public MyFrontier(String name) { 
        
super(Frontier.ATTR_NAME, "A simple frontier."); 
    } 
初始化,参数为一个CrawlController
public void initialize(CrawlController controller) 
            
throws FatalConfigurationException, IOException { 
         
        
// 注入 
        this.controller = controller; 
         
        
// 把种子文件中的链接加入到pendingURIs中去 
        this.controller.getScope().refreshSeeds(); 
        List seeds 
= this.controller.getScope().getSeedlist(); 
        
synchronized(seeds) { 
            
for (Iterator i = seeds.iterator(); i.hasNext();) { 
                UURI u 
= (UURI) i.next(); 
                CandidateURI caUri 
= new CandidateURI(u); 
                caUri.setSeed(); 
                schedule(caUri); 
            } 
        } 
    } 
该方法是给线程池中的线程调用的,用以取出下一个备处理的链接
public synchronized CrawlURI next(int timeout) throws 
InterruptedException { 
        
if (!uriInProcess && !isEmpty()) 

            uriInProcess 
= true
            CrawlURI curi; 
            
/* 
             * 算法很简单,总是先看prerequistes队列中是否有 
             * 要处理的链接,如果有,就先处理,如果没有 
             * 再看pendingURIs队列中是否有链接 
             * 每次在处理的时候,总是取出队列中的第一个链接 
             
*/ 
            
if (!prerequisites.isEmpty()) { 
                curi 
= CrawlURI.from((CandidateURI) prerequisites.remove(0)); 
            } 
else { 
                curi 
= CrawlURI.from((CandidateURI) pendingURIs.remove(0)); 
            } 
            
            curi.setServer(controller.getServerCache().getServerFor(curi)); 
            
return curi; 
        } 
else { 
            wait(timeout); 
            
return null
        } 
    } 

判断是否为空

public boolean isEmpty() { 
        
return pendingURIs.isEmpty() && prerequisites.isEmpty(); 
    } 
该方法用于将新链接加入到pendingURIs队列中,等待处理
 public synchronized void schedule(CandidateURI caURI) { 
        
/* 
         * 首先判断要加入的链接是否已经被抓取过 
         * 如果已经包含在alreadyIncluded这个HashMap中 
         * 则说明处理过了,即可以放弃处理 
         
*/ 
        
if (!alreadyIncluded.containsKey(caURI.getURIString())) { 
            
if(caURI.needsImmediateScheduling()) { 
                prerequisites.add(caURI); 
            } 
else { 
                pendingURIs.add(caURI); 
            } 
            
// HashMap中使用url的字符串来做为key 
            
// 而将实际的CadidateURI对象做为value 
            alreadyIncluded.put(caURI.getURIString(), caURI); 
        } 
    } 

 批处理

 public void batchSchedule(CandidateURI caURI) { 
        schedule(caURI); 
    } 
 
    
public void batchFlush() { 
    } 
一次抓取结束后所执行的操作,该操作由线程池中的线程来进行调用
 public synchronized void finished(CrawlURI cURI) { 
        uriInProcess 
= false
         
        
// 成功下载 
        if (cURI.isSuccess()) { 
            successCount
++
            
// 统计下载总数 
            totalProcessedBytes += cURI.getContentSize(); 
            
// 如果成功,则触发一个成功事件 
            
// 比如将Extractor解析出来的新URL加入队列中 
            controller.fireCrawledURISuccessfulEvent(cURI); 
            cURI.stripToMinimal(); 
        } 
        
// 需要推迟下载 
        else if (cURI.getFetchStatus() == S_DEFERRED) { 
            cURI.processingCleanup(); 
            alreadyIncluded.remove(cURI.getURIString()); 
            schedule(cURI); 
        } 
        
// 其他状态 
        else if (cURI.getFetchStatus() == S_ROBOTS_PRECLUDED 
                
|| cURI.getFetchStatus() == S_OUT_OF_SCOPE 
                
|| cURI.getFetchStatus() == S_BLOCKED_BY_USER 
                
|| cURI.getFetchStatus() == S_TOO_MANY_EMBED_HOPS 
                
|| cURI.getFetchStatus() == S_TOO_MANY_LINK_HOPS 
                
|| cURI.getFetchStatus() == S_DELETED_BY_USER) { 
            
// 抛弃当前URI 
            controller.fireCrawledURIDisregardEvent(cURI); 
            disregardedCount
++
            cURI.stripToMinimal(); 
        } 
else { 
            controller.fireCrawledURIFailureEvent(cURI); 
            failedCount
++
            cURI.stripToMinimal(); 
        } 
        cURI.processingCleanup(); 
    } 
最后的容器处理:
// 返回所有已经处理过的链接数量 
    public long discoveredUriCount() { 
        
return alreadyIncluded.size(); 
    } 
 
 
// 返回所有等待处理的链接的数量 
    public long queuedUriCount() { 
        
return pendingURIs.size() + prerequisites.size(); 
    } 
 
 
// 返回所有已经完成的链接数量 
    public long finishedUriCount() { 
        
return successCount + failedCount + disregardedCount; 
    } 
 
 
// 返回所有成功处理的链接数量 
    public long successfullyFetchedCount() { 
        
return successCount; 
    } 
 
 
// 返回所有失败的链接数量 
    public long failedFetchCount() { 
        
return failedCount; 
    } 
 
 
// 返回所有抛弃的链接数量 
    public long disregardedFetchCount() { 
        
return disregardedCount; 
    } 
 
 
// 返回总共下载的字节数 
    public long totalBytesWritten() { 
        
return totalProcessedBytes; 
    } 
 
    
public String report() { 
        
return "This frontier does not return a report."
    } 
 
    
public void importRecoverLog(String pathToLog) throws IOException { 
        
throw new UnsupportedOperationException(); 
    } 
 
    
public FrontierMarker getInitialMarker(String regexpr, 
            
boolean inCacheOnly) { 
        
return null
    } 
 
    
public ArrayList getURIsList(FrontierMarker marker, int numberOfMatches, 
            
boolean verbose) throws InvalidFrontierMarkerException { 
        
return null
    } 
 
    
public long deleteURIs(String match) { 
        
return 0
    } 
 

Frontier是用来向线程提供链接的,因此,在上面的代码中,使用了两个ArrayList来保存链接。其中,第一个pendingURIs保存的是等待处理的链接,第二个prerequisites中保存的也是链接,只不过它里面的每个链接的优先级都要高于pendingURIs里的链接。通常,在prerequisites中保存的都是如DNS之类的链接,只有当这些链接被首先解析后,其后续的链接才能够被解析。
除了这两个ArrayList外,在上面的Frontier还有一个名称为alreadyIncluded的HashMap。它用于记录那些已经被处理过的链接。每当调用Frontier的schedule()方法来加入一个新的链接时,Frontier总要先检查这个正要加入到队列中的链接是不是已经被处理过了。
很显然,在分析网页的时候,会出现大量相同的链接,如果没有这种检查,很有可能造成抓取任务永远无法完成的情况。同时,在schedule()方法中还加入了一些逻辑,用于判断当前要进入队列的链接是否属于需要优先处理的,如果是,则置入prerequisites队列中,否则,就简单的加入pendingURIs中即可。

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

唉!其实看不懂啊!…………

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值