Nutch1.0 crawl分析(转)

源自:http://jlife.javaeye.com/blog/478315

关键字: nutch-1.0中,org.apache.nutch.crawl.crawl类中提供了一个入口主函数main,通过接收

 
================================================================
 
Crawler
Searcher两部分被尽是分开,其主要目的是为了使两个部分可以布地配置在硬件平台上,例如CrawlerSearcher分别被放置在两个主机上,这样可以极大的提高灵活性和性能。 
一、总体介绍: 

1
、先注入种子urlscrawldb 
2
、循环: 

    * generate
crawldb中生成一个url的子集用于抓取 
    * fetch
抓取上一小的url生成一个个segment 
    * parse
分析已抓取segment的内容 
    * update
把已抓取的数据更新到原先的crawldb 

3
、从已抓取的segments中分析出link地图 
4
、索引segment文本及inlink锚文本 
二、相关的数据结构: 
Crawl DB
 
● CrawlDb
是一个包含如下结构数据的文件: 
<URL, CrawlDatum>
 
● CrawlDatum:
 
<status, date, interval, failures, linkCount, ...>
 
● Status:
 
{db_unfetched, db_fetched, db_gone,linked,
 
fetch_success, fetch_fail, fetch_gone}
 

  爬虫Crawler 
Crawler
的工作流程包括了整个nutch的所有步骤--injector,generator,fetcher,parseSegment, updateCrawleDB,Invert links, Index ,DeleteDuplicates, IndexMerger 
Crawler
涉及的数据文件和格式和含义,和以上的各个步骤相关的文件分别被存放在物理设备上的以下几个文件夹里,crawldb,segments,indexes,linkdb,index五个文件夹里。 
那么各个步骤和流程是怎么,各个文件夹里又是放着什么呢? 
观察Crawler类可以知道它的流程 
./nutch crawl urls -dir ~/crawl -depth 4 -threads 10 -topN 2000
 

1
Injector injector = new Injector(conf); 
Usage: Injector <crawldb> <url_dir>
 
首先是建立起始url集,每个url都经过URLNormalizersfilterscoreFilter三个过程并标记状态。首先经过normalizer plugin,url进行标准化,比如basic nomalizer的作用有把大写的url标准化为小写,把空格去除等等。然后再经过的plugin filter,可以根据你写的正则表达式把想要的url留下来。经过两个步骤后,然后就是把这个url进行状态标记,每个url都对应着一个 CrawlDatum,这个类对应着每个url在所有生命周期内的一切状态。细节上还有这个url处理的时间和初始时的分值。 
  同时,在这个步骤里,会在文件系统里生成 如下文件 crawlDB/current/part-00000 
  这个文件夹里还有.data.crc , .index.crc, data, index四个文件 

● MapReduce1:
把输入的文件转换成DB格式 
In:
包含urls的文本文件 
Map(line) → <url, CrawlDatum>; status=db_unfetched
 
Reduce() is identity;
 
Output:
临时的输出文件夹 
● MapReduce2:
合并到现有的DB 
Input:
第一步的输出和已存在的DB文件 
Map() is identity.
 
Reduce:
合并CrawlDatum成一个实体(entry) 
Out:
一个新的DB 
   

2
Generator generator = new Generator(conf); //Generates a subset of a crawl db to fetch 

Usage: Generator <crawldb> <segments_dir> [-force] [-topN N] [-numFetchers numFetchers] [-adddays numDays] [-noFilter]
 
在这个步骤里,Generator一共做了四件事情, 
1
、给前面injector完成的输出结果里按分值选出前topNurl,作为一个fetch的子集。 
2
、根据第一步的结果检查是否已经选取出一些url,CrawlDatum的实体集。 
3
、再次转化,此次要以urlhost来分组,并以urlhash来排序。 
4
、根据以上的步骤的结果来更新crawldb(injector产生) 

● MapReduce1:
根据要求选取一些要抓取的url 
In: Crawl DB
文件 
Map() → if date≥now, invert to <CrawlDatum, url>
 
Partition
以随机的hash值来分组 
Reduce:
 
