Nutch教程——Nutch流程控制源码详解(bin/crawl中文注释版)

  1. #!/bin/bash
  2. # 此中文注释由社区"Nutch开发者" nutcher.org提供,作者是"逼格DATA",未经允许,禁止转载
  3. # 官方网站 http://nutcher.org
  4. # 教程地址 http://nutcher.org/book/
  5. # 爬取命令 crawl 种子文件夹路径 存放数据文件夹路径 solr的URL 爬取深度(层数)
  6. # The Crawl command script : crawl <seedDir> <crawlDir> <solrURL> <numberOfRounds>
  7. #
  8. #
  9. # UNLIKE THE NUTCH ALL-IN-ONE-CRAWL COMMAND THIS SCRIPT DOES THE LINK INVERSION AND
  10. # INDEXING FOR EACH SEGMENT
  11. #$1 $2 ... $n 表示命令后跟的第n个参数
  12. #存放待注入种子的路径
  13. SEEDDIR="$1"
  14. #存放爬取数据(URL状态信息、爬取数据、解析数据)文件夹的路径
  15. CRAWL_PATH="$2"
  16. #如果要将爬取结果提交给solr做索引,第三个参数要设置为solr的url
  17. #如果不需要索引到solr,可将下面这行注释,但需要对其他地方做相应修改
  18. SOLRURL="$3"
  19. #爬取的深度(广度遍历的层数)
  20. #如果不需要提交到solr,将上行注释之后,注意将下行的$4改为$3
  21. LIMIT="$4"
  22. #如果缺少参数,结束整个程序
  23. if [ "$SEEDDIR" = "" ]; then
  24. echo "Missing seedDir : crawl <seedDir> <crawlDir> <solrURL> <numberOfRounds>"
  25. exit -1;
  26. fi
  27. if [ "$CRAWL_PATH" = "" ]; then
  28. echo "Missing crawlDir : crawl <seedDir> <crawlDir> <solrURL> <numberOfRounds>"
  29. exit -1;
  30. fi
  31. #如果不想提交数据到solr,记得将此模块全部注释
  32. if [ "$SOLRURL" = "" ]; then
  33. echo "Missing SOLRURL : crawl <seedDir> <crawlDir> <solrURL> <numberOfRounds>"
  34. exit -1;
  35. fi
  36. if [ "$LIMIT" = "" ]; then
  37. echo "Missing numberOfRounds : crawl <seedDir> <crawlDir> <solrURL> <numberOfRounds>"
  38. exit -1;
  39. fi
  40. #############################################
  41. # MODIFY THE PARAMETERS BELOW TO YOUR NEEDS #
  42. #############################################
  43. # set the number of slaves nodes
  44. #如果在集群运行,一定要将这里改成slave的数量,否则在爬取阶段,只有1台机器会执行爬取
  45. numSlaves=1
  46. # and the total number of available tasks
  47. # sets Hadoop parameter "mapred.reduce.tasks"
  48. #reduce任务的数量会根据上面设定的numSlaves来设定
  49. numTasks=`expr $numSlaves \* 2`
  50. # number of urls to fetch in one iteration
  51. # 250K per task?
  52. sizeFetchlist=`expr $numSlaves \* 50000`
  53. # time limit for feching
  54. timeLimitFetch=180
  55. # num threads for fetching
  56. #每台集群爬取的线程数
  57. numThreads=50
  58. #############################################
  59. #获取执行命令的目录(runtime/local或runtime/deploy)中bin文件夹的绝对路径
  60. bin="`dirname "$0"`"
  61. bin="`cd "$bin"; pwd`"
  62. # determines whether mode based on presence of job file
  63. mode=local
  64. #如果bin文件夹的上级目录(当前文件夹)中包含nutch*.job这样的文件,则认为是分布式运行
  65. #runtime/local文件夹中不包含nutch*.job文件,而runtime/deploy文件夹中包含
  66. if [ -f "${bin}"/../*nutch*.job ]; then
  67. mode=distributed
  68. fi
  69. # note that some of the options listed here could be set in the
  70. # corresponding hadoop site xml param file
  71. #一些公用参数
  72. commonOptions="-D mapred.reduce.tasks=$numTasks -D mapred.child.java.opts=-Xmx1000m -D mapred.reduce.tasks.speculative.execution=false -D mapred.map.tasks.speculative.execution=false -D mapred.compress.map.output=true"
  73. # check that hadoop can be found on the path
  74. # 如果是分布式模式,用which命令检测环境变量中是否配置了hadoop相关参数(HADOOP_HOME,PATH)
  75. # 如果没有在PATH中配置hadoop的bin文件夹,退出程序
  76. # 本地模式不需要配置hadoop相关参数
  77. if [ $mode = "distributed" ]; then
  78. if [ $(which hadoop | wc -l ) -eq 0 ]; then
  79. echo "Can't find Hadoop executable. Add HADOOP_HOME/bin to the path or run in local mode."
  80. exit -1;
  81. fi
  82. fi
  83. # initial injection
  84. # 注入种子列表,$SEEDDIR是bin/crawl后的第一个参数,$CRAWL_PATH是bin/crawl后的第二个参数
  85. # 种子被注入到
  86. "$bin/nutch" inject "$CRAWL_PATH"/crawldb "$SEEDDIR"
  87. #nutch里每个命令执行的都是某个java类的main函数,这些main函数中都会通过System.exit(结束代码)
  88. #$?可以获取到结束代码的值,如果结束代码为0,表示程序运行正常,如果不为0,表示程序异常
  89. #-ne表示不等于
  90. if [ $? -ne 0 ]
  91. then exit $?
  92. fi
  93. # main loop : rounds of generate - fetch - parse - update
  94. # 执行循环,每次执行一层(一个深度)的爬取
  95. # 每层爬取都会执行:
  96. # 1.生成本次待爬取任务列表(generate)
  97. # 2.抓取爬取列表中页面(fetch)
  98. # 3.解析爬取页面(parse),抽取所有链接,以及搜索引擎相关数据(网页标题、网页文本、meta信息等)
  99. # 4.将从本次爬取中解析出的链接,更新(update)到$CRAWL_PATH/crawldb中,同时将抓取到的链接的状态(抓取成功、失败、重试次数),更新到$CRAWL_PATH/crawldb中
  100. # LIMIT是bin/crawl命令后参数中给定的
  101. for ((a=1; a <= LIMIT ; a++))
  102. do
  103. #nutch爬取是一个长期的任务,如果想结束nutch的爬取,可以在当前目录(执行命令的目录)下,新建一个.STOP文件,每层爬取一开始都会检测目录是否有.STOP文件(-e表示文件是否存在)
  104. if [ -e ".STOP" ]
  105. then
  106. echo "STOP file found - escaping loop"
  107. break
  108. fi
  109. echo `date` ": Iteration $a of $LIMIT"
  110. echo "Generating a new segment"
  111. #从$CRAWL_PATH/crawldb中,生成待爬取任务列表,并且在segments文件夹下,根据当前时间生成一个文件夹segments/时间,将待爬取任务存放到segments/时间/crawl_generate文件夹中,以SequenceFile<Text,CrawlDatum>的形式存储
  112. #topN表示最多生成多少待爬取任务(URL)
  113. "$bin/nutch" generate $commonOptions "$CRAWL_PATH"/crawldb "$CRAWL_PATH"/segments -topN $sizeFetchlist -numFetchers $numSlaves -noFilter
  114. if [ $? -ne 0 ]
  115. then exit $?
  116. fi
  117. # capture the name of the segment
  118. # call hadoop in distributed mode
  119. # or use ls
  120. # 爬取(fetch)任务需要获取上面生成(generate)的任务列表,generate任务会根据当前时间在segments文件夹中生成响应的segment文件夹(segments/时间),时间是用System.currentTimeMillis()生成的long类型的数值,数值越小时间越早,获取刚生成(generate)的segment的方法是:
  121. # 1.用ls命令获取segments文件夹下的文件列表(long类型列表)
  122. # 2.用sort命令将文件名(long类型的时间)排序(从小到大)
  123. # 3.用tail -n 1获取最后一行(最大的时间),也就是最新生成的segment文件夹
  124. if [ $mode = "local" ]; then
  125. SEGMENT=`ls "$CRAWL_PATH"/segments/ | sort -n | tail -n 1`
  126. else
  127. #如果在分布式模式下,需要对hdfs中文件进行查询,用ls无效,需要用hadoop fs -ls
  128. SEGMENT=`hadoop fs -ls "$CRAWL_PATH"/segments/ | grep segments | sed -e "s/\//\\n/g" | egrep 20[0-9]+ | sort -n | tail -n 1`
  129. fi
  130. echo "Operating on segment : $SEGMENT"
  131. # fetching the segment
  132. # 开始抓取,$numThreads是上面给定的线程数量
  133. # 爬取有2种模式:
  134. # 1.爬取时同时解析
  135. # 2.爬取时不解析
  136. # 默认是爬取时不解析,将解析任务放到后面的parse任务中
  137. # shell中的 -noParsing已经无效
  138. # 如果要修改模式,需要修改conf/nutch-default.xml中的fetcher.parse的值,true表示爬取时同时解析
  139. # 如果将fetcher.parse设为true,设为爬取时同时解析,需要将下面的parse模块全部注释掉
  140. echo "Fetching : $SEGMENT"
  141. "$bin/nutch" fetch $commonOptions -D fetcher.timelimit.mins=$timeLimitFetch "$CRAWL_PATH"/segments/$SEGMENT -noParsing -threads $numThreads
  142. if [ $? -ne 0 ]
  143. then exit $?
  144. fi
  145. # parsing the segment
  146. # 解析网页,解析模块包含两个任务
  147. # 1.抽取网页中的链接,使得爬虫可以找到新的链接,在下一层(或者以后)进行爬取
  148. # 2.抽取网页的标题、文本、meta等信息,这些数据会在索引阶段,提交给solr供搜索使用
  149. # 如果将fetcher.parse设为true,设为爬取时同时解析,需要将本parse模块全部注释掉
  150. echo "Parsing : $SEGMENT"
  151. # enable the skipping of records for the parsing so that a dodgy document
  152. # so that it does not fail the full task
  153. skipRecordsOptions="-D mapred.skip.attempts.to.start.skipping=2 -D mapred.skip.map.max.skip.records=1"
  154. "$bin/nutch" parse $commonOptions $skipRecordsOptions "$CRAWL_PATH"/segments/$SEGMENT
  155. if [ $? -ne 0 ]
  156. then exit $?
  157. fi
  158. # 更新(update)模块,nutch的爬取任务维护最核心的模块
  159. # nutch将所有已知的URL信息全部存在$CRAWL_PATH/crawldb中,包含已经爬取的URL,和还未爬取的URL
  160. # updatedb任务完成2个功能:
  161. # 1.将fetch任务中爬取的URL的状态(爬取失败、成功、retry次数等),更新到$CRAWL_PATH/crawldb中
  162. # 2.将parse任务中解析出的链接信息,加到$CRAWL_PATH/crawldb中
  163. # 上面两个功能都要保证$CRAWL_PATH/crawldb中URL的唯一性(一个URL只出现一次),唯一性是通过map recude框架的机制来保证的,reduce获取到的value列表都是对应一个相同的URL的,在这些value中选取一个最合适的留下
  164. # updatedb with this segment
  165. echo "CrawlDB update"
  166. "$bin/nutch" updatedb $commonOptions "$CRAWL_PATH"/crawldb "$CRAWL_PATH"/segments/$SEGMENT
  167. if [ $? -ne 0 ]
  168. then exit $?
  169. fi
  170. # note that the link inversion - indexing routine can be done within the main loop
  171. # on a per segment basis
  172. # 链接反转,如果不需要做索引(不用solr)可以将这个模块注释
  173. echo "Link inversion"
  174. "$bin/nutch" invertlinks "$CRAWL_PATH"/linkdb "$CRAWL_PATH"/segments/$SEGMENT
  175. if [ $? -ne 0 ]
  176. then exit $?
  177. fi
  178. # 根据网页MD5等进行基于内容的去重,如果用nutch做数据采集而不是搜索引擎,可将此模块注释
  179. echo "Dedup on crawldb"
  180. $bin/nutch dedup $CRAWL_PATH/crawldb
  181. if [ $? -ne 0 ]
  182. then exit $?
  183. fi
  184. # 将爬取的数据索引并提交到solr,如果不需要索引,可以将下面两个模块注释
  185. echo "Indexing $SEGMENT on SOLR index -> $SOLRURL"
  186. "$bin/nutch" index -D solr.server.url=$SOLRURL "$CRAWL_PATH"/crawldb -linkdb "$CRAWL_PATH"/linkdb "$CRAWL_PATH"/segments/$SEGMENT
  187. if [ $? -ne 0 ]
  188. then exit $?
  189. fi
  190. #清除solr中一些索引(因为状态更新的缘故),如果不需要索引,可以将这个模块注释
  191. echo "Cleanup on SOLR index -> $SOLRURL"
  192. "$bin/nutch" clean -D solr.server.url=$SOLRURL "$CRAWL_PATH"/crawldb
  193. if [ $? -ne 0 ]
  194. then exit $?
  195. fi
  196. done
  197. exit 0