扩展Heritix之添加Extractor

api帮助查询文档http://crawler.archive.org/apidocs/

Heritrix内嵌的Extractor并不能够很好的完成所需要的工作,这不是说它不够强大,而是因为在解析一个网页时,常常有特定的需要。比如,可能只想抓取某种格式的链接,或是抓取某一特定格式中的文本片断。Heritrix所提供的大众化的Extractor只能够将所有信息全部抓取下来。在这种情况下,就无法控制Heritrix到底该抓哪些内容,不该抓哪些内容,进而造成镜象信息太复杂,不好建立索引。 以下就使用一个实例,来讲解该如何定制和使用Extractor。这个实例其实很简单,主要功能就是抓取所有在Sohu的新闻主页上出现的新闻,并且URL格式如下所示。
http://news.sohu.com/20061122/n246553333.shtml
(1)分析一下这个URL可以知道,其中的主机部分是http://news.sohu.com,这是搜狐新闻的域名,“20061122”应该表示的是新闻的日期,而最后的“n246553333.shtml”应该是一个新闻的编号,该编号全部以“n”打头。
(2)有了这样的分析,就可以根据URL的特点,来定出一个正则表达式,凡是当链接符合该正则表达式,就认为它是一个潜在的值得抓取的链接,将其收藏,以待抓取。正则表达式如下:
http://news.sohu.com/[//d]+/n[//d]+.shtml
(3)事实上所有的Extractor均继承自org.archive.crawler.extractor.Extractor这个抽象基类,在它的内部实现了innerProcess方法,以下便是innerProcess的实现:

