java nutch 爬虫_Java分布式爬虫Nutch教程——导入Nutch工程,执行完整爬取

这篇教程详细介绍了如何在Linux环境下配置和运行Java Nutch分布式爬虫。首先,你需要安装JDK 1.7、Apache Ant,并下载Nutch源码。接着,使用Intellij IDEA导入Nutch工程,修改ivysettings.xml以加速依赖下载。然后,通过ant命令转换项目并导入IDE。在运行过程中,需要手动配置Nutch的配置文件nutch-default.xml以指定插件路径。教程中还演示了如何处理运行错误,如URLNormalizer找不到的问题。最后,展示了如何执行Inject任务,读取SequenceFile,以及如何进行完整爬取的步骤。整个过程包括了注入种子URL、爬取、解析和索引等步骤。
摘要由CSDN通过智能技术生成

在使用本教程之前,需要满足条件:

1)有一台Linux或Linux虚拟机

2)安装JDK(推荐1.7)

3)安装Apache Ant

下载Nutch源码:

安装IDE:

推荐使用Intellij或者Netbeans,如果用eclipse也可以,不推荐。

Intellij官方下载地址:http://www.jetbrains.com/idea/download/

转换:

Nutch源码是用ant进行构建的,需要转换成eclipse工程才可以导入IDE正确使用,Intellij和Netbeans都可以支持ecilpse工程。

解压下载的apache-nutch-1.9-src.zip,得到文件夹apache-nutch-1.9。

在执行转换之前,我们先修改一下ivy中的一个源,将它改为开源中国的镜像,否则转换的过程会非常缓慢。(ant源码中并没有附带依赖jar包,ivy负责从网上自动下载jar包)。

修改apache-nutch-1.9文件夹中的ivy/ivysettings.xml:

de9f3ef95dffcd3f7e0eeac3a6c5f9f5.png

找到:

76cfca5e761cacd2067d540bef2a78eb.png

将value修改为http://maven.oschina.net/content/groups/public/ ,修改后:

2b590d0a2e686c8de3aae0fa80113901.png

保存并退出,保证当前目录为apache-nutch-1.9,执行命令:

ant eclipse-verbose

然后耐心等待,这个过程ant会根据ivy从中心仓库下载各种依赖jar包,可能要十几分钟。

1bfae0353d82fd87c6ce51ef0049b422.png

-verbose参数加上之后可以看到ant过程的详细信息。

10分钟左右,转换成功:

6fdc1d9f6c174212d846392eb2667110.png

打开Intellij, File -> Import Project ->选择apache-nutch-1.9文件夹,确定后选择Import project from external model(Eclipse)

08debcdfb68f5fac5419af397ecea6c2.png

一直点击next到结束。成功将项目导入Intellij:

9dfb17a62c006364265a38ba631f54d9.png

源码导入工程后,并不能执行完整的爬取。Nutch将爬取的流程切分成很多阶段,每个阶段分别封装在一个类的main函数中。在外面通过Linux Shell调用这些main函数,来完整爬取的流程。我们在后续教程中会对流程调度做一个详细的说明。

下面我们来运行Nutch中最简单的流程:Inject。我们知道爬虫在初始阶段,是需要人工给出一个或多个url,作为起始点(广度遍历树的树根)。Inject的作用,就是把用户写在文件里的种子(一行一个url,是TextInputFormat),插入到爬虫的URL管理文件(crawldb,是SequenceFile)中。

从src文件夹中找到org.apache.nutch.crawl.Injector类:

8160e4b099102be9135c9f506f0d6622.png

在阅读Nutch源码的过程中,最重要的就是找到每个类的main函数:

344a095a95815faff932e2781b78bbed.png

可以看到,main函数其实是利用ToolRunner,执行了run(String[] args)。这里ToolRunner.run会从第二个参数(new Injector())这个对象中,找到run(String[] args)这个方法执行。

从run方法中可以看出来,String[] args需要有2个参数,第一个参数表示爬虫的URL管理文件夹(输出),第二个参数表示种子文件夹(输入)。对hadoop中的map reduce程序来说,输入文件夹是必须存在的,输出文件夹应该不存在。我们创建一个文件夹 /tmp/urls,来存放种子文件(作为输入)。

ded0ca6d8a50d217f55ba59df07313dc.png

在seed.txt中加入一个种子URL

http://www.cnbeta.com/

指定一个文件夹/tmp/crawldb来作为URL管理文件夹(输出)

有一种简单的方法来指定args,直接在main函数下加一行:

bcfb3da562c54eacf73064f7fe642a12.png

args=newString[]{"/tmp/crawldb","/tmp/urls"};

运行这个类,我们会发现报错了(下面只给了错误的一部分):

Causedby:java.lang.RuntimeException:x point org.apache.nutch.net.URLNormalizernotfound.at org.apache.nutch.net.URLNormalizers.(URLNormalizers.java:123)at org.apache.nutch.crawl.Injector$InjectMapper.configure(Injector.java:84)...23more

这是因为用这种方式执行,按照Nutch默认的配置,不能正确地加载插件。我们需要修改Nutch的配置文件,为插件文件夹指定一个绝对路径,修改conf/nutch-default.xml文件,找到:

