/**
* Copyright 2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
//本次分析代码用到许多网上的免费资源,共享资源,在此对共享资源的同仁表示由衷感谢,
//这种无私的付出值得每一个人为之钦佩
//如果用到这些资源,一定注明出处,但即使在小心谨慎,难免会漏掉一些,
//或者本身用到的资源又是别人的转载,可能会无法注明原作者,希望能够理解,在此一并对致谢
//
/*
*用到的网络链接,论文,博客文章在此一一注明
*
*http://hi.baidu.com/injava/blog/ NUTCH研究系列 ( Dissecting The Nutch Crawler )
*
*博客园 有石九曲回肠有石孤立 Nutch配置笔记
*[ 任培成的文档收藏夹 ] Nutch 的配置文件
*《搜索引擎索引压缩技术》 张俊林
*Map Reduce - the Free Lunch is not over? Posted in Tech at 10:29 am by Meng Yan
*基于Java的搜索引擎Nutch中文搜索技术研究宿红毅 罗 宏 臧海峰(北京理工大学 计算机科学与工程系,北京 100081)
* Research on Chinese Search Technology for Java-based Search Engine --- Nutch Su Hong-yi Luo Hong Zang Hai-feng
*(Dept. of Computer Science and Engineer, Beijing Institute of Technology, *Beijing 100081)
*
*/
/*
*CrawlTool类的作用很好理解,它就是将爬行整个网络的一系列命令操作综合在一起并加以限制条件实现了局域性网络的爬行。
*它主要包括了两个静态函数和一个main函数。
*/
package org.apache.nutch.crawl;
import java.util.*;
import java.text.*;
// Commons Logging imports
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.nutch.fetcher.Fetcher;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.mapred.*;
import org.apache.nutch.parse.ParseSegment;
import org.apache.nutch.indexer.DeleteDuplicates;
import org.apache.nutch.indexer.IndexMerger;
import org.apache.nutch.indexer.Indexer;
import org.apache.nutch.util.NutchConfiguration;
import org.apache.nutch.util.NutchJob;
public class Crawl {
public static final Log LOG = LogFactory.getLog(Crawl.class);
private static String getDate() {
return new SimpleDateFormat("yyyyMMddHHmmss").format
(new Date(System.currentTimeMillis()));
}
/*
*输入爬行命令后,调用Crwal类,完成爬虫的工作。
*/
/* Perform complete crawling and indexing given a set of root urls. */
public static void main(String args[]) throws Exception {
/*
*如果输入的命令长度,不够打印提示。
*/
if (args.length < 1) {
System.out.println
("Usage: Crawl <urlDir> [-dir d] [-threads n] [-depth i] [-topN N]");
return;
}
/*
*Configuration类不是很清楚,
*调用它的Create函数
*/
Configuration conf = NutchConfiguration.create();
/*
*调用addDefaultResource方法,参数为crawl-tool.xml
*crawl-tool.xml介绍:
*根目录nutch-0.7.2.war解压,nutch-0.8.1\WEB-INF\classes下面,
*用IE打开后是一个表格,共一行三列[ name value description ]三项
*crawl-tool.xml代码如下:
*<?xml version="1.0"?>
*<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
* (configuration.xsl文件位于本目录内,是一个样式文件)
*<!-- Put site-specific property overrides in this file. -->
*<configuration>
* </configuration>
*在此目录下可以看到一个配置文件:nutch-default.xml
*同样,可以看到该文件也有三列( name value description )
*应该说这是对Nutch配置的一个解释
*这个文件非常重要,曾经在网上搜到很多的关于这个文件的解释
*下面是从网上查到的一点资料
**Nutch是支持插件扩展的,这样就可以满足各个不同使用群体的特定需求,
**例如是要做垂直搜索,并收集特定信息的收集,那么我们可以在其nutch-default.xml文件中找到如下一段
**。。。。。。 (plugin.folders)
**。。。。。。(plugin.includes)
***这两个是配置 插件功能 的配置项 ***plugin.folders制定插件加载路径,plugin.includes表示需要加载的插件列表
**并将plugin.includes拷贝到nutch-site.xml文件中,在plugin.includes的value中加入自己编写的插件即可
*对于这个文件的中文解释,[ 任培成的文档收藏夹 ] 非常详细
*
*
*/
conf.addDefaultResource("crawl-tool.xml");
/*
*
*对于JobConf类不是很了解,但可以看出这儿新建了一个对象并调用Create方法,
*通过在后面的分析,可以知道,job是很要的,这个类需要认真地看看
*可以猜想这个累的定义是在import org.apache.nutch.util.NutchJob;
*用conf作参数,这里是取得爬虫的配置文件
*/
JobConf job = new NutchJob(conf);
/*
*这儿的功能应该是新建了一个根url
*/
Path rootUrlDir = null;
/*创建另外一个Path对象,通过其调用构造函数时候的参数,
*可以猜想应该可通过在Cygwin运行时观察其结果
*/
/*
*以下四句话,
*作用应该是如果用户没有输入这四个中的值,在这里为添加默认
*dir depth -threads topN
*可以看出,这四个数据就是使用Crawl命令时候用到的参数
*/
Path dir = new Path("crawl-" + getDate()); //Path没猜错的话,是新建生成的文件夹
int threads = job.getInt("fetcher.threads.fetch", 10); //Threads同时运行的线程数
int depth = 5; //depth爬行深度
int topN = Integer.MAX_VALUE; //
/*
*这里是
*用户输入以上的四个数据的话,使用用户的输入!
*
*/
/*
*bin/nutch crawl urls -dir crawled -depth 5 -threads 10 -topN 30 >& logs.log
*/
for (int i = 0; i < args.length; i++) {
if ("-dir".equals(args[i])) { //如果用户输入了-dir,新建一个文件夹,对应上面的crawled
dir = new Path(args[i+1]);
i++;
} else if ("-threads".equals(args[i])) { //如果用户输入了-threads,将其写入threads,对应5
threads = Integer.parseInt(args[i+1]);
i++;
} else if ("-depth".equals(args[i])) { //如果用户输入了-depth, 将其写入depth,对应10
depth = Integer.parseInt(args[i+1]);
i++;
} else if ("-topN".equals(args[i])) { //如果用户输入了topN,写入topN 对应30
topN = Integer.parseInt(args[i+1]);
i++;
} else if (args[i] != null) { //此处应该是获得用户所要用到的根节点,
rootUrlDir = new Path(args[i]); //也就是爬行起始的地方,
} //rootUrlDir用来获得该爬行起始点所在的目录,对应上面的urls
}
//获得文件系统
FileSystem fs = FileSystem.get(job);
//fs应该是获得抓取的页面的文件夹,因为从下面可以看出如果存在同名的文件,将会调用异常处理
if (fs.exists(dir)) {
throw new RuntimeException(dir + " already exists.");
}
/*
*如果需要生成日志文件,加入相应的开始的信息
*/
if (LOG.isInfoEnabled()) {
LOG.info("crawl started in: " + dir);
LOG.info("rootUrlDir = " + rootUrlDir);
LOG.info("threads = " + threads);
LOG.info("depth = " + depth);
if (topN != Integer.MAX_VALUE)
LOG.info("topN = " + topN);
}
/*
*新建四个文件夹
*/
Path crawlDb = new Path(dir + "/crawldb");
Path linkDb = new Path(dir + "/linkdb");
Path segments = new Path(dir + "/segments");
Path indexes = new Path(dir + "/indexes");
Path index = new Path(dir + "/index");
/*
*看不清楚这句的功能
*/
Path tmpDir = job.getLocalPath("crawl"+Path.SEPARATOR+getDate());
/*
*初始化crawlDb文件夹,构建一个插入器
*/
new Injector(job).inject(crawlDb, rootUrlDir);
/*
*可以看出每一层爬取会生成Segment里面的一个新的文件夹
*Segment代表一个网页集合,这个集合中的网页被作为一个小的单元统一地进行抓取和索引。
*它里面存储的数据主要有三个类型:
*a "fetchlist": 将要被抓取的网页的名称列表
*the "fetcher output": 被抓取回来的网页的文件集合
*the "index":利用lucene为 the fetcher output 建立的索引
*
*/
for (int i = 0; i < depth; i++) { // generate new segment
Path segment = //新建segment里面的一个文件夹
new Generator(job).generate(crawlDb, segments, -1,
topN, System.currentTimeMillis()); //
new Fetcher(job).fetch(segment, threads); // fetch it //抓取
if (!Fetcher.isParsing(job)) {
new ParseSegment(job).parse(segment); // parse it, if needed //如果没有剖析过,剖析该Segment
}
new CrawlDb(job).update(crawlDb, segment); // update crawldb //更新crawl,应该是加入新剖析的Segment
}
new LinkDb(job).invert(linkDb, segments); // invert links //将Segments中的信息加入到linkDb中
// index, dedup & merge
/*
*虽然下面具体如何实现暂时无法分析出来,但是可以知道完成的功能
*是将爬取到的网页进行处理,除掉那些重复的索引,合并相关的索引
*/
new Indexer(job).index(indexes, crawlDb, linkDb, fs.listPaths(segments));
new DeleteDuplicates(job).dedup(new Path[] { indexes });
new IndexMerger(fs, fs.listPaths(indexes), index, tmpDir, job).merge();
//如果需要写日志文件,将信息加入到日志文件中
if (LOG.isInfoEnabled()) { LOG.info("crawl finished: " + dir); }
}
}