compare()
CrawlDatum.linkCount的降序排列 
output only top-N most-linked entries
 
● MapReduce2:
为下一步抓取准备 
Map() is invert; Partition() by host, Reduce() is identity.
 
Out:
包含<url,CrawlDatum> 要并行抓取的文件 



  3Fetcher fetcher = new Fetcher(conf); //The fetcher. Most of the work is done by plugins 
Usage: Fetcher <segment> [-threads n] [-noParsing]
 
这个步骤里,Fetcher所做的事情主要就是抓取了,同时也完成一些其它的工作。首先,这是一个多线程的步骤,默认以10个线程去抓取。根据抓取回来后的结果状态来进行不同的标记,存储,再处理等等行为。输入是上一步骤Generator产生的segment文件夹,这个步骤里,考虑到先前已经按照ip hostpatition了,所以在此就不再把input文件进行分割了。程序继承了SequenceFileInputFormat重写了 inputFormat来达到这点。这个类的各种形为都是插件来具体完成的,它只是一个骨架一样为各种插件提供一个平台。它先根据url来取出具体的 protocol,得到protocolOutput,进而得到状态status及内容content。然后,根据抓取的状态status来继续再处理。再处理时,首先会将这次抓取的内容content、状态status及它的状态标记进行存储。这个存储的过程中,还会记下抓取的时间,再把segment 存过metadata,同时在分析parsing前经过scoreFilter,再用parseUtil(一系列的parse插件)进行分析,分析后再经过一次score插件的处理。经过这一系列处理后,最后进行输出(url,fetcherOutput)。 
之前讲到根据抓取回来的各种状态,进行再处理,这些状态一共包括12种,比如当抓取成功时,会像上刚讲的那样先存储结果,再判断是否是链接跳转,跳转的次数等等处理。 

● MapReduce:
抓取 
In: <url,CrawlDatum>,
host分区, hash值排序 
Map(url,CrawlDatum) → <url, FetcherOutput>
 
多线程的, 同步的map实现 
调用已有的协议protocol插件 
FetcherOutput: <CrawlDatum, Content>
 
Reduce is identity
 
Out:
两个文件: <url,CrawlDatum>, <url,Content> 


4
ParseSegment parseSegment = new ParseSegment(conf); //Parse content in a segment 
Usage: ParseSegment segment
 
对于这个步骤的逻辑比较简单,只是对抓取后上一步骤存储在segment里的content进行分析parse。同样,这个步骤的具体工作也是由插件来完成的。 

MapReduce:
分析内容 
In: <url, Content>
抓取来的内容 
Map(url, Content) → <url, Parse>
 
调用分析插件parser plugins 
Reduce is identity.
 
Parse: <ParseText, ParseData>
 
Out:
分割成三个文件: <url,ParseText>, <url,ParseData> <url,CrawlDatum> 为了outlinks. 


5
CrawlDb crawlDbTool = new CrawlDb(conf); //takes the output of the fetcher and updates the crawldb accordingly. 
Usage: CrawlDb <crawldb> (-dir <segments> | <seg1> <seg2> ...) [-force] [-normalize] [-filter] [-noAdditions]
 
这个类主要是根据fetcher的输出去更新crawldb mapreduce分别做了两方面的事情,在map里是对urlnomalizer,filte,reduce里是对新抓取进来的页面(CrawlDatum)和原先已经存在的进行合并。 

MapReduce:
合并抓取的和分析后的输出到crawldb 
In: <url,CrawlDatum>
现有的db加上抓取后的和分析后的输出 
Map() is identity
 
Reduce()
合并所有实体(entry)成一个,以抓取后的状态覆盖原先的db状态信息,统计出分析后的链接数 
Out:
新的crawl db 

6.LinkDb linkDbTool = new LinkDb(conf); //Maintains an inverted link map, listing incoming links for each url.
 
Usage: LinkDb <linkdb> (-dir <segmentsDir> | <seg1> <seg2> ...) [-force] [-noNormalize] [-noFilter]
 