plugin.folderspluginsDirectories where nutch plugins are located. Each

element may be a relative or absolute path. If absolute, it is used

as is. If relative, it is searched for on the classpath.

将value修改为绝对路径 apache-nutch-1.9所在文件夹+”/src/plugin”,比如我的配置:

plugin.folders/home/hu/apache/apache-nutch-1.9/src/pluginDirectories where nutch plugins are located. Each

element may be a relative or absolute path. If absolute, it is used

as is. If relative, it is searched for on the classpath.

建议在修改nutch-default.xml时,将原来的配置注释,并复制一份新的修改,方便还原:

现在再运行Injector.java,看到结果:

240dd9029123fc8fe46955a2e9e245ba.png

运行成功。

读取爬虫文件:

我们查看程序的输出 tree /tmp/crawldb ,如果没有tree命令,就直接用资源管理器之类的查看吧:

3457afbed373773a2a94122eba3dddea.png

查看里面的data文件:

vim/tmp/crawldb/current/part-00000/data

71441aefc0bfb83f50c87d08f0106258.png

这是一个SequenceFile,Nutch中除了Inject的输入(种子)之外,其他文件 全部以SequenceFile的形式存储。SequenceFile的结构如下:

key0 value0

key1 value1

key2 value2......keyn valuen

以key value的形式,将对象序列(key value序列)存储到文件中。我们从SequenceFile头部可以看出来key value的类型。

上面的SequenceFile中,可以看出来,key的类型是org.apache.hadoop.io.Text,value的类型是org.apache.nutch.crawl.CrawlDatum。

下面教程给出如何读取SequenceFile的代码。

新建一个类org.apache.nutch.example.InjectorReader

packageorg.apache.nutch.example;importorg.apache.hadoop.conf.Configuration;importorg.apache.hadoop.fs.FileSystem;importorg.apache.hadoop.fs.Path;importorg.apache.hadoop.io.SequenceFile;importorg.apache.hadoop.io.Text;importorg.apache.nutch.crawl.CrawlDatum;importjava.io.IOException;/**

* Created by hu on 15-2-9.

*/publicclassInjectorReader{publicstaticvoidmain(String[]args)throwsIOException{Configurationconf=newConfiguration();PathdataPath=newPath("/tmp/crawldb/current/part-00000/data");FileSystemfs=dataPath.getFileSystem(conf);SequenceFile.Readerreader=newSequenceFile.Reader(fs,dataPath,conf);Textkey=newText();CrawlDatumvalue=newCrawlDatum();while(reader.next(key,value)){System.out.println("key:"+key);System.out.println("value:"+value);}reader.close();}}

运行结果:

key:http://www.cnbeta.com/value:Version:7Status:1(db_unfetched)Fetchtime:MonFeb0913:20:36CST2015Modifiedtime:ThuJan0108:00:00CST1970Retriessince fetch:0Retryinterval:2592000seconds(30days)Score:1.0Signature:nullMetadata:_maxdepth_=1000_depth_=1

我们可以看到,程序读出了刚才Inject到crawldb的url,key是url,value是一个CrawlDatum对象,这个对象用来维护爬虫的URL管理信息,我们可以看到一行:

Status:1(db_unfetched)

表示当前url为未爬取状态,在后续流程中,爬虫会从crawldb取未爬取的url进行爬取。

完整爬取:

下面给出的是各位最期待的代码,就是如何用Nutch完成一次完整的爬取。官方代码在1.7之前(包括1.7),包含一个Crawl.java,这个代码的main函数可以执行一次完整的爬取,但是从1.7之后就取消了。只保留了使用Linux Shell来调用每个流程,来完成爬取的方法。但是好在取消的Crawl.java修改一下,还是可以使用的。

在爬取之前,我们先修改一下conf/nutch-default.xml中的一个地方,找到:

http.agent.nameHTTP 'User-Agent' request header. MUST NOT be empty -

please set this to a single word uniquely related to your organization.

NOTE: You should also check other related properties:

http.robots.agents

http.agent.description

http.agent.url

http.agent.email

http.agent.version

and set their values appropriately.

在中随意添加一个值,修改为:

http.agent.nametestHTTP 'User-Agent' request header. MUST NOT be empty -

please set this to a single word uniquely related to your organization.

NOTE: You should also check other related properties:

http.robots.agents

http.agent.description

http.agent.url

http.agent.email

http.agent.version

and set their values appropriately.

这个值会在发送http请求时,作为User-Agent字段。

下面给出代码:

