Nutch中Injector的过程

Injector过程主要分成两个过程,而且这两个过程是两个独立的Map/Reduce任务,前一个任务只有Map,后一个任务是一个完整的Map/Reduce过程。

在详细介绍之前,先看inject()的主调用代码:
代码1:
public void inject(Path crawlDb, Path urlDir) throws IOException {
if (LOG.isInfoEnabled()) {
LOG.info("Injector: starting");
LOG.info("Injector: crawlDb: " + crawlDb);
LOG.info("Injector: urlDir: " + urlDir);
}
Path tempDir =
new Path(getConf().get("mapred.temp.dir", ".") +
"/inject-temp-"+
Integer.toString(new Random().nextInt(Integer.MAX_VALUE)));

// map text input file to a <url,CrawlDatum> file
if (LOG.isInfoEnabled()) {
LOG.info("Injector: Converting injected urls to crawl db entries.");
}

/* 第一个任务 */
JobConf sortJob = new NutchJob(getConf());
sortJob.setJobName("inject " + urlDir);
FileInputFormat.addInputPath(sortJob, urlDir); //设置第一个任务的输入路径
sortJob.setMapperClass(InjectMapper.class); //设置第一个任务的Map类

FileOutputFormat.setOutputPath(sortJob, tempDir); //设置第一个任务的输出路径,也是一个临时目录
sortJob.setOutputFormat(SequenceFileOutputFormat.class);
sortJob.setOutputKeyClass(Text.class);
sortJob.setOutputValueClass(CrawlDatum.class);
sortJob.setLong("injector.current.time", System.currentTimeMillis());
JobClient.runJob(sortJob); //执行第一个任务

// merge with existing crawl db
if (LOG.isInfoEnabled()) {
LOG.info("Injector: Merging injected urls into crawl db.");
}
/* 第二个任务 */
JobConf mergeJob = CrawlDb.createJob(getConf(), crawlDb); //初始化第二个任务,包括Map类和Reduce类,以及输入,输出路径,注意输出路径也是在这个函数里设置的
FileInputFormat.addInputPath(mergeJob, tempDir); //为第二个任务添加一个输入路径
mergeJob.setReducerClass(InjectReducer.class); //重置Reducer类,即替换之前在mergeJob初始化时定义的Reducer类
JobClient.runJob(mergeJob); //执行第二个任务
CrawlDb.install(mergeJob, crawlDb);

// clean up
FileSystem fs = FileSystem.get(getConf());
fs.delete(tempDir, true);
if (LOG.isInfoEnabled()) { LOG.info("Injector: done"); }
}
这个函数初一看,诈似一个Map/Reduce过程,实际上是两个Map/Reduce过程。

第一个任务(其实就一个Map过程,主要是为了从urlDir中读种子url,并对这些url进行初始化处理,并把处理结果保存到临时文件中):
主要过程介绍:在Map之前,先要将把urlDir(保存种子url的文件路径)中的url(一行一个url)一个一个地读出,把然后把url保存为value(value为Text类型对象),key为WritableComparable类型,值为0。然后将<key, value>作为Map过程的输入。
Map过程(Injector.InjectMapper类中定义):这个过程主要和urlDir有关,该过程主要是从的任务是判断输入的<key, value>中,value的值是否是应该爬取的页面的url,如果是,则将url重新加载到value中,然后整合配置信息中的fetch信息到一个新建的CrawlDatum对象datum中,然后将<value, datum>作为键值通过output.collect(value, datum)保存第一个任务的结果临时保存文件tempDir中。
其代码为:
代码2:
public void map(WritableComparable key, Text value,
OutputCollector<Text, CrawlDatum> output, Reporter reporter)
throws IOException {
String url = value.toString(); // value is line of text
try {
url = urlNormalizers.normalize(url, URLNormalizers.SCOPE_INJECT); //url规整化处理
url = filters.filter(url); // 对url进行过滤处理
} catch (Exception e) {
if (LOG.isWarnEnabled()) { LOG.warn("Skipping " +url+":"+e); }
url = null;
}
if (url != null) { // if it passes
value.set(url); //对value重置处理过的url
CrawlDatum datum = new CrawlDatum(CrawlDatum.STATUS_INJECTED, interval);//添加fetch间隔时间到datum中
datum.setFetchTime(curTime); //添加当前时间到datum中
datum.setScore(scoreInjected); //添加分值到datum中
try {
scfilters.injectedScore(value, datum); //根据分值进行一些操作
} catch (ScoringFilterException e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Cannot filter injected score for url " + url +
", using default (" + e.getMessage() + ")");
}
datum.setScore(scoreInjected); //出现异常时重置分值到datum中
}
output.collect(value, datum); //写处理结果到tempDir指定路径的临时文件中
}
}
}
主要的处理过程:
1、对url进行规整化处理;
2、对url进行过滤操作(即判断该url是否是应该爬取的页面的url);
3、分别将爬取间隔时间、当前爬取时间、分值等代入一个CrawlDatum对象中,构建一个datum对象中;4、写这个<value, datum>键值对到tempDir指定路径的文件中。

