Heritrix源码分析介绍 Heritrix总体介绍介绍

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 list) {

...

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内容生成、新媒体营销和短视频营销等业务的公司。我们拥有一支优秀的团队,专门致力于为客户提供优质的服务。

我们致力于为客户提供一站式的互联网营销服务,帮助客户在激烈的市场竞争中获得更大的优势和发展机会!

点赞(57) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部