Nutch1.7源码再研究之---18:Nutch中从Inject传递参数到Index

最近项目中碰到一个需求,就是从数据库mongo中查出相应的url ,然后爬虫解析出字段,再indexer回mongodb中。

那么碰到的问题就是:如何将mongo中的url的主键传递到indexer中去

--------------------------------------实现思路是:

修改源码Crawl.java,在

prepareInjectTxt(url);

 injector.inject(crawlDb, rootUrlDir);

也就是说在inject之前,执行一个函数,函数的作用是从mongo中查出需要爬虫的url,写入到对应的url文件

这个文件就是injector.inject所需要的文件。

精华在于:写入每个url时,后面附带上

outputStream.write((url+" id="+id+"\n").getBytes());

注意:!!!这里的url+" id="的id之前的这个不是空格,而是\t.

这是Injector.java中代码所定义的分隔符,见下面的代码:

if (url.indexOf("\t")!=-1){

       String[] splits = url.split("\t");

       url = splits[0];

       for (int s=1;s<splits.length;s++){

       // find separation between name and value

       int indexEquals = splits[s].indexOf("=");

       if (indexEquals==-1) {

       // skip anything without a =

       continue;     

       }

       String metaname = splits[s].substring(0, indexEquals);

       String metavalue = splits[s].substring(indexEquals+1);

       if (metaname.equals(nutchScoreMDName)) {

       try {

       customScore = Float.parseFloat(metavalue);}

       catch (NumberFormatException nfe){}

       }

                  else if (metaname.equals(nutchFetchIntervalMDName)) {

                          try {

                                  customInterval = Integer.parseInt(metavalue);}

                          catch (NumberFormatException nfe){}

                  }

                  else if (metaname.equals(nutchFixedFetchIntervalMDName)) {

                          try {

                                  fixedInterval = Integer.parseInt(metavalue);}

                          catch (NumberFormatException nfe){}

                  }

       else metadata.put(metaname,metavalue);

       }

      }

 也就是说,此刻,我们的主键存放到了metadata中!

----------继续跟踪这个metadata.

通过代码

datum.getMetaData().put(new Text(keymd), new Text(valuemd));

output.collect(value, datum);

 我们可以知道datum.getMetaData()中存放了我们的自定义主键。

----------------------------------------------------经过inject和generate的过程后,来到fetch部分。

文件读取是由

