- 浏览: 276135 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
86614009:
如何在service层,如何获取绑定到当前线程的entitna ...
使用spring的OpenEntityManagerInView -
yajunyajun2011:
好帖子 怎么没人顶呢
Java 正则表达式最大,最小匹配问题 -
xtuali:
能说明一下,你的nutch是哪个版本的吗?谢谢!
搜索引擎Nutch源代码研究之一 网页抓取(1) -
dongmusic:
需要学习这么多的东西,吐血中...
如何提高Java开发能力 -
jiminsc:
cool
LDAP 验证、添加、修改、删除(转)
搜索引擎Nutch源代码研究之一 网页抓取:
Nutch的爬虫代码部分主要集中在:package org.apache.nutch.fetcher和插件protocol-file
Protocol-ftp protocol-http protocol-httpclient以及相应的Parser插件中:
下面我们先从org.apache.nutch.fetcher开始:
最主要的类是Fetcher类,我们从它入手一步步跟踪整个代码:
我们从run函数入手:
首先:
建立了多个FetcherThread线程来抓取网页,threadCount可以配置或者使用默认值。
接着一个while(true)的循环里面的代码:
相当于维护一个线程池,并在Log中输入抓取页面的速度,状态之类的信息。其实可以使用java.util.concurrent包的Executors来创建一个线程池来使用。
现在我们看看抓取的线程FetcherThread是如何工作的:
线程当然要从run方法来跟踪了:
FetchListEntry fle = new FetchListEntry();
建立一个抓取列表类,为了不分散精力,我们稍候在看看这个FetchListEntry以及相关类的数据结构。
然后又是一个while (true)的循环,我们看看里面做了些什么:
如果不需要抓取,在handleFetch进行相应的处理。
然后又是一个do…while循环,用来处理抓取过程中重定向指定的次数:
整个循环的条件 refetch && (redirCnt < MAX_REDIRECT)
重新抓取并且重定向次数没有超出最大次数
ProtocolFactory工厂创建protocol实例:
Protocol protocol = ProtocolFactory.getProtocol(url);
Protocol的实现是以插件的形式提供的,我们先跳过Protocol实现的细节:
可以从protocol中获取到Fetch的输出流:
ProtocolOutput output = protocol.getProtocolOutput(fle);
通过输出流可以获取到抓取的状态ProtocolStatus和抓取的内容Content:
然后根据抓取的状态:
switch(pstat.getCode())
如果成功 case ProtocolStatus.SUCCESS:
如果内容不为空if (content != null)
修改抓取的页数,抓取的字节数,并且如果抓取了100页,根据pages,bytes在日志中记录抓取的速度等信息。
在handleFetch进行相应的处理
ParseStatus ps = handleFetch(fle, output);
如果处理返回的状态不为空,并且成功的重定向:
if (ps != null && ps.getMinorCode() == ParseStatus.SUCCESS_REDIRECT)
获取重定向的链接并进行过滤:
如果重定向的链接newurl不为空并且和现在的url不同:
if (newurl != null && !newurl.equals(url))
重新获取,更新refetch、url、redirCnt++;
创建当前页面的FetchListEntry:
fle = new FetchListEntry(true, new Page(url, NEW_INJECTED_PAGE_SCORE), new String[0]);
如果链接页面已经转移或者临时转移:
case ProtocolStatus.MOVED: // try to redirect immediately
case ProtocolStatus.TEMP_MOVED: // try to redirect immediately
立即重定向:
处理抓取的结果:
handleFetch(fle, output);
获取重定向的url:
过程和上面的重定向类似。
以下几种状态:
直接交由handleFetch(fle, output);来处理
如果发生异常,logger异常信息,然后交给handleFetch处理:
case ProtocolStatus.EXCEPTION:
logError(url, fle, new Exception(pstat.getMessage()));
handleFetch(fle, output);
其他情况为未知状态,log出当前的状态,然后交给handleFetch处理
default:
LOG.warning("Unknown ProtocolStatus: " + pstat.getCode());
handleFetch(fle, output);
循环结束。
最后如果完成的线程数等于threadCount,关闭所有的插件:
我们看到Fetch到页面后大多数的处理都交给了handleFetch了。
现在我们来看看private ParseStatus handleFetch(FetchListEntry fle, ProtocolOutput output) 的代码:
根据output获取到内容和url
如果content为null,我们直接空的content,然后对url 用digest编码,否则对content 用digest来编码:
在获取ProtocolStatus
ProtocolStatus protocolStatus = output.getStatus();
如果Fetcher不进行解析(parse),直接把抓取的页面写入磁盘
否则进行parse:
首先获取页面contentType,以便根据正确编码进行Parse的:
String contentType = content.getContentType();
下面便是使用Parser进行页面提取得过程:
如果提取页面成功:if (status.isSuccess())
将FetcherOutput提取的内容以及状态作为写入保存:
否则 else 将FetcherOutput和空的parse内容保存:
我们先跳过Parser的过程。下次我们看看如何在http协议下载的web页面,这就Protocol
插件的实现。
Nutch的爬虫代码部分主要集中在:package org.apache.nutch.fetcher和插件protocol-file
Protocol-ftp protocol-http protocol-httpclient以及相应的Parser插件中:
下面我们先从org.apache.nutch.fetcher开始:
最主要的类是Fetcher类,我们从它入手一步步跟踪整个代码:
我们从run函数入手:
首先:
- for (int i = 0; i < threadCount; i++) { // spawn threads
- FetcherThread thread = new FetcherThread(THREAD_GROUP_NAME+i);
- thread.start();
- }
for (int i = 0; i < threadCount; i++) { // spawn threads FetcherThread thread = new FetcherThread(THREAD_GROUP_NAME+i); thread.start(); }
建立了多个FetcherThread线程来抓取网页,threadCount可以配置或者使用默认值。
接着一个while(true)的循环里面的代码:
- int n = group.activeCount();
- Thread[] list = new Thread[n];
- group.enumerate(list);
- boolean noMoreFetcherThread = true; // assumption
- for (int i = 0; i < n; i++) {
- // this thread may have gone away in the meantime
- if (list[i] == null) continue;
- String tname = list[i].getName();
- if (tname.startsWith(THREAD_GROUP_NAME)) // prove it
- noMoreFetcherThread = false;
- if (LOG.isLoggable(Level.FINE))
- LOG.fine(list[i].toString());
- }
- if (noMoreFetcherThread) {
- if (LOG.isLoggable(Level.FINE))
- LOG.fine("number of active threads: "+n);
- if (pages == pages0 && errors == errors0 && bytes == bytes0)
- break;
- status();
- pages0 = pages; errors0 = errors; bytes0 = bytes;
- }
int n = group.activeCount(); Thread[] list = new Thread[n]; group.enumerate(list); boolean noMoreFetcherThread = true; // assumption for (int i = 0; i < n; i++) { // this thread may have gone away in the meantime if (list[i] == null) continue; String tname = list[i].getName(); if (tname.startsWith(THREAD_GROUP_NAME)) // prove it noMoreFetcherThread = false; if (LOG.isLoggable(Level.FINE)) LOG.fine(list[i].toString()); } if (noMoreFetcherThread) { if (LOG.isLoggable(Level.FINE)) LOG.fine("number of active threads: "+n); if (pages == pages0 && errors == errors0 && bytes == bytes0) break; status(); pages0 = pages; errors0 = errors; bytes0 = bytes; }
相当于维护一个线程池,并在Log中输入抓取页面的速度,状态之类的信息。其实可以使用java.util.concurrent包的Executors来创建一个线程池来使用。
现在我们看看抓取的线程FetcherThread是如何工作的:
线程当然要从run方法来跟踪了:
FetchListEntry fle = new FetchListEntry();
建立一个抓取列表类,为了不分散精力,我们稍候在看看这个FetchListEntry以及相关类的数据结构。
然后又是一个while (true)的循环,我们看看里面做了些什么:
- if (fetchList.next(fle) == null)
- break;
- url = fle.getPage().getURL().toString();
- 从当前的FetchListEntry中获得一个要抓取的url,然后
- if (!fle.getFetch()) { // should we fetch this page?
- if (LOG.isLoggable(Level.FINE))
- LOG.fine("not fetching " + url);
- handleFetch(fle, new ProtocolOutput(null, ProtocolStatus.STATUS_NOTFETCHING));
- continue;
- }
if (fetchList.next(fle) == null) break; url = fle.getPage().getURL().toString(); 从当前的FetchListEntry中获得一个要抓取的url,然后 if (!fle.getFetch()) { // should we fetch this page? if (LOG.isLoggable(Level.FINE)) LOG.fine("not fetching " + url); handleFetch(fle, new ProtocolOutput(null, ProtocolStatus.STATUS_NOTFETCHING)); continue; }
如果不需要抓取,在handleFetch进行相应的处理。
然后又是一个do…while循环,用来处理抓取过程中重定向指定的次数:
整个循环的条件 refetch && (redirCnt < MAX_REDIRECT)
重新抓取并且重定向次数没有超出最大次数
ProtocolFactory工厂创建protocol实例:
Protocol protocol = ProtocolFactory.getProtocol(url);
Protocol的实现是以插件的形式提供的,我们先跳过Protocol实现的细节:
可以从protocol中获取到Fetch的输出流:
ProtocolOutput output = protocol.getProtocolOutput(fle);
通过输出流可以获取到抓取的状态ProtocolStatus和抓取的内容Content:
ProtocolStatus pstat = output.getStatus(); Content content = output.getContent();
然后根据抓取的状态:
switch(pstat.getCode())
如果成功 case ProtocolStatus.SUCCESS:
如果内容不为空if (content != null)
修改抓取的页数,抓取的字节数,并且如果抓取了100页,根据pages,bytes在日志中记录抓取的速度等信息。
- synchronized (Fetcher.this) { // update status
- pages++;
- bytes += content.getContent().length;
- if ((pages % 100) == 0) { // show status every
- status();
- }
- }
synchronized (Fetcher.this) { // update status pages++; bytes += content.getContent().length; if ((pages % 100) == 0) { // show status every status(); } }
在handleFetch进行相应的处理
ParseStatus ps = handleFetch(fle, output);
如果处理返回的状态不为空,并且成功的重定向:
if (ps != null && ps.getMinorCode() == ParseStatus.SUCCESS_REDIRECT)
获取重定向的链接并进行过滤:
String newurl = ps.getMessage(); newurl = URLFilters.filter(newurl);
如果重定向的链接newurl不为空并且和现在的url不同:
if (newurl != null && !newurl.equals(url))
重新获取,更新refetch、url、redirCnt++;
refetch = true; url = newurl; redirCnt++;
创建当前页面的FetchListEntry:
fle = new FetchListEntry(true, new Page(url, NEW_INJECTED_PAGE_SCORE), new String[0]);
如果链接页面已经转移或者临时转移:
case ProtocolStatus.MOVED: // try to redirect immediately
case ProtocolStatus.TEMP_MOVED: // try to redirect immediately
立即重定向:
处理抓取的结果:
handleFetch(fle, output);
获取重定向的url:
- String newurl = pstat.getMessage();
- newurl = URLFilters.filter(newurl);
- if (newurl != null && !newurl.equals(url)) {
- refetch = true;
- url = newurl;
- redirCnt++;
- // create new entry.
- fle = new FetchListEntry(true, new Page(url, NEW_INJECTED_PAGE_SCORE), new String[0]);
- }
String newurl = pstat.getMessage(); newurl = URLFilters.filter(newurl); if (newurl != null && !newurl.equals(url)) { refetch = true; url = newurl; redirCnt++; // create new entry. fle = new FetchListEntry(true, new Page(url, NEW_INJECTED_PAGE_SCORE), new String[0]); }
过程和上面的重定向类似。
以下几种状态:
- case ProtocolStatus.GONE:
- case ProtocolStatus.NOTFOUND:
- case ProtocolStatus.ACCESS_DENIED:
- case ProtocolStatus.ROBOTS_DENIED:
- case ProtocolStatus.RETRY:
- case ProtocolStatus.NOTMODIFIED:
case ProtocolStatus.GONE: case ProtocolStatus.NOTFOUND: case ProtocolStatus.ACCESS_DENIED: case ProtocolStatus.ROBOTS_DENIED: case ProtocolStatus.RETRY: case ProtocolStatus.NOTMODIFIED:
直接交由handleFetch(fle, output);来处理
如果发生异常,logger异常信息,然后交给handleFetch处理:
case ProtocolStatus.EXCEPTION:
logError(url, fle, new Exception(pstat.getMessage()));
handleFetch(fle, output);
其他情况为未知状态,log出当前的状态,然后交给handleFetch处理
default:
LOG.warning("Unknown ProtocolStatus: " + pstat.getCode());
handleFetch(fle, output);
循环结束。
最后如果完成的线程数等于threadCount,关闭所有的插件:
- synchronized (Fetcher.this) {
- atCompletion++;
- if (atCompletion == threadCount) {
- try {
- PluginRepository.getInstance().finalize();
- } catch (java.lang.Throwable t) {
- // do nothing
- }
- }
- }
synchronized (Fetcher.this) { atCompletion++; if (atCompletion == threadCount) { try { PluginRepository.getInstance().finalize(); } catch (java.lang.Throwable t) { // do nothing } } }
我们看到Fetch到页面后大多数的处理都交给了handleFetch了。
现在我们来看看private ParseStatus handleFetch(FetchListEntry fle, ProtocolOutput output) 的代码:
根据output获取到内容和url
- Content content = output.getContent();
- MD5Hash hash = null;
- String url = fle.getPage().getURL().toString();
Content content = output.getContent(); MD5Hash hash = null; String url = fle.getPage().getURL().toString();
如果content为null,我们直接空的content,然后对url 用digest编码,否则对content 用digest来编码:
- if (content == null) {
- content = new Content(url, url, new byte[0], "", new Properties());
- hash = MD5Hash.digest(url);
- } else {
- hash = MD5Hash.digest(content.getContent());
- }
if (content == null) { content = new Content(url, url, new byte[0], "", new Properties()); hash = MD5Hash.digest(url); } else { hash = MD5Hash.digest(content.getContent()); }
在获取ProtocolStatus
ProtocolStatus protocolStatus = output.getStatus();
如果Fetcher不进行解析(parse),直接把抓取的页面写入磁盘
- if (!Fetcher.this.parsing) {
- outputPage(new FetcherOutput(fle, hash, protocolStatus),
- content, null, null);
- return null;
- }
if (!Fetcher.this.parsing) { outputPage(new FetcherOutput(fle, hash, protocolStatus), content, null, null); return null; }
否则进行parse:
首先获取页面contentType,以便根据正确编码进行Parse的:
String contentType = content.getContentType();
下面便是使用Parser进行页面提取得过程:
- Parser parser = null;
- Parse parse = null;
- ParseStatus status = null;
- try {
- parser = ParserFactory.getParser(contentType, url);
- parse = parser.getParse(content);
- status = parse.getData().getStatus();
- } catch (Exception e) {
- e.printStackTrace();
- status = new ParseStatus(e);
- }
Parser parser = null; Parse parse = null; ParseStatus status = null; try { parser = ParserFactory.getParser(contentType, url); parse = parser.getParse(content); status = parse.getData().getStatus(); } catch (Exception e) { e.printStackTrace(); status = new ParseStatus(e); }
如果提取页面成功:if (status.isSuccess())
将FetcherOutput提取的内容以及状态作为写入保存:
- outputPage(new FetcherOutput(fle, hash, protocolStatus),
- content, new ParseText(parse.getText()), parse.getData());
outputPage(new FetcherOutput(fle, hash, protocolStatus), content, new ParseText(parse.getText()), parse.getData());
否则 else 将FetcherOutput和空的parse内容保存:
- LOG.info("fetch okay, but can't parse " + url + ", reason: "
- + status.toString());
- outputPage(new FetcherOutput(fle, hash, protocolStatus),
- content, new ParseText(""),
- new ParseData(status, "", new Outlink[0], new Properties()));
LOG.info("fetch okay, but can't parse " + url + ", reason: " + status.toString()); outputPage(new FetcherOutput(fle, hash, protocolStatus), content, new ParseText(""), new ParseData(status, "", new Outlink[0], new Properties()));
我们先跳过Parser的过程。下次我们看看如何在http协议下载的web页面,这就Protocol
插件的实现。
发表评论
-
【转】搜索引擎最新技术发展分析
2011-11-21 09:19 686一、提高搜索引擎对用户检索提问的理解为了提高搜索引擎对用户检索 ... -
nutch官网下载,compass官网下载,lucene官网下载
2010-12-06 21:52 902nutch官网下载,compass官网下载,lucene官 ... -
Java开源搜索引擎[收藏]
2010-12-06 21:49 865Egothor Egothor是一个用Ja ... -
索引擎Nutch源代码研究之一 网页抓取(4)
2010-12-06 21:48 969今天来看看Nutch如何Parse网页的: Nutch使用了两 ... -
搜索引擎Nutch源代码研究之一 网页抓取(3)
2010-12-06 21:47 974今天我们看看Nutch网页抓取,所用的几种数据结构: 主要涉及 ... -
搜索引擎Nutch源代码研究之一 网页抓取(2)
2010-12-06 21:46 894今天我们来看看Nutch的源代码中的protocol-ht ... -
一个简单的JAVA网页爬虫
2010-12-05 14:26 928public class Access impleme ... -
Google的PageRank原理
2010-12-02 12:50 783PageRank我想稍微接触 ... -
中文分词技术
2010-12-02 12:49 802中文分词技术属于自然语言处理技术范畴,对于一句话 ... -
搜索引擎概述
2010-12-02 12:49 1057搜索引擎的概念 搜索引擎是一应用于web上的 ... -
国内搜索引擎技术现状
2010-12-02 12:48 849当你登录某一个网站 ... -
搜索引擎的技术发展趋势
2010-12-02 12:48 771搜索引擎经过几年的 ... -
什么是第三代搜索引擎
2010-12-02 12:48 854(www.marketingman.net ... -
聚焦爬虫
2010-12-02 12:45 1062聚焦爬虫,又称主题爬虫(或专业爬虫),是“面向特定主 ... -
Google搜索引擎的工作流程
2010-12-02 12:44 1591①Google使用高速的 ... -
福布斯评出最具发展潜力10大搜索引擎
2010-12-02 12:43 761美国知名财经杂志《福布斯》网络版周二评出了最具发展潜 ... -
网页爬虫程序pageSpider
2010-12-02 12:34 7462009-05-05 19:44 该程 ... -
lucene教程
2010-10-24 18:34 653Lucene是apache组织的一个用java实现全文搜索引擎 ...
相关推荐
《Lucene+nutch搜索引擎开发》书附带的源代码
nutch 源代码的详细分析,对于自己实现自己的搜索引擎很有帮助,尤其是将nutch项目嵌入到 自己的项目 当中很有帮助!
lucene nutch 搜索引擎 开发 实例 源代码 源码 包含lucene使用的所有源代码,从建立索引,搜索,删除,排序,都有,非常齐全 还有PDF 解析,WORD解析 ,EXCEL,ppt,xml解析等,,都有源码实现 还有nutch源码,spider...
Nutch是一个优秀的开放源代码的Web搜索引擎。虽然Nutch的页面排序方法比较合理,但是很多情况下仍然不能 满足需要。分析开源搜索引擎Nutch代码,研究了Nutch的页面排序方法。在Nutch原有的结构基础上提出了3种修改...
图解搜索引擎nutch配置,自己制作的教程。因为在网上搜索到的教程很多都是粗略,对于初学nutch搜索引擎很难配置好,所以自己亲自打造了一篇图解教程!希望你能够配置成功!
licene 实例代码 nutch实例代码 lucene+nutch搜索引擎开发实例代码(王学松版)
基于Java的搜索引擎Nutch中文搜索技术研究 摘要:Nutch是一个优秀的基于Java的开放源码搜索引擎,为了使它能够支持中文搜索,本文在分析了Nutch结构的基础上,采用词表分词技术和前向匹配分词算法对中文信息进行分词...
Lucene+nutch搜索引擎开发(源代码),内含本书的PDF电子下载地址。
Nutch 是一个开源的、Java 实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。 nutch 1.0
Nutch 是一个开源Java 实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。包括全文搜索和Web爬虫。 本资源官网上下的源代码。 nutch-2.1 适用于windows系统
Nutch搜索引擎·Nutch简介及安装(第1期) Nutch搜索引擎·Solr简介及安装(第2期) Nutch搜索引擎·Nutch简单应用(第3期) Nutch搜索引擎·Eclipse开发配置(第4期) Nutch搜索引擎·Nutch浅入分析(第5期)
相对于那些商用的搜索引擎, Nutch作为开放源代码 搜索引擎将会更加透明, 从而更值得大家信赖. 现在所有主要的搜索引擎都采用私有的排序算法, 而不会解释为什么一个网页会排在一个特定的位置. 除此之外, 有的搜索...
nutch源代码,共享给大家。nutch是一个开源的搜索引擎
这里是在网上搜到的Nutch配置的博客,比较详细,担心自己以后配置的时候忘了,所以传到csdn,顺便分享给大家。
其中内容均为前段时间研究开源搜索引擎时搜集参考的资料,非常齐全包含的内容有: Computing PageRank Using Hadoop.ppt Google的秘密PageRank彻底解说中文版.doc JAVA_Lucene_in_Action教程完整版.doc Java开源搜索...
基于Nutch的搜索引擎系统的研究与实现
Lucene+Nutch搜索引擎开发
非常实用的分布式搜索引擎开发工具nutch,有兴趣的赶紧下吧!
lucene+nutch搜索引擎开发一书源码第一部分,因为源码太大,所以分两部分。