public   void  innerProcess(CrawlURI curi) { 
 
try  { 
        
/*  
         * 处理链接 
         
*/  
     extract(curi); 
 } 
catch  (NullPointerException npe) { 
     curi.addAnnotation(
" err= "   +  npe.getClass().getName()); 
     curi.addLocalizedError(getName(), npe, 
"" ); 
     logger.log(Level.WARNING, getName() 
+   " : NullPointerException "
npe); 
 } 
catch  (StackOverflowError soe) { 
     curi.addAnnotation(
" err= "   +  soe.getClass().getName()); 
     curi.addLocalizedError(getName(), soe, 
"" ); 
     logger.log(Level.WARNING, getName() 
+   " : StackOverflowError " , soe); 
 } 
catch  (java.nio.charset.CoderMalfunctionError cme) { 
     curi.addAnnotation(
" err= "   +  cme.getClass().getName()); 
     curi.addLocalizedError(getName(), cme, 
"" ); 
     logger.log(Level.WARNING, getName() 
+   " : CoderMalfunctionError "
cme); 
 } 
这个方法中,大部分代码都用于处理在解析过程中发生的各种异常和日志写入,不过,它为所有的Extractor定义了新的一个接口extract(CrawlURI),也就是说,所有的Extractor继承自它后,只需实现extract方法就可以了。以下是扩展Extractor时要做的几件事:
(1)写一个类,继承Extractor的基类。
(2)在构造函数中,调用父类的构造函数,以形成完整的家族对象。
(3)继承extract(curi)方法。
为了实现抓取news.sohu.com首页上所有新闻的链接,所开发的Extractor的完整源代码如下所示。
package  my; 
 
import  java.io.IOException; 
import  java.util.logging.Level; 
import  java.util.logging.Logger; 
import  java.util.regex.Matcher; 
import  java.util.regex.Pattern; 
 
import  org.apache.commons.httpclient.URIException; 
import  org.archive.crawler.datamodel.CrawlURI; 
import  org.archive.crawler.extractor.Extractor; 
import  org.archive.crawler.extractor.Link; 
import  org.archive.io.ReplayCharSequence; 
import  org.archive.util.HttpRecorder; 
 
public   class  SohuNewsExtractor  extends  Extractor { 
 
private   static  Logger logger  =  Logger.getLogger(SohuNewsExtractor. class  
   .getName()); 
 
//  构造函数 
  public  SohuNewsExtractor(String name) { 
        
this (name,  " Sohu News Extractor " ); 
    } 
 
//  构造函数 
  public  SohuNewsExtractor(String name, String description) { 
  
super (name, description); 
 
 } 
 
 
//  第一个正则式,用于匹配SOHU新闻的格式 
  public   static   final  String PATTERN_SOHU_NEWS  =   
                                 
" http://news.sohu.com/[/d]+/n[/d]+.shtml "
 
 
//  第二个正则式,用于匹配所有的<a href="xxx"> 
  public   static   final  String PATTERN_A_HREF  =   
                                 
" <a/s+href/s*=/s*("([^"]*)"|[^/s>])/s*> "
 
 
//  继承的方法 
  protected   void  extract(CrawlURI curi) { 
 
  
//  将链接对象转为字符串 
  String url  =  curi.toString(); 
 
  
/*  
   * 下面一段代码主要用于取得当前链接的返回 字符串,以便对内容进行分析时使用 
   
*/  
  ReplayCharSequence cs 
=   null
  
try  { 
   HttpRecorder hr 
=  curi.getHttpRecorder(); 
   
if  (hr  ==   null ) { 
    
throw   new  IOException( " Why is recorder null here? " ); 
   } 
   cs 
=  hr.getReplayCharSequence(); 
  } 
catch  (IOException e) { 
   curi.addLocalizedError(
this .getName(), e, 
     
" Failed get of replay char sequence  "   +  curi.toString() 
       
+   "   "   +  e.getMessage()); 
   logger.log(Level.SEVERE, 
" Failed get of replay char sequence in 
"
 
      +  Thread.currentThread().getName(), e); 
  } 
 
  
//  如果什么也没抓取到,就返回 
   if  (cs  ==   null ) { 
   
return
  } 
 
  
//  将链接返回的内容转成字符串 
  String content  =  cs.toString(); 
  
try  { 
    
   
//  将字符串内容进行正则匹配 
   
//  取出其中的链接信息 
   Pattern pattern  =  Pattern.compile(PATTERN_A_HREF, 
     Pattern.CASE_INSENSITIVE); 
   Matcher matcher 
=  pattern.matcher(content); 
 
   
//  若找到了一个链接 
    while  (matcher.find()) { 
    String newUrl 
=  matcher.group( 2 ); 
    
//  查看其是否为SOHU新闻的格式 
     if  (newUrl.matches(PATTERN_SOHU_NEWS)) { 
     
//  若是,则将链接加入到队列中 
     
//  以备后续处理 
     addLinkFromString(curi, newUrl,  "" , Link.NAVLINK_HOP); 
    } 
   } 
 
  } 
catch  (Exception e) { 
   e.printStackTrace(); 
  } 
 } 
 
 
//  将链接保存记录下来,以备后续处理 
  private   void  addLinkFromString(CrawlURI curi, String uri, 
   CharSequence context, 
char  hopType) { 
  
try  { 
   curi.createAndAddLinkRelativeToBase(uri, context.toString(), 
     hopType); 
  } 
catch  (URIException e) { 
   
if  (getController()  !=   null ) { 
    getController().logUriError(e, curi.getUURI(), uri); 
   } 
else  { 
    logger.info(
" Failed createAndAddLinkRelativeToBase  "   
    
+  curi  +   " "   +  uri  +   " "   +  context  +   " "   
    
+  hopType  +   " "   +  e); 
   } 
  } 
 } 

注:还是不了解到底是如何继承extract(curi)方法的,难道是继承了Extractor中的innerprocess中的方法?还是详讲一下吧。

(1)首先是将Fetcher所获得的链接的HTML响应取得,并转成字符串,这样,才有可能在后面对页面中的链接做处理。
(2)从页面内容中,使用正则式取出所有链接的内容。判断链接是否符合Sohu的新闻格式,倘若符合,则调用addLinkFromString()方法,来将这个链接加入到某个队列缓存中,以备后续的处理。
在Extractor类开发完毕后,如果使用WebUI的方式启动Heritrix,并让它出现在下拉选项中,则需要修改Eclipse工程中的modules目录下的Processor.options文件。

打开Processor.options文件可以看到,所有在WebUI中设置处理器链时,页面上的下拉列表中的数据都保存在了其中,为了加入我们开发的SohuNewsExtractor,只需在其中合适的位置上加入一行,内容如下所示:
my.SohuNewsExtractor|SohuNewsExtractor
接下来,再次启动Heritrix,创建一个任务,进入处理器链设置的页面,就可以看到自己开发的Extractor了。 
 
选择后,单击“Add”按钮,就可以将其加入到队列中。

需要注意的是,一定要将其置于ExtractorHTTP的后面,以保证Heritrix能够先行处理HTTP协议中的相关内容。与加入自己定制的Extractor的过程类似,开发者们也可以定制其他几种处理器。同样,只需要在modules目录下找到相应的.options文件,然后将类全名加入即可。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值