private static class QueueFeeder extends Thread {

完成的。里面的具体读取代码见:

 

while (feed > 0 && hasMore) {

            try {

              Text url = new Text();

              CrawlDatum datum = new CrawlDatum();

              hasMore = reader.next(url, datum);

              if (hasMore) {

                queues.addFetchItem(url, datum);

我们打印出这个datum的内容如下:

 

 MapWritable mw=datum.getMetaData();

                 Set<Entry<Writable,Writable>> kv= mw.entrySet();

                 Iterator<Entry<Writable,Writable>> iterator = kv.iterator();

                 while(iterator.hasNext()){

                 Entry<Writable,Writable> entry = iterator.next();

                 Writable key = entry.getKey();

                 Writable value = entry.getValue();

                 System.out.println("k/v---------------------------"+key +"  "+value);

                 }

                

打印出来的结果是:

k/v---------------------------id  158134
k/v---------------------------_ngt_  1414041495640

说明确实存在这个从数据库里读取的id.继续跟踪这个值。

再来看

 private class FetcherThread extends Thread {

有这么一段代码:

 

 output.collect(key, new NutchWritable(datum));

        if (content != null && storingContent)

          output.collect(key, new NutchWritable(content));

打印这个datum的值。

k/v---------------------------id  158134
k/v---------------------------_ngt_  1414042449861
k/v---------------------------Content-Type  text/html
k/v---------------------------_pst_  success(1), lastModified=0

至于怎么写入到磁盘的,请参考类FetcherOutputFormat.java类

代码如下:

 

 if (w instanceof CrawlDatum)

            fetchOut.append(key, w);

          else if (w instanceof Content && contentOut != null)

            contentOut.append(key, w);

          else if (w instanceof Parse && parseOut != null)

            parseOut.write(key, (Parse)w);

为了印证确实是这里写入了数据,我们打印它,结果如下:

[FetcherOutputFormat]k/v---------------------------id  158134
[FetcherOutputFormat]k/v---------------------------_ngt_  1414043468795
[FetcherOutputFormat]k/v---------------------------Content-Type  text/html
[FetcherOutputFormat]k/v---------------------------_pst_  success(1), lastModified=0

 

--------------------------------------------然后进入Parse环节。

 这个环境的内容由以下2行代码提供  

 job.setMapperClass(ParseSegment.class);

 job.setReducerClass(ParseSegment.class);

  这个job的输入路径是

FileInputFormat.addInputPath(jobnew Path(segment, Content.DIR_NAME));

跟fetch之后的结果没有影响,因为不会读文件夹crawl_fetch,只读文件夹content.

所以我们把parse忽略掉。update也忽略掉。

------------------------------index过程。

 

 IndexerMapReduce.initMRJob(crawlDb, linkDb, segments, job);

跟踪这个函数,有以下的代码:

 

 for (final Path segment : segments) {

      LOG.info("IndexerMapReduces: adding segment: " + segment);

      FileInputFormat.addInputPath(job, new Path(segment, CrawlDatum.FETCH_DIR_NAME));

      FileInputFormat.addInputPath(job, new Path(segment, CrawlDatum.PARSE_DIR_NAME));

      FileInputFormat.addInputPath(job, new Path(segment, ParseData.DIR_NAME));

      FileInputFormat.addInputPath(job, new Path(segment, ParseText.DIR_NAME));

    }

然后查看代码,发现:

 

 job.setMapperClass(IndexerMapReduce.class);

    job.setReducerClass(IndexerMapReduce.class);

----------------------------这里主要看它的reduce方法。

 

// don't index unmodified (empty) pages

          if (datum.getStatus() != CrawlDatum.STATUS_FETCH_NOTMODIFIED) {

            fetchDatum = datum;

应该我们需要的fetchDatum就在这个fetchDatum中,大胆猜测,小心验证,写代码输出值如下:

[IndexerMapReduce]k/v---------------------------id  158134
[IndexerMapReduce]k/v---------------------------_ngt_  1414044736594
[IndexerMapReduce]k/v---------------------------Content-Type  text/html
[IndexerMapReduce]k/v---------------------------_pst_  success(1), lastModified=0

果然是如此,很好,我们拿到了主键。下面就可以拿来用了!

-------------------------------------------------------------------------------------------

最关键的代码就是IndexeMapReduce中的

 // run indexing filters

doc = this.filters.filter(doc, parse, key, fetchDatum, inlinks);

看来是要在索引过滤插件IndexingFilter中写代码提取这个参数了。

修改函数

 

// implements the filter-method which gives you access to important Objects

// like NutchDocument

public NutchDocument filter(NutchDocument doc, Parse parse, Text url,

CrawlDatum datum, Inlinks inlinks) {

我们从datum中获取这个_id.

String id = getValueFromMetaData(datum.getMetaData(),"id");

doc.add("_id", id);

 

 public String getValueFromMetaData(MapWritable mw,String k){
    Set<Entry<Writable,Writable>> kv= mw.entrySet();
   Iterator<Entry<Writable,Writable>> iterator = kv.iterator();
   while(iterator.hasNext()){
    Entry<Writable,Writable> entry = iterator.next();
    Writable key = entry.getKey();
    Writable value = entry.getValue();
    System.out.println("[IndexingFilterAll]k/v---------------------------"+key +"  "+value);
  if(key.toString().equals(k)) return value.toString();
   }
 return null;
  }【这里或许有直接获取key的方法,对Hadoop暂时不是很熟悉】

大功告成!

 

 

你可能感兴趣的:(Nutch,index,inject)