Spiderman是一款纯Java的网络爬虫开源软件,其以微内核,插件化带来的良好扩展性和维护性而著称,从这篇文章开始,我们将逐步对其源码进行分析,如有理解不到位之处,还望大家之处,本人在此现行谢过。本节就来看看spiderman的加载和初始化。
在谈加载和初始化之前,有必要对spiderman的几个核心类的组织和分工做个简单的说明,这个有利于后续内容的理解,贯穿爬取这条主线的有如下几个类:Spiderman,Spider,Task,Site,其它辅助性的功能类在讲到时再介绍。Spiderman,是爬虫的入口类,也是主调度类,主要包含两类信息:网站信息(待爬站点列表),调度控制信息。
Spider是具体的一个爬虫(负责一个具体的爬取任务(Task)),一个Task包含对一个具体页面Target的封装,Site对应一个具体的要爬取的站点,这个站点可能包含多个要爬取的目标(Target)。
要想启动一次爬取任务,需要做两部分工作:一,按照给定的格式填写相应网站的配置文件即:site.xml,二:编写客户端代码,例如:
//启动爬虫
spiderman.init(listener)//初始化
.startup();//启动
这里的spiderman就是一个Spiderman的实例,listener就是一个重写了SpiderListenerAdaptor的客户自定义的监听类,这个监听类的接口如下:
public class SpiderListenerAdaptor implements SpiderListener{
public void onDigUrls(Thread thread, Task task, String fieldName, Collection<String> urls) {}
public void onFetch(Thread thread, Task task, FetchResult result) {}
public void onNewUrls(Thread thread, Task task, Collection<String> newUrls) {}
public void onDupRemoval(Thread currentThread, Task task, Collection<Task> validTasks) {}
public void onTaskSort(Thread currentThread, Task task, Collection<Task> afterSortTasks) {}
public void onNewTasks(Thread thread, Task task, Collection<Task> newTasks) {}
public void onTargetPage(Thread thread, Task task, Page page) {}
public void onParse(Thread thread, Task task, List<Map<String, Object>> models) {}
public void onPojo(Thread thread, Task task, List<Object> pojos) {}
public void onInfo(Thread thread, Task task, String info) {}
public void onError(Thread thread, Task task, String err, Throwable e) {e.printStackTrace();}
<span style="color:#ff6666;">public void onAfterScheduleCancel() {}
public void onBeforeEveryScheduleExecute(Date theLastTimeScheduledAt){}
public void onBeforeShutdown() {}
public void onAfterShutdown() {}
public void onBeforeShutdown(Site site) {}
public void onAfterShutdown(Site site) {}
</span> public void onParseField(Thread thread, Task task, Object selector, String field, Object value) {}
public void onParseOne(Thread thread, Task task, int size, int index, Map<String, Object> model) {}
}
从接口列表,我们可以看出,用户可以重写这些接口来实现自己的业务逻辑,而spiderman会在相应的事件发生时调用这些接口,将具体的信息传递给客户端(其实就是观察者模式的应用)。这些不是我们要讲的重点,本节我们要讲的是init函数,即:加载和初始化,下面我们来看看这个函数都做了哪些工作:
public Spiderman init(){
if (this.listener == null)
this.listener = new SpiderListenerAdaptor();
isShutdownNow = false;
sites = null;
pool = null;
try {
<span style="color:#ff6666;">loadPlugins();</span>
<span style="color:#ff6666;"> initSites();
initPool();
</span> } catch (Throwable e){
e.printStackTrace();
listener.onInfo(Thread.currentThread(),null, "Spiderman init error.");
listener.onError(Thread.currentThread(), null, "Spiderman init error.", e);
}
return this;
}
很明显:这个函数主要做了三个工作:加载插件,初始化sites和初始化pool,下面分别来分析:
加载插件:
private void loadPlugins() throws Exception{
File siteFolder = new File(Settings.website_xml_folder());
if (!siteFolder.exists())
throw new Exception("can not found WebSites folder -> " + siteFolder.getAbsolutePath());
if (!siteFolder.isDirectory())
throw new Exception("WebSites -> " + siteFolder.getAbsolutePath() + " must be folder !");
File[] files &