最近在思考网络爬虫的增量更新问题,很明显,如果将URL放在unvisitedQueue中和visitedQueue中,而这两个队列仅仅简单的用JAVA提供的容器进行实现,那么由于内存的掉电易失性,再下一次爬爬取时,又是重复的从种子URL集合中取得URL,判断是否访问过,放入unvisitedQueue队列中,其实如果两次爬取的间隔时间不长的话,大量重复的链接被重复判断,效率极低。
如果爬虫想要实现增量更新,那么URL就应该存放在一种快速而持久的数据库中。那么第二次爬取的链接来自于unvisitedQueue中,相当于在第一次爬取的基础上进行更新。如果要判断以前爬取页面的时新性,可以用一个单独的模块,遍历已存储的链接(在每次爬取时,就获得页面时间),进行更新。
Berkeley DB是一种嵌入式的数据库,以键值对的方式进行数据的存储,而且对于数据的读和取非常快,非常满足我们对于URL队列,快速、持久及海量存取的要求。
基于Berkeyley DB的队列实现代码如下所示:
package com.ThemeSearchSpider.container;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.collections.StoredMap;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import java.io.*;
import java.util.Iterator;
import java.util.Map.Entry;
/*
* author:Tammy Pi function:用Berkeley DB模拟队列
*/
public class DBQueue implements Queue{
private EnvironmentConfig envConfig=null;
private Environment env=null;
private Database database=null;
private Database dbCatalog=null;
private StoredClassCatalog javaCatalog=null;
private String dbPath="e:\\BerkeleyDB\\unVisitedQueue\\";
private final String catalog="java_class_catalog";
private StoredMap map=null;
//构造函数,初始化
public DBQueue(){
if(!new File(dbPath).exists())
{
new File(dbPath).mkdir();
}
envConfig=new EnvironmentConfig();
envConfig.setTransactional(true);
envConfig.setAllowCreate(true);
env=new Environment(new File(dbPath),envConfig);
DatabaseConfig databaseConfig1=new DatabaseConfig();
databaseConfig1.setTransactional(true);
databaseConfig1.setAllowCreate(true);
dbCatalog=env.openDatabase(null,catalog,databaseConfig1);
javaCatalog=new StoredClassCatalog(dbCatalog);
DatabaseConfig databaseConfig=new DatabaseConfig();
databaseConfig.setAllowCreate(true);
databaseConfig.setTransactional(true);
database=env.openDatabase(null,"URL",databaseConfig);
SerialBinding keyBinding=new SerialBinding(javaCatalog,String.class);
SerialBinding valueBinding=new SerialBinding(javaCatalog,String.class);
map=new StoredMap(database,keyBinding,valueBinding,true);
}
//关闭数据库
public void close(){
if(dbCatalog!=null)
{
dbCatalog.close();
}
if(database!=null)
{
database.close();
}
if(env!=null)
{
env.close();
}
}
@Override
public void enQueue(String url) {
// TODO Auto-generated method stub
//如果不含有,才将url放入队列
if(!map.containsKey(url))
{
map.put(url,url);
}
}
@Override
public String deQueue() {
// TODO Auto-generated method stub
Iterator iterator=map.entrySet().iterator();
String url=null;
while(iterator!=null&&iterator.hasNext())
{
Entry<String,String> entry=(Entry<String, String>) iterator.next();
url=entry.getKey();
}
if(url!=null)
{
map.remove(url);
}
return url;
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return map.isEmpty();
}
@Override
public boolean contains(String url) {
// TODO Auto-generated method stub
return map.containsKey(url);
}
//用于测试的主函数
public static void main(String[] args)
{
DBQueue queue=new DBQueue();
queue.enQueue("http://www.baidu.com");
queue.enQueue("http://www.sina.com");
System.out.println(queue.contains("http://www.baidu1.com"));
System.out.println(queue.contains("http://www.baidu.com"));
while(!queue.isEmpty())
{
System.out.println(queue.deQueue());
}
queue.close();
}
}
Berkeley DB的下载链接:http://download.csdn.net/detail/rongyongfeikai2/4317973
首先写一个Queue的接口,定义好应该包括的方法;再用DBQueue实现Queue接口;再用unVisitedQueue和visitedQueue继承至DBQueue,这两个类没有多的代码,仅仅是数据库名字不同。这样共同构成了com.ThemeSearchSpider.container。