?
将nutch-1.2导入到eclipse中运行
以手动的方式将nutch的源代码导入eclipse中,可以使目录更清晰。具体步骤如下:
1.在eclipse中新建立一个Java Project. 名字自己定义(Nutch)。选择"Create New project in WorkSpace".点击完成。
2.将解压后的nutch目录下的\src\java\中的代码全部复制到新建工程中的src下。
将解压后的nutch目录下的lib、plugins、conf三个文件夹复制到新建工程的根目录下(与src同级)
3.右键工程properties, 切换到"Libraries"选择"Add Class Folder..." 按钮,从列表中选择"conf"。将 conf加入到classpath中。点击Add External JARs
选择工程里lib下的所有jar包,打开。
4.修改配置
?
在nutch的安装目录下建立一个名为urls的文件夹(与src同级),并在文件夹下新建url.txt文件,在文件中写入:http://www.sohu.com/(即要抓取网站的网址,注意最后要加斜杠)。
?
设置网站过滤规则。
编辑conf/crawl-urlfilter.txt文件,修改MY.DOMAIN.NAME部分。
将
# accept hosts in MY.DOMAIN.NAME
+^http://([a-z0-9]*\.)*MY.DOMAIN.NAME/
改为:
# accept hosts in MY.DOMAIN.NAME
+^http://([a-z0-9]*\.)*sohu.com/
?
设置代理信息。
编辑conf/nutch-site.xml文件。在<configuration>和</configuration>之间添加如下内容:
<property>
<name>http.agent.name</name>
<value>http://www.sohu.com/</value>
</property>
<property>
<name>http.agent.url</name>
<value>http://www.sohu.com/</value>
</property>
<property>
<name>http.robots.agents</name>
<value>http://www.sohu.com/</value>
</property>
?
设置代理名
编辑nutch-1.2\conf\nutch-default.xml文件,找<name>http.agent.name</name>,然后随便设置Value值。
<property>
<name>plugin.folders</name>
<value> ./plugins</value><!—这里路径变了时间-->
要成功运行程序cygwin必须在电脑上已经成功安装,关于cygwin的安装上“Win7环境下配置nutch-1.2”已经详细讲述,这里不再赘述。
?
右键单击项目debug as→debug configurations→在左侧选中New_configuration→在arguments中Program arguments 填写如下参数crawl urls -dir crawled -depth 4 -threads 3 -topN 30? 为防止内存溢出在VM arguments中填写 -Xms800m -Xmx800m→debug 程序开始运行(爬虫爬取)
控制台出现crawl finished: crawled后
在nutch项目里就会生成一个文件夹crawled,爬取完成,索引建立成功
?
为nutch添加中文分词功能(IKAnalyzer中文分词)
到此并没有大功告成,因为原版nutch自身所带分词只支持英文分词,对中文的处理是按字划分,而不是按词划分,我们可以为其添加一些中文分词插件,例如IKAnalyzer、庖丁分词等。
首先作以下几点说明,以方便理解:
(1)目前,Nutch中文分词方式大致有两种方式:
一是修改源代码:这种方式是直接对Nutch分词处理类进行修改,调用已写好的一些分词组件进行分词。
二是编写分词插件。这种方式是按照Nutch定义的插件编写规则重新编写或者添加中文分词插件。
(2)NutchAnalysis.jj文件用于搜索时;NutchDocumentAnalyzer.java 用于索引时。
(3)用到了javacc、ant两种工具。javacc用于编译NutchAnalysis.jj文件,该文件最好拷贝到其它目录编译,编译后再把生成的7个文件拷贝回原目录,如果在原目录下编译的话只会生成4个文件。
(4)build.xml文件是ant的配置文件
(5)ant、javacc工具的使用方法类似,解压后把bin目录的路径加到系统目录(path)中,重启计算机即可。详看:Ant、Javacc 等安装与使用
?
?
?
?
?
下面是具体过程:
一、准备工作:
把IKAnalyzer3.2.8.jar文件拷入nutch/lib目录下,并加到classpath里
二、代码修改:
1. NutchAnalysis.jj
在nutch/src/java/org/apache/nutch/analysis目录
在该文件里找到 | <SIGRAM: <CJK> >,这代表按字划分,修改为 | <SIGRAM: (<CJK>)+ >
用javacc工具生成NutchAnalysis.jj的源代码,将生成的所有java源代码(7个文件)全部覆盖到 src/java/org/apache/nutch/analysis包下.
javacc的使用方法:cmd进入命令行,切换到NutchAnalysis.jj所在目录(最后把它拷贝到其它目录进行编译,如D盘),D:,输入命令
javacc NutchAnalysis.jj
就会生成7个文件了。
?
2.修改 NutchAnalysis.java
在org/apache/nutch/analysis下
(1)在import区域加入下面的代码
import org.wltea.analyzer.lucene.IKTokenizer;
(2)在两个位置加入ParseException异常捕捉命令,否则ant时会提示。下面的代码已经添加
public static Query parseQuery(String queryString, Configuration conf) throws IOException,ParseException {
??? return parseQuery(queryString, null, conf);
? }
public static Query parseQuery(String queryString, Analyzer analyzer, Configuration conf)
??? throws IOException,ParseException {
??? NutchAnalysis parser = new NutchAnalysis(
????????? queryString, (analyzer != null) ? analyzer : new NutchDocumentAnalyzer(conf));
??? parser.queryString = queryString;
??? parser.queryFilters = new QueryFilters(conf);
??? return parser.parse(conf);
? }
如果其他地方出现抛异常错误要及时catch
?
3. NutchDocumentAnalyzer.java
(1)引入IK包
import org.wltea.analyzer.lucene.IKAnalyzer;
import org.apache.lucene.analysis.tokenattributes.*;
(2)修改public TokenStream tokenStream(String fieldName, Reader reader) {函数为:
public TokenStream tokenStream(String fieldName, Reader reader) {
?? /* Analyzer analyzer;
??? if ("anchor".equals(fieldName))
????? analyzer = ANCHOR_ANALYZER;
??? else
????? analyzer = CONTENT_ANALYZER;
??? return analyzer.tokenStream(fieldName, reader);
??? */
?? ?Analyzer analyzer = new org.wltea.analyzer.lucene.IKAnalyzer();
????? TokenStream tokenStream = analyzer.tokenStream(fieldName, reader);
????? tokenStream.addAttribute(TypeAttribute.class);
????? tokenStream.addAttribute(FlagsAttribute.class);
????? tokenStream.addAttribute(PayloadAttribute.class);
????? tokenStream.addAttribute(PositionIncrementAttribute.class);
?? return tokenStream;
? }
?
如果要将nutch源文件夹下的nutch-1.2.war导入到eclipse下运行,进行搜索,则用eclipse重新将nutch生成jar包,替换nutch-1.2下的nutch-1.2.jar,并将IKAnalyzer3.2.8.jar包放到其lib里加入classpath里。
?
到此离成功还有一步之遥,我们在搜索的时候会发现有些关键字无法正常搜索,那是因为nutch建立索引的时候出现了词元交叉充电的原因,我们要解决这一问题。
?
由于Nutch不是原生支持中文的,开发者没有考虑到中文的分词会存在token的交叉重叠的情况,导致在根据用户输入查询串的token获取页面summary时出现:StringIndexOutOfBoundsException的异常。比如:“城市学院”可能出现这样的分词“城市学院”、“城市”、“学院”,这几个token就交叉重叠了。
?
错误重现
java.lang.RuntimeException:java.util.concurrent.ExecutionException: java.lang.StringIndexOutOfBoundsException: String index out of range: -4 at org.apache.nutch.searcher.FetchedSegments.getSummary(FetchedSegments.java:316)
at org.apache.nutch.searcher.NutchBean.getSummary(NutchBean.java:357)
at org.apache.nutch.searcher.NutchBean.main(NutchBean.java:429)
Caused by: java.util.concurrent.ExecutionException: java.lang.StringIndexOutOfBoundsException: String index out of range: -4
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at org.apache.nutch.searcher.FetchedSegments.getSummary(FetchedSegments.java:311)
... 2 more
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -4
at java.lang.String.substring(String.java:1937)
at org.apache.nutch.summary.basic.BasicSummarizer.getSummary(BasicSummarizer.java:190)
at org.apache.nutch.searcher.FetchedSegments.getSummary(FetchedSegments.java:275)
at org.apache.nutch.searcher.FetchedSegments$SummaryTask.call(FetchedSegments.java:65)
at org.apache.nutch.searcher.FetchedSegments$SummaryTask.call(FetchedSegments.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
?
分析:
从错误日志中可以跟踪到错误的根源为
org.apache.nutch.summary.basic.BasicSummarizer.getSummary
也就是
nutch/src/plugin/summary-basic/src/java/org/apache/nutch/summary/basic/BasicSummarizer.java
文件中的188行开始的如下代码:
if (highlight.contains(t.term())) {
excerpt.addToken(t.term());
//在连个token重叠的情况下,会使得offset>t.startOffset()
excerpt.add(newFragment(text.substring(offset, t.startOffset())));//这就是异常的地方当offset>t.startOffset()就会出错。
excerpt.add(newHighlight(text.substring(t.startOffset(),t.endOffset())));
offset = t.endOffset();
endToken = Math.min(j +sumContext, tokens.length);
}
所以把代码修改为:(可以将while部分全部修改,也可以改部分)
while ((j < endToken) && (j - startToken < sumLength)) {
Token t = tokens[j];
if (highlight.contains(t.term())) {
excerpt.addToken(t.term());
if(offset<t.startOffset()){
excerpt.add(new Fragment(text.substring(offset, t.startOffset())));
excerpt.add(new Highlight(text.substring(t.startOffset(),t.endOffset())));
}
if(offset>=t.startOffset()){
if(offset<t.endOffset()){
excerpt.add(new Highlight(text.substring(offset,t.endOffset())));
}
}
offset = Math.max(offset, t.endOffset());
endToken = Math.min(j + sumContext, tokens.length);
}
j++;
}
同时还要将下面的
if(j<tokes.length){
excerpt.add(new Fragment(text.subString(offset,tokens[j].endOffset())));
}
的修改为
if(j<tokes.length){
if(offset< tokens[j].endOffset()){
excerpt.add(new Fragment(text.subString(offset,tokens[j].endOffset())));
}
}
重新编译,在nutch/目录下运行ant,在nutch/build/summary-basic/目录下生成了
summary-basic.jar,把它复制到nutch/plugins/summary-basic/目录下覆盖原来的文件。不要忘记重新生成nutch-1.2jar文件替换搜索项目的同名文件,还要把生成的summary-basic.jar文件替换搜索项目里面的相同文件,不然会出现问题。
?
恭喜你,到此工作完成。我们自己的搜索引擎建立起来了,我们也可以在此基础上做开发。
?