这个类的作用是管理新转化进来的链接映射,并列出每个url的外部链接(incoming links)。先是对每一个url取出它的outLinks,作map操作把这个url作为每个outLinksincoming link,在reduce里把根据每个key来把一个url的所有incoming link都加到inlinks里。这样就把每个url的外部链接统计出来了。然后一步是对这些新加进来的链接进行合并。 

● MapReduce:
统计每个链接的外部链接 
In: <url,ParseData>,
包含所有链接的分析后的结果 
Map(srcUrl, ParseData> → <destUrl, Inlinks>
 
为每个链出链接收集一个入链。 
Inlinks: <srcUrl, anchorText>*
 
Reduce()
加上外部入链数量 
Out: <url, Inlinks>,
一个相关完整的链接地图 


7.Indexer indexer = new Indexer(conf); //Create indexes for segments
 
Usage: <index> <crawldb> <linkdb> <segment> ...
 
  这个类的任务是另一方面的工作了,它是基于hadooplucene的分布式索引。它就是为前面爬虫抓取回来的数据进行索引好让用户可以搜索到这些数据。这里的输入就比较多了,有segments下的fetch_dir,parseDataparseText,还有crawldb下的 current_dirlinkdb下的current_dir。在这个类里,map也不做,在reduce时处理。当然要把这些数据体组合成一个 lucenedocument让它索引了。在reduce里组装好后收集时是<url,doc>,最后在输出的OutputFormat类里进行真正的索引。 

● MapReduce:
生成lucene的索引文件 
In:
外个文件, values <Class, Object>包装 
<url, ParseData> from parse,
title, metadata, 等等信息. 
<url, ParseText> from parse,
文本 text 
<url, Inlinks> from invert,
锚文本anchors 
<url, CrawlDatum> from fetch,
用于抓取 
Map() is identity
 
Reduce()
生成Lucene Document 
调用index插件 
Out:
建立Lucene 索引; 最后存储到文件系统上 


8. DeleteDuplicates dedup = new DeleteDuplicates(conf); //
这个类的作用就是它的名字了。 
Usage: DeleteDuplicates <indexes> ...
 
  这个类的作用就是这它的名字所写的意思--去重。前面索引后(当然不是一次时的情况)会有重复,所以要去重。为什么呢,在一次索引时是不重复的,可是多次抓取后就会有重复了。就是这个原因才要去重。当然去重的规则有两种一个是以时间为标准,一种是以内容的md5值为标准。 
9.IndexMerger merger = new IndexMerger(conf);
 
IndexMerger [-workingdir <workingdir>] outputIndex indexesDir...
 
  这个类就比较简单了,把所有的小索引合并成一个索引。在这一步没有用到map-reduce 

  在这九大步骤中generator,fetcher,parseSegment,crawlDbTool会根据抓取的层数循环运行,当抓取的层数大于1时会运行linkInvert,index,dedup,merge 
=================================================================
 
crawdb,linkdb
 

web link目录,存放url url的互联关系,作为爬行与重新爬行的依据,页面默认30天过期。 

segments
 

是主目录,存放抓回来的网页。页面内容有bytes[]raw content parsed text的形式。nutch以广度优先的原则来爬行,因此每爬完一轮会生成一个segment目录。 

index
 

lucene的索引目录,是indexes目录里所有index合并后的完整索引,注意索引文件只对页面内容进行索引,没有进行存储,因此查询时要去访问segments目录才能获得页面内容。 

果你研究过Lucene,相信indexindexes目录中的文件会非常熟悉的,他们是索引文件,使用不同扩展名的文件来存储不同的内容,比 如,.nrm文件是存储标准化因子信息的,.fnm文件是存储文件名字信息的,.prx文件是存储Term的词频信息的,等等。 

=================================================================
 

Nutch
整体执行流程: 

抓取程序自动在用户根目录(/user/nutch)下面建立了crawl10目录,可以看到crawldb,segments,index,indexs,linkdb目录, 
1)crawldb
目录下面存放下载的URL,以及下载的日期,用来页面更新检查时间。 
2)linkdb
目录存放URL的关联关系,是下载完成后分析时创建的,通过这个关联关系可以实现类似googlepagerank功能。 
3)segments
目录存储抓取的页面,下面子目录的个数与获取页面的层数有关系,我指定-depth10层,这个目录下就有10层。 
 