第二个任务(主要是对一个url的多个crawlDB对象进行过滤操作):
主要过程介绍:这个过程主要和crawlDB有关,其任务主要是从第一个任务产生的tempDir文件和crawlDB中读<url, datum>键值对,并将这些键值对进行url规整化和过滤处理,并按照同一个url为一组的原则组成一个<value,datums>(datums不是datum,datums是CrawlDatum的一个迭代器,其中包含了所有属于url 的datum),然后将从这些对应同一个url的众多CrawlDatum对象中选择一个CrawlDatum对象作为合适的<value, datum>作为输出,输出到crawlDB中。
先看(代码1)中的代码:
JobConf mergeJob = CrawlDb.createJob(getConf(), crawlDb);
该函数初始化了一个完整的Map/Reduce任务mergeJob,包括Map类和reduce类的设置,同时设置输入路径(输入路径为CrawlDB的路径,后面还要添加第一个任务处理后保存的临时文件路径tempDir)和输出路径。只不过这时的mergeJob不符合第二个任务的需要,后面需要做一些修改:
代码3:
public static JobConf createJob(Configuration config, Path crawlDb)
throws IOException {
Path newCrawlDb =
new Path(crawlDb,
Integer.toString(new Random().nextInt(Integer.MAX_VALUE)));

JobConf job = new NutchJob(config); //设置配置信息
job.setJobName("crawldb " + crawlDb);

Path current = new Path(crawlDb, CURRENT_NAME); //根据CrawlDB的路径crawlDb处理输入路径
if (FileSystem.get(job).exists(current)) {
FileInputFormat.addInputPath(job, current); //添加输入路径
}
job.setInputFormat(SequenceFileInputFormat.class);

job.setMapperClass(CrawlDbFilter.class); //设置Map类
job.setReducerClass(CrawlDbReducer.class); //设置Reduce类,不过实际不需要这个类,后面会重置它
FileOutputFormat.setOutputPath(job, newCrawlDb); //设置输出路径
job.setOutputFormat(MapFileOutputFormat.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(CrawlDatum.class);

return job;
}
前面提到输入路径需要添加第一个任务处理后保存的临时文件路径tempDir,因为事先不知道种子url是否在CrawlDB中已经存在了,而且如果种子url太多的话,就不能保证它们之中没有重复的url,所以需要结合第一个任务导入的种子url和CrawlDB中包含的url,来对url进行去重。(这里就有一个感觉nutch设计不好的地方,因为每次注入种子url时,其实只要根据种子url和CrawlDB中可能存在的相同的url进行去重就可以了,根本就不需要把整个CrawlDB都去重一次)。所以需要添加临时文件路径tempDir,来一起进行去重。添加路径代码见(代码1)的:
FileInputFormat.addInputPath(mergeJob, tempDir);
又因为CrawlDb.createJob(getConf(), crawlDb);中设置的Reduce类不是inject需要的类,所以需要对其进行重置,重置的代码见(代码1)的:
mergeJob.setReducerClass(InjectReducer.class); //重置ReducerClass
现在再分别介绍Map过程和Reduce过程:
Map过程(CrawlDbFilter类中定义):
先看其map函数的代码,
代码4:
public void map(Text key, CrawlDatum value,
OutputCollector<Text, CrawlDatum> output,
Reporter reporter) throws IOException {
//该函数的作用实际上就是写crawlDB
String url = key.toString();
if (urlNormalizers) { //key(url)规整化
try {
url = normalizers.normalize(url, scope); // normalize the url // normalize the url, 为什么这里还要进行url规整化操作
} catch (Exception e) {
LOG.warn("Skipping " + url + ":" + e);
url = null;
}
}
if (url != null && urlFiltering) {//key(url)过滤
try {
url = filters.filter(url); // filter the url
} catch (Exception e) {
LOG.warn("Skipping " + url + ":" + e);
url = null;
}
}
if (url != null) { // if it passes
newKey.set(url); // collect it
output.collect(newKey, value); //写文件
}
}
通过这个函数的代码发现,其实第二个任务的Map过程其实就是一个对url进行规整化和过滤的过程,只不过它不仅对CrawlDB中的url进行规整化和过滤操作,而且它还对tempDir指定路径的文件中的url进行规整化和过滤操作。然后把得到的<url, datum>(这个url是Text类型,Map过程没有对datum做任何处理)通过output.collect(newKey, value);临时保存着,等待紧接着的Reduce过程的处理。
Reduce过程(Injector.InjectReducer类中定义):
这个过程可以先看代码,
代码5:
private CrawlDatum old = new CrawlDatum(); //表示是CrawlDB中的url
private CrawlDatum injected = new CrawlDatum(); //表示是注入的种子url
public void reduce(Text key, Iterator<CrawlDatum> values,
OutputCollector<Text, CrawlDatum> output, Reporter reporter)
throws IOException {
boolean oldSet = false;
while (values.hasNext()) { //通过迭代器循环获得所有该url下对应的CrawlDatum对象val
CrawlDatum val = values.next();
if (val.getStatus() == CrawlDatum.STATUS_INJECTED) { //如果是先前第一个任务中注入到临时文件tempDir中的种子url的话
injected.set(val); //保存到injected中
injected.setStatus(CrawlDatum.STATUS_DB_UNFETCHED); //初始化未爬取的状态
} else { //如果是CrawlDB中的url的话
old.set(val); //保存到old中
oldSet = true; // oldSet为true表示crawlDB存在于该url对应的CrawlDatum信息
}
}
CrawlDatum res = null;

//通过下面这个if-else语句,可以看出返回的结果是优先保存CrawlDB中存在的old,也就是说如果CrawlDB中存在当前url(key)对应的CrawlDatum对象,在爬取时,则优先根据当前url在CrawlDB中存在的CrawlDatum对象信息来对该url进行相关处理(是爬取还是不爬取),即使种子url中也包括当前url。
if (oldSet) res = old; // don't overwrite existing value
else res = injected;

output.collect(key, res);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值