2、中央控制器CrawlController
该类是一次抓取任务中的核心组件。塔将决定整个抓取任务的开始和结束。
org.archive.crawler.framework |
在它的Field声明中,看到的一部分代码,如下图所示:
public class CrawlController implements Serializable, Reporter { …… // key subcomponents which define and implement a crawl in progress private transient CrawlOrder order; private transient CrawlScope scope; private transient ProcessorChainList processorChains;
private transient Frontier frontier;
private transient ToePool toePool;
private transient ServerCache serverCache;
// This gets passed into the initialize method. private transient SettingsHandler settingsHandler; |
下面让我们来解释一下这几个组件:
l CrawlOrder:它保存了对该次抓取任务中order.xml的属性配置。
l CrawlScope:决定当前抓取范围的一个组件。
l ProcessorChainList:从名称上可知,其表示处理器链。
l Frontier:它是一个URL的处理器,决定下一个要被处理的URL是什么。
l ToePool:它表示一个线程池,管理了所有该抓取任务所创建的子线程。
l ServerCache:它表示一个缓冲池,保存了所有在当前任务中,抓取过的Host名称和Server名称。
1) 如何获得CrawlController实例,并通过自主编程来使用Heritrix提供的API进行一次抓取任务?
CrawlController提供了一个无参的构造函数:
public CrawlController() |
注意:在构造一个实例并进行抓取任务时,需完成以下几个步骤:
(1) 首先构造一个XMLSettingsHandler对象,将order.xml内的属性信息装入,并调用它的initialize方法进行初始化。
(2) 调用CrawlController构造函数,构造一个CrawlController实例
(3) 调用CrawlController的initilize(SettingsHandler)方法,初始化CrawlController实例。其中,传入的参数就是在第一步里构造的XMLSettingsHandler实例。
(4) 当上述3步完成后,CrawlController就具备了运行的条件。此时,只需调用它的requestCrawlStart()方法,就可以启动线程池和Frontier,然后开始不断的抓取网页。
1)其中在CrawlController的initilize(SettingsHandler)方法整个代码如下:
public void initialize(SettingsHandler sH) throws InitializationException { sendCrawlStateChangeEvent(PREPARING, CrawlJob.STATUS_PREPARING);
this.singleThreadLock = new ReentrantLock();
this.settingsHandler = sH; this.order = settingsHandler.getOrder(); this.order.setController(this); this.bigmaps = new Hashtable<String,CachedBdbMap<?,?>>(); sExit = ""; this.manifest = new StringBuffer(); String onFailMessage = ""; try { onFailMessage = "You must set the User-Agent and From HTTP" + " header values to acceptable strings. /n" + " User-Agent: [software-name](+[info-url])[misc]/n" + " From: [email-address]/n"; order.checkUserAgentAndFrom();
onFailMessage = "Unable to setup disk"; if (disk == null) { setupDisk(); }
onFailMessage = "Unable to create log file(s)"; setupLogs();
onFailMessage = "Unable to test/run checkpoint recover"; this.checkpointRecover = getCheckpointRecover(); if (this.checkpointRecover == null) { this.checkpointer = new Checkpointer(this, this.checkpointsDisk); } else { setupCheckpointRecover(); }
onFailMessage = "Unable to setup bdb environment."; setupBdb();
onFailMessage = "Unable to setup statistics"; setupStatTracking();
onFailMessage = "Unable to setup crawl modules"; setupCrawlModules(); } catch (Exception e) { String tmp = "On crawl: " + settingsHandler.getSettingsObject(null).getName() + " " + onFailMessage; LOGGER.log(Level.SEVERE, tmp, e); throw new InitializationException(tmp, e); } Lookup.getDefaultCache(DClass.IN).setMaxEntries(1);
setupToePool(); setThresholds();
reserveMemory = new LinkedList<char[]>(); for(int i = 1; i < RESERVE_BLOCKS; i++) { reserveMemory.add(new char[RESERVE_BLOCK_SIZE]); } } |
主要完成了以下操作:
(1) 从XMLSettingsHandler中取出order。
this.settingsHandler = sH; this.order = settingsHandler.getOrder(); |
(2) 检查用户设定的UserAgent等信息是否符合格式
order.checkUserAgentAndFrom(); |
(3) 设定开始抓取后保存文件信息的目录结构
if (disk == null) { setupDisk(); } |
(4) 初始化日志信息的记录工具
setupLogs(); |
(5) 初始化使用Berkley DB 的一些工具
setupBdb(); |
(6) 初始化Scope、Frontier以及ProcessorChain
setupCrawlModules(); |
(7) 最后实例化线程池
setupToePool(); |
在正常情况下,以上顺序不能随意变动,因为后一项功能的初始化很有可能需要前几项功能初始化的结果。例如线程池的初始化,必须要在先有了Frontier实例的基础上才可进行。
2)启动抓取工作的方法requestCrawlStart
代码如下:
/** * Operator requested crawl begin */ public void requestCrawlStart() { //初始化处理器链 runProcessorInitialTasks(); //设置抓取状态的改变,以便能够激发一些Listeners来处理相应的事件 sendCrawlStateChangeEvent(STARTED, CrawlJob.STATUS_PENDING); String jobState; state = RUNNING; jobState = CrawlJob.STATUS_RUNNING; sendCrawlStateChangeEvent(this.state, jobState);
// A proper exit will change this value. this.sExit = CrawlJob.STATUS_FINISHED_ABNORMAL; //开始日志线程 Thread statLogger = new Thread(statistics); statLogger.setName("StatLogger"); statLogger.start(); //启动Frontier,抓取工作开始 frontier.start(); } |
heritrix中默认采用的Frontier是BdbFrontier,其父类AbstractFroniter中的start()方法:
public void start() { if (((Boolean)getUncheckedAttribute(null, ATTR_PAUSE_AT_START)) .booleanValue()) { // 若配置文件中不允许该次抓取开始则停止 controller.requestCrawlPause(); } else { // 若允许开始,则开始 unpause(); } }
//其中的unpause()方法 synchronized public void unpause() { //去除当前阻塞变量 shouldPause = false; //唤醒所有阻塞线程,开始抓取任务 notifyAll(); } |