Nutch 1.3 学习笔记 7 CrawlDb - updatedb


这里主要看一下CrawlDb中的updatedb,它主要是用来更新CrawlDb数据库的


1. bin/nutch updatedb

我们用nutch的命令行时会看到一个方法叫updatedb,其实这个方法就是调用CrawlDb.java类中的update方法,它的参数帮助如下:
[html]  view plain copy
  1. Usage: CrawlDb <crawldb> (-dir <segments> | <seg1> <seg2> ...) [-force] [-normalize] [-filter] [-noAdditions]  
  2.     crawldb CrawlDb to update  
  3.     -dir segments   parent directory containing all segments to update from  
  4.     seg1 seg2 ...   list of segment names to update from  
  5.     -force  force update even if CrawlDb appears to be locked (CAUTION advised)  
  6.     -normalize  use URLNormalizer on urls in CrawlDb and segment (usually not needed)  
  7.     -filter use URLFilters on urls in CrawlDb and segment  
  8.     -noAdditions    only update already existing URLs, don't add any newly discovered URLs  


2. 下面我们来分析一下其update方法到底做了些什么

2.1 update的任务提交参数,部分代码如下

[html]  view plain copy
  1. // 生成一个新的任务,这里面也做了一些相应的配置,  
  2.     // 加入了current目录,就是初始的CrawlDb目录,设置了输入格式为SequenceFileInputFormat  
  3.     // 配置了Map-Reducer为CrawlDbFilter-CrawlDbReducer  
  4.     // 配置了输出格式为MapFileOutputFormat  
  5.     // 还配置了输出的<key,value>类型<Text,CrawlDatum>  
  6.     JobConf job = CrawlDb.createJob(getConf(), crawlDb);  
  7.     // 配置一些参数  
  8.     job.setBoolean(CRAWLDB_ADDITIONS_ALLOWED, additionsAllowed);  
  9.     job.setBoolean(CrawlDbFilter.URL_FILTERING, filter);  
  10.     job.setBoolean(CrawlDbFilter.URL_NORMALIZING, normalize);  
  11.     // 加入输入目录,一个是crawl_fetch,另一个是crawl_parse  
  12.     for (int i = 0; i < segments.length; i++) {  
  13.       Path fetch = new Path(segments[i], CrawlDatum.FETCH_DIR_NAME);  
  14.       Path parse = new Path(segments[i], CrawlDatum.PARSE_DIR_NAME);  
  15.       if (fs.exists(fetch) && fs.exists(parse)) {  
  16.         FileInputFormat.addInputPath(job, fetch);  
  17.         FileInputFormat.addInputPath(job, parse);  
  18.       } else {  
  19.         LOG.info(" - skipping invalid segment " + segments[i]);  
  20.       }  
  21.     }  

2.2 分析一下其任务的Map-Reducer做了些什么