里面有6个子目录 
  content,
下载页面的内容 
  crawl_fetch,
下载URL的状态内容 
  crawl_generate,
待下载的URL的集合,在generate任务生成时和下载过程中持续分析出来 
  crawl_parse,
存放用来更新crawldb的外部链接库 
  parse_data,
存放每个URL解析出来的外部链接和元数据 
  parse_text,
存放每个解析过的URL的文本内容 
4)index
目录存放符合lucene格式的索引目录,是indexs里所有的索引内容合并后的完整内容,看了一下这里的索引文件和用lucenedemo做出来的文件名称都不一样,待进一步研究 
5)indexs
目录存放每次下载的索引目录,存放part-0000part-0003 
====================================================================
 
抓取程序工作流程 

(1.)
建立初始URL 

(2.)
URL集注入crawldb数据库---inject 

这一步骤,上面的图中没有涉及到。既然需要维护一个crawlDB,那么在任何时候只要与抓取任务有关的而且是有用的信息都会被写入crawlDB 

(3.)
根据crawldb数据库创建抓取列表---generate 

(4.)
执行抓取,获取网页信息---fetch 

(5.)
更新数据库,把获取到的页面信息存入数据库中---updatedb 

(6.)
重复进行35的步骤,直到预先设定的抓取深度。---这个循环过程被称为产生/抓取/更新循环 

(7.)
根据sengments的内容更新linkdb数据库---invertlinks 

(8.)
建立索引---index 

搜索程序工作流程 

(1.)
用户通过用户接口进行查询操作 

(2.)
将用户查询转化为lucene查询 

(3.)
从索引库中提取满足用户检索需求的结果集 

(4.)
返回结 
===================================================================
 

Java代码 

1.                  /* Perform complete crawling and indexing given a set of root urls. */  