packageorg.apache.nutch.crawl;importjava.util.*;importjava.text.*;// Commons Logging importsimportorg.apache.commons.lang.StringUtils;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.apache.hadoop.fs.*;importorg.apache.hadoop.conf.*;importorg.apache.hadoop.mapred.*;importorg.apache.hadoop.util.Tool;importorg.apache.hadoop.util.ToolRunner;importorg.apache.nutch.parse.ParseSegment;importorg.apache.nutch.indexer.IndexingJob;//import org.apache.nutch.indexer.solr.SolrDeleteDuplicates;importorg.apache.nutch.util.HadoopFSUtil;importorg.apache.nutch.util.NutchConfiguration;importorg.apache.nutch.util.NutchJob;importorg.apache.nutch.fetcher.Fetcher;publicclassCrawlextendsConfiguredimplementsTool{publicstaticfinalLoggerLOG=LoggerFactory.getLogger(Crawl.class);privatestaticStringgetDate(){returnnewSimpleDateFormat("yyyyMMddHHmmss").format(newDate(System.currentTimeMillis()));}/* Perform complete crawling and indexing (to Solr) given a set of root urls and the -solr

parameter respectively. More information and Usage parameters can be found below. */publicstaticvoidmain(Stringargs[])throwsException{Configurationconf=NutchConfiguration.create();intres=ToolRunner.run(conf,newCrawl(),args);System.exit(res);}@Overridepublicintrun(String[]args)throwsException{/*种子所在文件夹*/PathrootUrlDir=newPath("/tmp/urls");/*存储爬取信息的文件夹*/Pathdir=newPath("/tmp","crawl-"+getDate());intthreads=50;/*广度遍历时爬取的深度,即广度遍历树的层数*/intdepth=2;longtopN=10;JobConfjob=newNutchJob(getConf());FileSystemfs=FileSystem.get(job);if(LOG.isInfoEnabled()){LOG.info("crawl started in: "+dir);LOG.info("rootUrlDir = "+rootUrlDir);LOG.info("threads = "+threads);LOG.info("depth = "+depth);if(topN!=Long.MAX_VALUE)LOG.info("topN = "+topN);}PathcrawlDb=newPath(dir+"/crawldb");PathlinkDb=newPath(dir+"/linkdb");Pathsegments=newPath(dir+"/segments");Pathindexes=newPath(dir+"/indexes");Pathindex=newPath(dir+"/index");PathtmpDir=job.getLocalPath("crawl"+Path.SEPARATOR+getDate());Injectorinjector=newInjector(getConf());Generatorgenerator=newGenerator(getConf());Fetcherfetcher=newFetcher(getConf());ParseSegmentparseSegment=newParseSegment(getConf());CrawlDbcrawlDbTool=newCrawlDb(getConf());LinkDblinkDbTool=newLinkDb(getConf());// initialize crawlDbinjector.inject(crawlDb,rootUrlDir);inti;for(i=0;i

if (i > 0) {

linkDbTool.invert(linkDb, segments, true, true, false); // invert links

if (solrUrl != null) {

// index, dedup & merge

FileStatus[] fstats = fs.listStatus(segments, HadoopFSUtil.getPassDirectoriesFilter(fs));

IndexingJob indexer = new IndexingJob(getConf());

indexer.index(crawlDb, linkDb,

Arrays.asList(HadoopFSUtil.getPaths(fstats)));

SolrDeleteDuplicates dedup = new SolrDeleteDuplicates();

dedup.setConf(getConf());

dedup.dedup(solrUrl);

}

} else {

LOG.warn("No URLs to fetch - check your seed list and URL filters.");

}

*/if(LOG.isInfoEnabled()){LOG.info("crawl finished: "+dir);}return0;}}

运行成功,对网站进行了一个2层的爬取,爬取信息都保存在/tmp/crawl+时间的文件夹中。

2015-02-0914:23:17,171INFO crawl.CrawlDb(CrawlDb.java:update(115))-CrawlDbupdate:finished at2015-02-0914:23:17,elapsed:00:00:012015-02-0914:23:17,171INFO crawl.Crawl(Crawl.java:run(117))-crawl finished:/tmp/crawl-20150209142212

有些时候爬虫爬一层就停止了,有几种原因:

1)种子对应的页面大小超过配置的上限,页面被忽略。

2)nutch默认遵循robots协议,有可能robots协议禁止了爬取,不过出现这种情况日志会给出相关信息。

3)网页没有被正确爬取(这种情况少)。

爬很多门户网站时容易出现第一种情况,这种情况只需要找到conf/nutch-default.xml中的:

http.content.limit65536The length limit for downloaded content using the http://

protocol, in bytes. If this value is nonnegative (>=0), content longer

than it will be truncated; otherwise, no truncation at all. Do not

confuse this setting with the file.content.limit setting.

将value设置为-1即可

http.content.limit-1The length limit for downloaded content using the http://

protocol, in bytes. If this value is nonnegative (>=0), content longer

than it will be truncated; otherwise, no truncation at all. Do not

confuse this setting with the file.content.limit setting.

如果看到日志中有说被robots协议阻拦,修改Fetcher.java的源码,找到:

if(!rules.isAllowed(fit.u.toString())){// unblockfetchQueues.finishFetchItem(fit,true);if(LOG.isDebugEnabled()){LOG.debug("Denied by robots.txt: "+fit.url);}output(fit.url,fit.datum,null,ProtocolStatus.STATUS_ROBOTS_DENIED,CrawlDatum.STATUS_FETCH_GONE);reporter.incrCounter("FetcherStatus","robots_denied",1);continue;}

将整段代码注释即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值