CrawlDbFilter主要是对url进行过滤和正规化。
CrawlDbReducer主要是用来聚合相同url(老的与新产生的)的,这东东写得很复杂,下面来分析一下其源代码:
[html]  view plain copy
  1. public void reduce(Text key, Iterator<CrawlDatum> values,  
  2.                      OutputCollector<Text, CrawlDatum> output, Reporter reporter)  
  3.     throws IOException {  
  4.   
  5.   
  6.     CrawlDatum fetch = new CrawlDatum();  
  7.     CrawlDatum old = new CrawlDatum();  
  8.   
  9.   
  10.     boolean fetchSet = false;  
  11.     boolean oldSet = false;  
  12.     byte[] signature = null;  
  13.     boolean multiple = false; // avoid deep copy when only single value exists  
  14.     linked.clear();  
  15.     org.apache.hadoop.io.MapWritable metaFromParse = null;  
  16.       
  17.     // 这个循环主要是遍历所有相同url的value(CrawlDatum)值,对old和fetch两个变量进行赋值。  
  18.     // 和收集其外链接,把它们放入一个按分数排序的优先队列中去  
  19.     while (values.hasNext()) {  
  20.       CrawlDatum datum = (CrawlDatum)values.next();  
  21.       // 判断是否要对CrawlDatum进行深度复制  
  22.       if (!multiple && values.hasNext()) multiple = true;  
  23.       // 判断CrawlDatum中是否有数据库相关的参数,如STATUS_DB_(UNFETCHED|FETCHED|GONE|REDIR_TEMP|REDIR_PERM|NOTMODIFIED)  
  24.       if (CrawlDatum.hasDbStatus(datum)) {  
  25.         if (!oldSet) {  
  26.           if (multiple) {  
  27.             old.set(datum);  
  28.           } else {  
  29.             // no need for a deep copy - this is the only value  
  30.             old = datum;  
  31.           }  
  32.           oldSet = true;  
  33.         } else {  
  34.           // always take the latest version  
  35.           // 总是得到最新的CrawlDatum版本  
  36.           if (old.getFetchTime() < datum.getFetchTime()) old.set(datum);  
  37.         }  
  38.         continue;  
  39.       }  
  40.   
  41.   
  42.         // 判断CrawlDatum是否有关抓取的状态,如STATUS_FETCH_(SUCCESS|RETRY|REDIR_TEMP|REDIR_PERM|GONE|NOTMODIFIED)  
  43.       if (CrawlDatum.hasFetchStatus(datum)) {  
  44.         if (!fetchSet) {  
  45.           if (multiple) {  
  46.             fetch.set(datum);  
  47.           } else {  
  48.             fetch = datum;  
  49.           }  
  50.           fetchSet = true;  
  51.         } else {  
  52.           // always take the latest version  
  53.           if (fetch.getFetchTime() < datum.getFetchTime()) fetch.set(datum);  
  54.         }  
  55.         continue;  
  56.       }  
  57.   
  58.   
  59.     // 根据CrawlDatum的状态来收集另一些信息  
  60.       switch (datum.getStatus()) {                // collect other info  
  61.           // 如果这个CrawlDatum是一个外链接,那放入一个优先队列中,按分数的降序来做  
  62.       case CrawlDatum.STATUS_LINKED:  
  63.         CrawlDatum link;  
  64.         if (multiple) {  
  65.           link = new CrawlDatum();  
  66.           link.set(datum);  
  67.         } else {  
  68.           link = datum;  
  69.         }  
  70.         linked.insert(link);  
  71.         break;  
  72.       case CrawlDatum.STATUS_SIGNATURE:  
  73.         // 得到其唯一ID号  
  74.         signature = datum.getSignature();  
  75.         break;  
  76.       case CrawlDatum.STATUS_PARSE_META:  
  77.         // 得到其元数据  
  78.         metaFromParse = datum.getMetaData();  
  79.         break;  
  80.       default:  
  81.         LOG.warn("Unknown status, key: " + key + ", datum: " + datum);  
  82.       }  
  83.     }  
  84.       
  85.     // copy the content of the queue into a List  
  86.     // in reversed order  
  87.     int numLinks = linked.size();  
  88.     List<CrawlDatum> linkList = new ArrayList<CrawlDatum>(numLinks);  
  89.     for (int i = numLinks - 1; i >= 0; i--) {  
  90.       linkList.add(linked.pop());  
  91.     }  
  92.       
  93.     // 如果这个CrawlDatum集合中没有数据库相关的状态(也就是说没有这个url的原始状态)或者配置了不添加外链接,直接返回  
  94.     // if it doesn't already exist, skip it  
  95.     if (!oldSet && !additionsAllowed) return;  
  96.       
  97.     // if there is no fetched datum, perhaps there is a link  
  98.     // 如果这个CrawlDatum集合中没有和抓取相关的状态,并且外链接数量要大于0  
  99.     if (!fetchSet && linkList.size() > 0) {  
  100.       fetch = linkList.get(0); // 得到第一个外链接  
  101.       fetchSet = true;  
  102.     }  
  103.       
  104.     // still no new data - record only unchanged old data, if exists, and return  
  105.     // 如果没有抓取相头的状态,也没有外链接,也就是说这个CrawlDatum是老的,  
  106.     if (!fetchSet) {  
  107.         // 判断是否有和数据库相关的状态,有的话就输出,没有的话就直接返回  
  108.       if (oldSet) {// at this point at least "old" should be present  
  109.         output.collect(key, old);  
  110.       } else {  
  111.         LOG.warn("Missing fetch and old value, signature=" + signature);  
  112.       }  
  113.       return;  
  114.     }  
  115.       
  116.     // 下面是用来初始化最新的CrawlDatum版本  
  117.     if (signature == null) signature = fetch.getSignature();  
  118.     long prevModifiedTime = oldSet ? old.getModifiedTime() : 0L;  
  119.     long prevFetchTime = oldSet ? old.getFetchTime() : 0L;  
  120.   
  121.   
  122.     // initialize with the latest version, be it fetch or link  
  123.     result.set(fetch);  
  124.     if (oldSet) {  
  125.       // copy metadata from old, if exists  
  126.       if (old.getMetaData().size() > 0) {  
  127.         result.putAllMetaData(old);  
  128.         // overlay with new, if any  
  129.         if (fetch.getMetaData().size() > 0)  
  130.           result.putAllMetaData(fetch);  
  131.       }  
  132.       // set the most recent valid value of modifiedTime  
  133.       if (old.getModifiedTime() > 0 && fetch.getModifiedTime() == 0) {  
  134.         result.setModifiedTime(old.getModifiedTime());  
  135.       }  
  136.     }  
  137.       
  138.     下面是用来确定其最新的状态  
  139.     switch (fetch.getStatus()) {                // determine new status  
  140.   
  141.   
  142.     case CrawlDatum.STATUS_LINKED:                // it was link  
  143.       if (oldSet) {                          // if old exists  
  144.         result.set(old);                          // use it  
  145.       } else {  
  146.         result = schedule.initializeSchedule((Text)key, result);  
  147.         result.setStatus(CrawlDatum.STATUS_DB_UNFETCHED);  
  148.         try {  
  149.           scfilters.initialScore((Text)key, result);  
  150.         } catch (ScoringFilterException e) {  
  151.           if (LOG.isWarnEnabled()) {  
  152.             LOG.warn("Cannot filter init score for url " + key +  
  153.                      ", using default: " + e.getMessage());  
  154.           }  
  155.           result.setScore(0.0f);  
  156.         }  
  157.       }  
  158.       break;  
  159.         
  160.     case CrawlDatum.STATUS_FETCH_SUCCESS:         // succesful fetch  
  161.     case CrawlDatum.STATUS_FETCH_REDIR_TEMP:      // successful fetch, redirected  
  162.     case CrawlDatum.STATUS_FETCH_REDIR_PERM:  
  163.     case CrawlDatum.STATUS_FETCH_NOTMODIFIED:     // successful fetch, notmodified  
  164.       // determine the modification status  
  165.       int modified = FetchSchedule.STATUS_UNKNOWN;  
  166.       if (fetch.getStatus() == CrawlDatum.STATUS_FETCH_NOTMODIFIED) {  
  167.         modified = FetchSchedule.STATUS_NOTMODIFIED;  
  168.       } else {  
  169.         if (oldSet && old.getSignature() != null && signature != null) {  
  170.           if (SignatureComparator._compare(old.getSignature(), signature) != 0) {  
  171.             modified = FetchSchedule.STATUS_MODIFIED;  
  172.           } else {  
  173.             modified = FetchSchedule.STATUS_NOTMODIFIED;  
  174.           }  
  175.         }  
  176.       }  
  177.       // set the schedule  
  178.       result = schedule.setFetchSchedule((Text)key, result, prevFetchTime,  
  179.           prevModifiedTime, fetch.getFetchTime(), fetch.getModifiedTime(), modified);  
  180.       // set the result status and signature  
  181.       if (modified == FetchSchedule.STATUS_NOTMODIFIED) {  
  182.         result.setStatus(CrawlDatum.STATUS_DB_NOTMODIFIED);  
  183.         if (oldSet) result.setSignature(old.getSignature());  
  184.       } else {  
  185.         switch (fetch.getStatus()) {  
  186.         case CrawlDatum.STATUS_FETCH_SUCCESS:  
  187.           result.setStatus(CrawlDatum.STATUS_DB_FETCHED);  
  188.           break;  
  189.         case CrawlDatum.STATUS_FETCH_REDIR_PERM:  
  190.           result.setStatus(CrawlDatum.STATUS_DB_REDIR_PERM);  
  191.           break;  
  192.         case CrawlDatum.STATUS_FETCH_REDIR_TEMP:  
  193.           result.setStatus(CrawlDatum.STATUS_DB_REDIR_TEMP);  
  194.           break;  
  195.         default:  
  196.           LOG.warn("Unexpected status: " + fetch.getStatus() + " resetting to old status.");  
  197.           if (oldSet) result.setStatus(old.getStatus());  
  198.           else result.setStatus(CrawlDatum.STATUS_DB_UNFETCHED);  
  199.         }  
  200.         result.setSignature(signature);  
  201.         if (metaFromParse != null) {  
  202.             for (Entry<Writable, Writable> e : metaFromParse.entrySet()) {  
  203.               result.getMetaData().put(e.getKey(), e.getValue());  
  204.             }  
  205.           }  
  206.       }  
  207.       // if fetchInterval is larger than the system-wide maximum, trigger  
  208.       // an unconditional recrawl. This prevents the page to be stuck at  
  209.       // NOTMODIFIED state, when the old fetched copy was already removed with  
  210.       // old segments.  
  211.       if (maxInterval < result.getFetchInterval())  
  212.         result = schedule.forceRefetch((Text)key, result, false);  
  213.       break;  
  214.     case CrawlDatum.STATUS_SIGNATURE:  
  215.       if (LOG.isWarnEnabled()) {  
  216.         LOG.warn("Lone CrawlDatum.STATUS_SIGNATURE: " + key);  
  217.       }     
  218.       return;  
  219.     case CrawlDatum.STATUS_FETCH_RETRY:           // temporary failure  
  220.       if (oldSet) {  
  221.         result.setSignature(old.getSignature());  // use old signature  
  222.       }  
  223.       result = schedule.setPageRetrySchedule((Text)key, result, prevFetchTime,  
  224.           prevModifiedTime, fetch.getFetchTime());  
  225.       if (result.getRetriesSinceFetch() < retryMax) {  
  226.         result.setStatus(CrawlDatum.STATUS_DB_UNFETCHED);  
  227.       } else {  
  228.         result.setStatus(CrawlDatum.STATUS_DB_GONE);  
  229.       }  
  230.       break;  
  231.   
  232.   
  233.     case CrawlDatum.STATUS_FETCH_GONE:            // permanent failure  
  234.       if (oldSet)  
  235.         result.setSignature(old.getSignature());  // use old signature  
  236.       result.setStatus(CrawlDatum.STATUS_DB_GONE);  
  237.       result = schedule.setPageGoneSchedule((Text)key, result, prevFetchTime,  
  238.           prevModifiedTime, fetch.getFetchTime());  
  239.       break;  
  240.   
  241.   
  242.     default:  
  243.       throw new RuntimeException("Unknown status: " + fetch.getStatus() + " " + key);  
  244.     }  
  245.   
  246.   
  247.     // 这里用来更新result的分数  
  248.     try {  
  249.       scfilters.updateDbScore((Text)key, oldSet ? old : null, result, linkList);  
  250.     } catch (Exception e) {  
  251.       if (LOG.isWarnEnabled()) {  
  252.         LOG.warn("Couldn't update score, key=" + key + ": " + e);  
  253.       }  
  254.     }  
  255.     // remove generation time, if any  
  256.     result.getMetaData().remove(Nutch.WRITABLE_GENERATE_TIME_KEY);  
  257.     output.collect(key, result);   // 写出数据  
  258.   }  
  259.     
  260. }  