2.                    public static void main(String args[]) throws Exception {  

3.                     LOG.info("Thank you for using the turbosearch,It was based on Nutch,Thanks for the open source");  

4.                     LOG.info("                                                                               gavin");  

5.                      if (args.length < 1) {  

6.                        System.out.println  

7.                          ("Usage: Crawl <urlDir> [-dir d] [-threads n] [-depth i] [-topN N]");  

8.                        return;  

9.                      }  

10.                  

11.                    Configuration conf = NutchConfiguration.create();  

12.                    conf.addResource("crawl-tool.xml");  

13.                    JobConf job = new NutchJob(conf);  

14.                  

15.                    Path rootUrlDir = null;  

16.                    Path dir = new Path("crawl-" + getDate());  

17.                    int threads = job.getInt("fetcher.threads.fetch"10);  

18.                    int depth = 5;  

19.                    long topN = Long.MAX_VALUE;  

20.                    for (int i = 0; i < args.length; i++) {  

21.                      if ("-dir".equals(args[i])) {  

22.                        dir = new Path(args[i+1]);  

23.                        i++;  

24.                      } else if ("-threads".equals(args[i])) {  

25.                        threads = Integer.parseInt(args[i+1]);  

26.                        i++;  

27.                      } else if ("-depth".equals(args[i])) {  

28.                        depth = Integer.parseInt(args[i+1]);  

29.                        i++;  

30.                      } else if ("-topN".equals(args[i])) {  

31.                          topN = Integer.parseInt(args[i+1]);  

32.                          i++;  

33.                      } else if (args[i] != null) {  

34.                        rootUrlDir = new Path(args[i]);  

35.                      }  

36.                    }  

37.                  

38.                    FileSystem fs = FileSystem.get(job);  

39.                  

40.                    if (LOG.isInfoEnabled()) {  

41.                      LOG.info("crawl started in: " + dir);  

42.                      LOG.info("rootUrlDir = " + rootUrlDir);  

43.                      LOG.info("threads = " + threads);  

44.                      LOG.info("depth = " + depth);  

45.                      if (topN != Long.MAX_VALUE)  

46.                        LOG.info("topN = " + topN);  

47.                    }  

48.                      

49.                    Path crawlDb = new Path(dir + "/crawldb");  

50.                    Path linkDb = new Path(dir + "/linkdb");  

51.                    Path segments = new Path(dir + "/segments");  

52.                    Path indexes = new Path(dir + "/indexes");  

53.                    Path index = new Path(dir + "/index");  

54.                  

55.                    Path tmpDir = job.getLocalPath("crawl"+Path.SEPARATOR+getDate());  

56.                      

57.                    Injector injector = new Injector(conf);  

58.                    Generator generator = new Generator(conf);  

59.                    Fetcher fetcher = new Fetcher(conf);  

60.                    ParseSegment parseSegment = new ParseSegment(conf);  

61.                    CrawlDb crawlDbTool = new CrawlDb(conf);  

62.                    LinkDb linkDbTool = new LinkDb(conf);  

63.                    Indexer indexer = new Indexer(conf);  

64.                    DeleteDuplicates dedup = new DeleteDuplicates(conf);  

65.                    IndexMerger merger = new IndexMerger(conf);  

66.                        

67.                    // initialize crawlDb  

68.                    injector.inject(crawlDb, rootUrlDir);  

69.                    int i;  

70.                    for (i = 0; i < depth; i++) {             // generate new segment  

71.                      Path segment = generator.generate(crawlDb, segments, -1, topN, System  

72.                          .currentTimeMillis());  

73.                      if (segment == null) {  

74.                        LOG.info("Stopping at depth=" + i + " - no more URLs to fetch.");  

75.                        break;  

76.                      }  

77.                      fetcher.fetch(segment, threads, org.apache.nutch.fetcher.Fetcher.isParsing(conf));  // fetch it  

78.                //方法抓取中的核心    JobClient.runJob(job);  

79.                      if (!Fetcher.isParsing(job)) {  

80.                        parseSegment.parse(segment);    // parse it, if needed  

81.                      }  

82.                      crawlDbTool.update(crawlDb, new Path[]{segment}, truetrue); // update crawldb  

83.                    }  

84.                    if (i > 0) {  

85.                      linkDbTool.invert(linkDb, segments, truetruefalse); // invert links  

86.                  

87.                      if(indexes != null) {  

88.                        // Delete old indexes  

89.                        if (fs.exists(indexes)) {  

90.                          LOG.info("Deleting old indexes: " + indexes);  

91.                          fs.delete(indexes, true);  

92.                        }  

93.                  

94.                        // Delete old index  

95.                        if (fs.exists(index)) {  

96.                          LOG.info("Deleting old merged index: " + index);  

97.                          fs.delete(index, true);  

98.                        }  

99.                      }  

100.                

101.                    // index, dedup & merge  

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

103.                    indexer.index(indexes, crawlDb, linkDb, Arrays.asList(HadoopFSUtil.getPaths(fstats)));  

104.                    if(indexes != null) {  

105.                      dedup.dedup(new Path[] { indexes });  

106.                      fstats = fs.listStatus(indexes, HadoopFSUtil.getPassDirectoriesFilter(fs));  

107.                      merger.merge(HadoopFSUtil.getPaths(fstats), index, tmpDir);  

108.                    }  

109.                  } else {  

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

111.                  }  

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

113.                }  


日志分析: 

Java代码 

1.                  下面是我用来crawl的命令   

2.                  > bin/nutch crawl urls -dir crawl10 -depth 10 -threads 10 >& nohup.out   

3.                    

4.                  crawl started in: crawl10                               //表明网目录络蜘蛛的名称   

5.                  rootUrlDir = urls //待下载数据的列表文件或列表   

6.                  threads = 10 //下载线程为10   

7.                  depth = 10 //深度是10   

8.                  Injector: starting //注入下载列表   

9.                  Injector: crawlDb: crawl10/crawldb   

10.                Injector: urlDir: urls   

11.                Injector: Converting injected urls to crawl db entries. //根据注入的列表生成待下载的地址库   

