- 浏览: 1653450 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (405)
- C/C++ (16)
- Linux (60)
- Algorithm (41)
- ACM (8)
- Ruby (39)
- Ruby on Rails (6)
- FP (2)
- Java SE (39)
- Java EE (6)
- Spring (11)
- Hibernate (1)
- Struts (1)
- Ajax (5)
- php (2)
- Data/Web Mining (20)
- Search Engine (19)
- NLP (2)
- Machine Learning (23)
- R (0)
- Database (10)
- Data Structure (6)
- Design Pattern (16)
- Hadoop (2)
- Browser (0)
- Firefox plugin/XPCOM (8)
- Eclise development (5)
- Architecture (1)
- Server (1)
- Cache (6)
- Code Generation (3)
- Open Source Tool (5)
- Develope Tools (5)
- 读书笔记 (7)
- 备忘 (4)
- 情感 (4)
- Others (20)
- python (0)
最新评论
-
532870393:
请问下,这本书是基于Hadoop1还是Hadoop2?
Hadoop in Action简单笔记(一) -
dongbiying:
不懂呀。。
十大常用数据结构 -
bing_it:
...
使用Spring MVC HandlerExceptionResolver处理异常 -
一别梦心:
按照上面的执行,文件确实是更新了,但是还是找不到kernel, ...
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法 -
dsjt:
楼主spring 什么版本,我的3.1 ,xml中配置 < ...
使用Spring MVC HandlerExceptionResolver处理异常
搜索引擎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(); }
建立了多个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; }
相当于维护一个线程池,并在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; }
如果不需要抓取,在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(); } }
在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]); }
过程和上面的重定向类似。
以下几种状态:
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 } } }
我们看到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为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()); }
在获取ProtocolStatus
ProtocolStatus protocolStatus = output.getStatus();
如果Fetcher不进行解析(parse),直接把抓取的页面写入磁盘
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); }
如果提取页面成功:if (status.isSuccess())
将FetcherOutput提取的内容以及状态作为写入保存:
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()));
我们先跳过Parser的过程。下次我们看看如何在http协议下载的web页面,这就Protocol
插件的实现。
评论
4 楼
youngliu_liu
2014-02-28
楼主是怎样把代码看明白的呢?我看很费劲,能给在讲一下Fetch类的原理吗
3 楼
laomosan
2008-08-05
万分感谢你,太有用了!!
2 楼
fuliang
2007-12-17
Nutch的源代码,你可以用svn从http://svn.apache.org/viewvc/lucene/nutch/下到啊
1 楼
SunShineBoy
2007-12-17
我也做数据抓取呢,楼主能不能把这整理成一个包,可以下载啊!
发表评论
-
Lucene 索引格式
2013-06-25 20:11 0索引结构: 索引层次 ... -
计算广告学
2012-08-12 13:53 0计算广告学一: 1、核 ... -
《Lucene in Action》简单笔记
2011-12-22 09:19 0第一章 Meet Lucene -
Information Retrieval Resources
2011-04-07 16:40 1388Information Retrieval Resource ... -
使用Jsoup抽取数据
2011-03-20 19:22 4945Jsoup是一个Java的HTML解析器,提供了非常方便的抽取 ... -
常见文件类型识别
2010-09-22 20:09 11879根据文件的后缀名识别文件类型并不准确,可以使用文件的头信息进行 ... -
(zz)信息检索领域资料整理
2010-06-05 13:05 3171A Guide to Information Retrieva ... -
Introduce to Inforamtion Retrieval读书笔记(2)
2009-10-31 13:02 1934The term vocabulary and posting ... -
Introduce to Inforamtion Retrieval读书笔记(1)
2009-10-25 23:49 2052很好的一本书,介绍的非常全面,看了很久了,还没有看完,刚看完前 ... -
Query Log Mining notes
2009-10-02 18:08 1272Enhancing Efficiency of Search ... -
百度搜索的一些高级语法
2009-08-27 20:06 19321.title语法 就是在title ... -
Hadoop好书推荐:Hadoop The Definitive Guide
2009-08-16 22:49 3639第一本详细介绍Hadoop的书籍,从网上下来看了几章,作者是H ... -
Java开源搜索引擎[收藏]
2008-04-24 00:09 2903Egothor Egothor是一个用Java编写的开 ... -
分享一本斯坦福的信息检索的教材
2008-01-04 23:59 2462斯坦福的信息检索的教材,还没出版,先分享一下电子版原稿. 对于 ... -
分享一本搜索引擎的电子书
2007-12-29 19:42 2530还没有来得及看,但搜索引擎的书不是很好找,先放上,希望对大家能 ... -
分享一个Nutch入门学习的资料
2007-12-18 20:49 4266分享一个Nutch入门学习的资料,感觉写的还不错. -
搜索引擎Nutch源代码研究之一 网页抓取(4)
2007-12-17 22:37 8396今天来看看Nutch如何Parse网页的: Nutch使用了两 ... -
[转]MAP/REDUCE:Google和Nutch实现异同及其他
2007-12-15 19:21 2997设计要素 nutch包含以下几个部分: 辅助类 Log:记载运 ... -
Nutch源代码学习小小总结一下
2007-12-15 19:13 4466我现在看得源码主要是网页抓取部分,这部分相对比较容易。我首先定 ... -
搜索引擎Nutch源代码研究之一 网页抓取(3)
2007-12-15 16:39 4586今天我们看看Nutch网页抓取,所用的几种数据结构: 主要涉及 ...
相关推荐
从Apache官方网站下载Nutch的最新源代码,通常通过Git克隆仓库。解压后,进入Nutch的工作目录。 3. **配置Nutch** 打开`conf/nutch-site.xml`文件,这是Nutch的主要配置文件。以下是一些关键配置项: - `...
2. **Nutch源代码**:包括Nutch的爬虫模块、索引模块和搜索模块,可以帮助开发者学习如何配置和运行一个完整的网络爬虫,以及如何与Lucene集成进行全文检索。 3. **示例项目**:可能包含了一些示例应用,展示如何...
Nutch不仅仅是一个搜索引擎,它还包含了一个Web爬虫,能够抓取互联网上的网页,并对抓取的数据进行索引和处理。 Nutch的源代码包含了整个项目的完整实现,包括爬虫、索引器、搜索器以及相关的配置和文档。这对于...
Nutch 的源代码解析对于深入理解搜索引擎的工作原理以及自定义搜索引擎的实现非常有帮助。下面我们将详细探讨 Nutch 的注入(Injector)过程,这是整个爬取流程的第一步。 Injector 类在 Nutch 中的作用是将输入的 ...
总的来说,王学松的“Lucene+Nutch搜索引擎开发实例代码”是一份宝贵的教育资源,它可以帮助开发者快速入门搜索引擎开发,并深入了解这两个项目的内部工作机制。通过实践这些代码,不仅可以提升技术能力,还能为构建...
《lucene+nutch搜索引擎开发源码1》是一个包含开源搜索引擎项目Lucene和Nutch源代码的压缩包,主要针对搜索引擎开发的学习和实践。这个压缩包是书籍《lucene+nutch搜索引擎开发》的一部分,由于源码量较大,因此分为...
分布式搜索引擎Nutch开发详解 Nutch是一款开源的、基于Java实现的全文搜索引擎,它主要用于构建大规模的网络爬虫系统,并提供了对抓取的网页进行索引和搜索的功能。Nutch与Hadoop紧密集成,能够充分利用分布式计算...
《lucene+nutch开发自己的搜索引擎一书源代码》是一份专为初学者设计的资源,旨在教授如何利用Apache Lucene和Nutch构建自定义搜索引擎。Lucene是Java编写的一个高性能全文检索库,而Nutch则是一个开源的网络爬虫...
Nutch 1.5 是一个基于Java开发的开源搜索引擎项目,它主要负责网络抓取、索引和搜索等功能。这个源代码包包含了实现这些功能的所有模块和组件,为开发者提供了深入理解搜索引擎工作原理以及定制化搜索引擎的机会。接...
1. **Nutch介绍**:Nutch是一个基于Java的开源Web爬虫,它能够抓取互联网上的网页,并对抓取的数据进行索引和搜索。Nutch的设计目标是提供可扩展性和高效率,适合大规模的Web数据处理。 2. **增量索引**:在Nutch中...
Lucene 是一个全文搜索引擎库,而 Nutch 是一个完整的网络爬虫项目,两者结合可以提供从网页抓取到索引、搜索的一站式解决方案。 在开发自定义搜索引擎的过程中,首先我们需要了解 **Lucene** 的基本原理。Lucene ...
1. **Java编程基础**:由于Nutch是用Java编写的,因此理解和修改Nutch源代码需要扎实的Java基础知识。 2. **搜索引擎原理**:理解搜索引擎的基本工作流程,包括爬虫、预处理、索引和查询处理。 3. **Hadoop和...
2. **Nutch**: Nutch是一个开放源代码的网络爬虫,主要用于抓取和索引网页内容。它基于Lucene,提供了完整的爬虫解决方案,包括网页抓取、预处理(如HTML解析、链接分析、去重等)、索引和搜索功能。Nutch的主要优势...
Nutch 是一个开源的搜索引擎项目,它提供了网络爬虫、索引和搜索的功能。在构建一个自定义的搜索引擎时,可能会遇到几个常见的问题,如搜索结果的关键词高亮、快照链接无效以及网页在预览时的变形。下面将详细讨论...
《Lucene+Nutch搜索引擎开发》是一本专注于搜索引擎技术的书籍,配套光盘资源为学习者提供了丰富的实践材料,尤其对于想要深入理解Nutch搜索引擎开发的读者来说,这是一份不可多得的学习资料。Nutch是基于Apache ...
总之,Nutch是一个强大的开源搜索引擎工具,它不仅适用于构建自己的搜索引擎,也是研究和学习搜索引擎技术的理想平台。通过熟悉其工作流程、分析源代码以及查阅相关文档,你将能够掌握搜索引擎的核心技术和实现细节...
Apache Nutch 是一款高度可扩展的开源全文搜索引擎框架,它为构建自定义的网络爬虫和搜索引擎提供了强大的工具集。Nutch 的设计目标是处理大量网页数据,进行高效的抓取、索引和搜索操作。在“apache-nutch-1.4-src....
- **配置 Nutch 创建索引**:下载 Nutch 的源代码并解压,然后通过 Maven 进行编译。配置 Nutch 的 `conf/nutch-site.xml` 文件以设置存储路径、抓取策略等参数。 - **安装 Tomcat**:Tomcat 用于运行 Nutch 的 UI...
在使用Nutch之前,你需要配置Nutch的运行环境,包括安装Java、设置Hadoop(如果需要分布式爬取)、下载和编译Nutch源代码。还需要配置Nutch的`conf/nutch-site.xml`文件,指定抓取策略、存储路径、爬虫范围等参数。 ...