3. 总结

  • 这里大概分析了一下CrawlDb的更新流程,有一些地方还是没有看得太明白,可能要通过测试来更深入的理解。
  • 其中流程就是对三个目录进行合并,对相同的url的value(CrawlDatum)进行聚合,产生新的CarwlDatum,再写回原来的数据库中。
  • 其复杂的地方在于如果对聚合后的结果进行处理,这个有空还要再看一下。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
水资源是人类社会的宝贵财富,在生活、工农业生产中是不可缺少的。随着世界人口的增长及工农业生产的发展,需水量也在日益增长,水已经变得比以往任何时候都要珍贵。但是,由于人类的生产和生活,导致水体的污染,水质恶化,使有限的水资源更加紧张。长期以来,油类物质(石油类物质和动植物油)一直是水和土壤中的重要污染源。它不仅对人的身体健康带来极大危害,而且使水质恶化,严重破坏水体生态平衡。因此各国都加强了油类物质对水体和土壤的污染的治理。对于水中油含量的检测,我国处于落后阶段,与国际先进水平存在差距,所以难以满足当今技术水平的要求。为了取得具有代表性的正确数据,使分析数据具有与现代测试技术水平相应的准确性和先进性,不断提高分析成果的可比性和应用效果,检测的方法和仪器是非常重要的。只有保证了这两方面才能保证快速和准确地测量出水中油类污染物含量,以达到保护和治理水污染的目的。开展水中油污染检测方法、技术和检测设备的研究,是提高水污染检测的一条重要措施。通过本课题的研究,探索出一套适合我国国情的水质污染现场检测技术和检测设备,具有广泛的应用前景和科学研究价值。 本课题针对我国水体的油污染,探索一套检测油污染的可行方案和方法,利用非分散红外光度法技术,开发研制具有自主知识产权的适合国情的适于野外便携式的测油仪。利用此仪器,可以检测出被测水样中亚甲基、甲基物质和动植物油脂的污染物含量,为我国众多的环境检测站点监测水体的油污染状况提供依据。
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值