12.                Injector: Merging injected urls into crawl db. //执行merge   

13.                Injector: done   

14.                Generator: Selecting best-scoring urls due for fetch. //判断网页重要性,决定下载顺序   

15.                Generator: starting   

16.                Generator: segment: crawl10/segments/20080904102201 //生成下载结果存储的数据段   

17.                Generator: filtering: false   

18.                Generator: topN: 2147483647 //没有指定topN大小,nutch会取默认值   

19.                Generator: Partitioning selected urls by host, for politeness.  //url下载列表按hadoop的中配置文件slaves中定义的datanode来分配。   

20.                Generator: done.   

21.                Fetcher: starting   

22.                Fetcher: segment: crawl10/segments/20080904102201 //下载指定网页内容到segment中去   

23.                Fetcher: done   

24.                CrawlDb update: starting //下载完毕后,更新下载数据库,增加新的下载   

25.                CrawlDb update: db: crawl10/crawldb   

26.                CrawlDb update: segments: [crawl10/segments/20080904102201]   

27.                CrawlDb update: additions allowed: true   

28.                CrawlDb update: URL normalizing: true   

29.                CrawlDb update: URL filtering: true   

30.                CrawlDb update: Merging segment data into db.   

31.                CrawlDb update: done   

32.                  

33.                //循环执行下载   

34.                Generator: Selecting best-scoring urls due for fetch.   

35.                Generator: starting   

36.                Generator: segment: crawl10/segments/20080904102453   

37.                Generator: filtering: false   

38.                Generator: topN: 2147483647   

39.                Generator: Partitioning selected urls by host, for politeness.   

40.                Generator: done.   

41.                Fetcher: starting   

42.                Fetcher: segment: crawl10/segments/20080904102453   

43.                Fetcher: done   

44.                CrawlDb update: starting   

45.                CrawlDb update: db: crawl10/crawldb   

46.                CrawlDb update: segments: [crawl10/segments/20080904102453]   

47.                CrawlDb update: additions allowed: true   

48.                CrawlDb update: URL normalizing: true   

49.                CrawlDb update: URL filtering: true   

50.                CrawlDb update: Merging segment data into db.   

51.                CrawlDb update: done   

52.                  

53.                ...... //一共循环10次,Nutch的局域网模式采用了广度优先策略,把二级页面抓取完成以后,进行三级页面抓取。   

54.                  

55.                LinkDb: starting //进行网页链接关系分析   

56.                LinkDb: linkdb: crawl10/linkdb   

57.                LinkDb: URL normalize: true //规范化   

58.                LinkDb: URL filter: true //根据crawl-urlfilter.txt来过滤   

59.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904102201   

60.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904102453   

61.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904102841   

62.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904104322   

63.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904113511   

64.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904132510   

65.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904153615   

66.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904175052   

67.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904194724   

68.                LinkDb: adding segment: /user/nutch/crawl10/segments/20080904211956   

69.                LinkDb: done //链接分析完毕   

70.                Indexer: starting //开始创建索引   

71.                Indexer: linkdb: crawl10/linkdb   

72.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904102201   

73.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904102453   

74.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904102841   

75.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904104322   

76.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904113511   

77.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904132510   

78.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904153615   

79.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904175052   

80.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904194724   

81.                Indexer: adding segment: /user/nutch/crawl10/segments/20080904211956   

82.                Indexer: done //索引创建完毕   

83.                Dedup: starting //索页去重   

84.                Dedup: adding indexes in: crawl10/indexes   

85.                Dedup: done   

86.                merging indexes to: crawl10/index //索引合并   

87.                Adding /user/nutch/crawl10/indexes/part-00000   

88.                Adding /user/nutch/crawl10/indexes/part-00001   

89.                Adding /user/nutch/crawl10/indexes/part-00002   

90.                Adding /user/nutch/crawl10/indexes/part-00003   

91.                done merging //合并完毕   

92.                crawl finished: crawl10 //入口注入、循环下载、链接分析、建立索引、去重、合并 

 

你可能感兴趣的:(mapreduce,数据库,Lucene,url,存储,generator)