Heritrix是一个开源的网络爬虫框架,主要用于建立高效率,可扩展的大规模网站抓取系统。Heritrix以Java编写,支持HTTP和HTTPS协议的网站爬取,同时还支持Java插件和自定义过滤器。其在互联网社区中广泛应用,是建立高质量网络文本检索系统的必要工具之一。本文的主要内容是对Heritrix的源码分析和使用方法进行介绍,并给出一些案例说明。
一、Heritrix的总体介绍
1.1 Heritrix的特点
Heritrix是一个大规模的网络爬虫框架,主要具有以下优点:
(1)高效性:Heritrix采用多线程技术,可以实现高速数据爬取,有效利用网络资源,并且支持分布式爬取。
(2)可扩展性:Heritrix支持Java插件和自定义过滤器,用户可根据需要进行二次开发,实现应用多样化。
(3)优秀的过滤器:Heritrix针对各种数据类型和文件格式进行了优秀的过滤器设计,可以对爬虫中的无效网址进行过滤,从而提高抓取效率和数据质量。
(4)用户友好性:Heritrix提供了丰富的文档和教程,方便用户学习和使用。此外,其具有单一的配置文件,可简化用户的配置工作。
1.2 Heritrix的架构
Heritrix的架构如下所示:
![Heritrix架构图](https://img-blog.csdn.net/20180403223623929)
Heritrix的架构分为三层,分别是控制层,管理层和工作层。
(1)控制层:控制层负责调度整个系统的运行,包括配置文件的读取,任务调度,系统监控,日志记录等。控制层采用了Bdb PersistencedQueue,用于任务分配和调度,实现了多个节点之间的分布式协调。
(2)管理层:管理层是整个系统的核心部分,包括爬虫模块,爬虫过滤器,解析器和分析器等。管理层主要完成网站的抓取和数据解析、处理等工作。
(3)工作层:工作层是实现将采集到的数据存储到数据库中,包括消息队列、数据存储和结果分析等操作。
1.3 Heritrix的主要组件
Heritrix包含了以下主要组件:
(1)CrawlerController:爬虫控制器,负责协调和管理爬虫任务。
(2)Frontier:调度器,按照一定策略规则控制任务的访问。
(3)Fetcher:获取器,负责获取URL链接。
(4)Processor:处理器,对爬取到的信息进行处理。
(5)Writer:写入器,将处理后的数据写入到指定存储中。
二、Heritrix源码分析
2.1 Heritrix的安装
在源码分析之前,我们需要先对Heritrix进行安装。Heritrix的安装流程基本可以分为以下几个步骤:
(1)下载安装包:Heritrix的官网地址是 http://crawler.archive.org,从官网上点击下载链接即可下载到Heritrix的安装包。
(2)安装JDK:安装JDK并配置环境变量,Heritrix依赖于JDK的运行环境。
(3)解压安装包:使用winrar等解压软件,解压安装包到指定的目录中。
(4)启动Heritrix:在命令行中进入Heritrix的安装目录,输入命令“bin\heritrix.bat”,即可启动Heritrix。
2.2 Heritrix的源码结构
Heritrix的源码结构如下所示:
![Heritrix源码结构图](https://img-blog.csdn.net/2018040322520179)
源码结构分为几个主要目录,分别是:
(1)src:源代码目录,包括类和接口的Java源代码。
(2)lib:依赖库目录,Heritrix所依赖的第三方库和插件都存放在该目录下。
(3)test:测试目录,包含对Heritrix程序包的单元测试和集成测试。
(4)docs:文档目录,包含Heritrix的使用手册和开发文档。
2.3 Heritrix的源码分析
2.3.1 CrawlerController
CrawlerController是Heritrix的爬虫控制器,负责协调和管理Heritrix的爬虫任务。CrawlerController主要完成三个任务:任务调度,创建工作线程,监视和反馈控制信息。下面是CrawlerController的主要源码:
```
public class CrawlerController {
// 控制器状态
private int status;
// 爬虫任务队列
private TaskQueue taskQueue;
// 工作线程池
private ExecutorService crawlerExecutors;
// 检测间隔时间(毫秒)
private long checkIntervalMs = 3000;
// 生产者线程
private Thread producerThread;
public void start() {
init();
startTaskProducer();
startCrawlingLoops();
}
/*
* 初始化函数,包括一些配置和初始化Bean
*/
public void init() {
taskQueue = new TaskQueue();
crawlerExecutors = Executors.newFixedThreadPool(100);
}
/*
* 启动任务生产者线程
*/
public void startTaskProducer() {
producerThread = new Thread(new TaskProducer(), "producer");
producerThread.setDaemon(false);
producerThread.start();
}
/*
* 启动数据爬取任务线程
*/
public void startCrawlingLoops() {
/*
* 设置线程池参数
*/
ThreadPoolExecutor pool = (ThreadPoolExecutor)crawlerExecutors;
pool.setMaximumPoolSize(200);
pool.setCorePoolSize(20);
for(int i=0; i<10; i++) {
crawlerExecutors.execute(new Loop(i));
}
}
/*
* 定时对Heritrix进行状态检测
*/
public void checkStatus() {
int unassigned = taskQueue.countUnassignedTasks();
int assigned = taskQueue.countAssignedTasks();
System.out.println("Unassigned:" + unassigned + " " +
"Assigned:" + assigned);
}
/*
* 控制器状态枚举类
*/
enum State {
CREATED,
INITIALISING,
INITIALISED,
STARTING,
RUNNING,
PAUSING,
PAUSED,
STOPPING,
STOPPED,
DESTROYING,
DESTROYED
}
/*
* 爬虫任务生产者线程,用于产生指定类型的任务
*/
class TaskProducer implements Runnable {
public void run() {
while (true) {
taskQueue.addTask(new CrawlTask());
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
* 爬虫任务执行线程
*/
class Loop implements Runnable {
private int id;
public Loop(int id) {
this.id = id;
}
public void run() {
state = State.RUNNING;
while (state == State.RUNNING) {
try {
CrawlTask task = taskQueue.assignNextAvailableTask();
HttpFetch fetcher = new HttpFetch(task);
fetcher.doFetch(null);
} catch (NoMoreTasksException e) {
break;
}
}
}
}
}
```
CrawlerController启动时会初始化一些基本的配置和Bean,包括任务队列,工作线程池等。然后会分别启动工作线程和任务生产者线程,负责生产和消费任务的调度。每个工作线程调用assignNextAvailableTask方法获取任务,然后通过HttpFetch执行爬取操作。
2.3.2 Frontier
Frontier是Heritrix的调度器,按照一定策略规则控制任务的访问。下面是Frontier的主要源码:
```
class Frontier {
// 待访问任务队列
private WorkQueue waitingQ;
// 已经访问任务队列
private WorkQueue snoozedQ;
// 正在处理任务队列
private WorkQueue inProcessQ;
// 处理错误任务队列
private WorkQueue errorsQ;
// 已访问任务数
private long alreadyIncludedCount;
// 此次访问任务数
private int addedThisProcess;
// 当前活跃线程数
private AtomicInteger activeThreadCount;
// 任务队列重启时间(毫秒)
private long rescheduleIntervalMs;
public Frontier() {
waitingQ = new WorkQueue();
snoozedQ = new WorkQueue();
inProcessQ = new WorkQueue();
errorsQ = new WorkQueue();
activeThreadCount = new AtomicInteger(0);
}
/*
* 加入新的任务URL链接
*/
public void schedule(URI uri, CrawlOrder order) {
waitingQ.schedule(uri, order);
}
/*
* 从工作队列中获取任务URL链接
*/
public CrawlURI next() {
try {
CrawlURI curi = waitingQ.dequeue();
if (curi != null) {
curi.setSchedulingDirective(S.DO);
curi.setHolderThread(Thread.currentThread());
curi.setFetchBeginTime(System.currentTimeMillis());
activeThreadCount.getAndIncrement();
}
return curi;
} catch (InterruptedException e) {
return null;
}
}
/*
* 待访问任务数
*/
public int waitingOrdinal() {
return waitingQ.getOrdinalOfEarliestQueued(null);
}
/*
* 改变任务状态
*/
public void setState(CrawlURI curi, short state) {
curi.setState(state);
switch (state) {
case S_WAITING:
waitingQ.enqueue(curi);
return;
case S_SNOOZED:
snoozedQ.enqueue(curi);
return;
case S_RUNNING:
inProcessQ.enqueue(curi);
return;
case S_OUTLINKED:
inProcessQ.remove(curi);
return;
case S_BLOCKED:
waitingQ.enqueue(curi);
return;
case S_DELETED:
waitingQ.deleteItem(curi);
snoozedQ.deleteItem(curi);
inProcessQ.deleteItem(curi);
return;
default:
errorsQ.enqueue(curi);
return;
}
}
}
```
Frontier主要包括四个任务队列,分别是待访问队列,已经访问队列,正在处理队列和处理错误队列等。Frontier的主要作用是负责对URL链接进行调度和任务管理,并通过调用相应的CrawlURI方法获取和改变任务状态。
2.3.3 Fetcher
Fetcher是Heritrix的获取器,负责获取URL链接。Fetcher主要完成两个任务:采取相应的爬取协议,并通过调用相应的方法获取和解析URL数据。Fetcher实现了采用HTTP协议的网络爬取。下面是Fetcher的主要源码:
```
public class HttpFetch {
private CrawlURI curi;
// 可重入httpclient
private MultiThreadedHttpClient http;
private HttpConnectionManager httpConnectionManager;
private static final int SOCKET_CONNECT_TIMEOUT = 20000;
private HttpState state;
public HttpFetch(CrawlURI curi) {
this.curi = curi;
http = new MultiThreadedHttpClient(curi.getThreadKey());
}
/*
* 执行HTTP协议访问
*/
public FetchResponse doFetch(DocumentHolder holder) {
HttpMethod method = null;
int statusCode = -1;
InputStream instream = null;
byte[] content = null;
HttpHeader httpHeader = null;
FetchResponse fr = null;
try {
URL url = curi.getURI().toURL();
HttpMethodBase.getParams().setParameter(
HttpMethodParams.USER_AGENT, useragent);
method = HttpMethodBase.buildMethod(methodName, url);
...
statusCode = http.executeMethod(method);
content = method.getResponseBody();
httpHeader = HttpHeader.createHeader(method);
...
} catch (Exception e) {
statusCode = HttpStatus.SC_BAD_REQUEST;
} finally {
method.releaseConnection();
IOUtils.closeQuietly(instream);
}
curi.setFetchCompletedTime(System.currentTimeMillis());
fr = new FetchResponse(statusCode, content, httpHeader.toNamedOrderedMap());
return fr;
}
}
```
Fetcher负责采用HTTP协议进行网络爬取,其中HTTPMethod是Apache HttpClient的主要组件。Fetcher在访问URL时还考虑许多问题,例如有些服务器只能在一定时间内访问,一旦访问过量就会屏蔽访问。在Heritrix中,Fetcher考虑了这些问题,实现了连接管理等功能。
2.3.4 Processor
Processor是Heritrix的处理器,对采集到的信息进行处理。Processor主要负责网页解析和数据格式转换等工作。下面是Processor的主要源码:
```
public class PageProcessor {
private ParserFactory parserFactory = null;
private ParseOutput parseOutput = null;
private ExtractorRegistry xreg;
public PageProcessor(ParserFactory parserFactory, ExtractorRegistry xreg) {
this.parserFactory = parserFactory;
this.xreg = xreg;
}
/*
* 执行Page内容的处理
*/
public void process(Page page) {
try {
HttpResponse response = page.getHttpResponse();
HttpHeader httpHeader = page.getHttpHeader();
InputSource source = new InputSource(new InputStreamReader(response));
Parse parser = null;
try {
parser = parserFactory.createParser(httpHeader.getContentType(), page.getCrawlURI());
} catch (Exception e) {
return;
}
if (parser == null) {
return;
}
ParseState parseState = new ParseState();
...
parser.parse(source, contentHandler, metadata, parseState);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/*
* 页面ContentHandler
*/
private ContentHandler getPageContentHandler(final Page page) {
return new ContentHandler() {
public void startDocument() throws SAXException {
}
public void endDocument() throws SAXException {
page.setContentParseComplete(true);
page.setParseEndTime(System.currentTimeMillis());
}
...
};
}
/*
* 页面MetadataHandler
*/
private MetadataHandler getPageMetadataHandler(final Page page) {
return new MetadataHandler() {
public void handle(String name, Object value) {
page.putToMetadata(name, value.toString());
}
};
}
}
```
Processor主要负责对采集到的网页内容进行解析,形成相应的Java对象,并对不同数据类型进行转换,最终将其存储到指定的存储设备中。Processor通过调用ParserFactory和ExtractorRegistry等组件,完成网页解析和数据格式转换等操作。
2.3.5 Writer
Writer是Heritrix的写入器,将处理后的数据写入到指定存储中。下面是Writer的主要源码:
```
public class Writer {
private String connectionString;
private Connection connection;
private Statement statement;
public Writer(String connectionString) {
this.connectionString = connectionString;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(connectionString);
statement = connection.createStatement();
} catch (Exception e) {
logger.error("Writer create error:" + e);
}
}
/*
* 把数据写入数据库中
*/
public void write(Collection ... try { String sql = "insert into table_name"; for(Writable w : list) { sql += w.toInsertStatement(); } statement.executeBatch(); } catch (SQLException e) { logger.error("Write to database error:{}", e); 壹涵网络我们是一家专注于网站建设、企业营销、网站关键词排名、AI内容生成、新媒体营销和短视频营销等业务的公司。我们拥有一支优秀的团队,专门致力于为客户提供优质的服务。 我们致力于为客户提供一站式的互联网营销服务,帮助客户在激烈的市场竞争中获得更大的优势和发展机会!
发表评论 取消回复