搜索引擎分词:Nutch整合Paoding中文分词步骤详解

来源:互联网 发布:淘宝华黎 编辑:程序博客网 时间:2024/05/01 05:18

发表时间:2010-6-12 22:40:32  发表者:杨国松


搜索引擎+B2B平台+SNS网站=?, 一个三不像网站。偏偏投资人需要这样一个三不像网站。从4月份开始组建团队。时间一瞬2个月过去了。做B2B需要的就是大工作量和时间,而做搜索引擎光分词这块就搞的头大。在此结合开源数据写个一二,已备后用。

搜索引擎需要的就是数据,抓取海量数据、然后存储、分析、建立索引、计算,最终根据用户需求快速检索出结果。存储分析和建立索引的过程开源项目中有个Hadoop是不二之选。这里分析的不是hadoop,而是搜索引擎中最后一步,也是至关重要的一步:中文分词。因为程序员基本是写java为主,故在考虑搜索引擎时,就采用了搜索领域影响颇深的基于纯java语言开发的Nutch搜索引擎。搜索引擎处理英文文档时,几乎不需要特殊的加工处理,英文文档本身就是以词为单位的组织,词个词之间是靠空格或标点符号显式地表示词的边界。我们采用的庖丁解牛正是为中文的分词提供了技术基础。首先,用wget下载了细胞词库,哇,太棒了,数据大的惊人。加上paoding自带的22M词库,基本可以满足我们词典库。

第一步是建立两个文件,在nutch目录的src/plugin目录下建立两个文件夹分别命名为analysis-zh,lib-paoding-analyzers。然后在这两个文件夹下建立plugin.xml和build.xml。
analysis-zh文件夹下plugin.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<plugin id="analysis-zh" name="Chinese Analysis Plug-in" version="1.0.0"
provider-name="net.paoding.analysis">
<runtime>
<library name="analysis-zh.jar">
<export name="*"/>
</library>
</runtime>
<requires>
<import plugin="nutch-extensionpoints"/>
<import plugin="lib-paoding-analyzers"/>
</requires>
<extension id="org.apache.nutch.analysis.zh"
name="Chinese Analyzer"
point="org.apache.nutch.analysis.NutchAnalyzer">
<implementation id="ChineseAnalyzer"
class="org.apache.nutch.analysis.zh.ChineseAnalyzer">
<parameter name="lang" value="zh"/>
</implementation>
</extension>
</plugin>

analysis-zh文件夹下build.xml的内容如下:
<project name="analysis-zh" default="jar-core">
<import file="../build-plugin.xml"/>
<!-- Build compilation dependencies -->
<target name="deps-jar">
<ant target="jar" inheritall="false" dir="../lib-paoding-analyzers"/>
</target>
<!-- Add compilation dependencies to classpath -->
<path id="plugin.deps">
<fileset dir="${nutch.root}/build">
<include name="**/lib-paoding-analyzers/*.jar" />
</fileset>
</path>
</project>


在analysis-zh文件夹依次建立如下文件夹:
src/java/org/apache/nutch/analysis/zh
在zh文件夹下建立java文件ChineseAnalyzer.java。其内容如下:
/**
* Paoding chinese analyzer
*/
package org.apache.nutch.analysis.zh;
// JDK imports
import java.io.Reader;
// Lucene imports
import net.paoding.analysis.analyzer.PaodingAnalyzer;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
// Nutch imports
import org.apache.nutch.analysis.NutchAnalyzer;
/**
* A simple Chinese Analyzer that wraps the Lucene one.
* @author
*/
public class ChineseAnalyzer extends NutchAnalyzer {
private final static Analyzer ANALYZER = new PaodingAnalyzer();
/** Creates a new instance of ChineseAnalyzer */
public ChineseAnalyzer() { }

public TokenStream tokenStream(String fieldName, Reader reader)
{
return ANALYZER.tokenStream(fieldName, reader);
}
}
lib-paoding-analyzers文件夹下plugin.xml的内容如下:
<plugin
id="lib-paoding-analyzers"
name="paoding Analysers"
version="2.4.0"
provider-name="net.paoding">

<runtime>
<library name="lib-paoding-analyzers.jar">
<export name="*"/>
</library>
</runtime>

</plugin>
lib-paoding-analyzers文件夹下build.xml内容如下:
<?xml version="1.0"?>
<project name="lib-paoding-analyzers" default="jar">

<import file="../build-plugin.xml"/>

<!--
! Override the compile and jar targets,
! since there is nothing to compile here.
! -->
<target name="compile" depends="init"/>

<target name="jar" depends="compile">
<copy todir="${build.dir}" verbose="true">
<fileset dir="./lib" includes="**/*.jar"/>
</copy>
</target>

</project>
然后在lib-paoding-analyzers下建立lib文件,把paoding目录下的paoding-analysis.jar放在该lib下。

接着配置nutch-1.0/conf下的nutch-site.xml文件
加上如下内容:
<property>
<name>plugin.includes</name>
<value>protocol-http|urlfilter-regex|parse-(text|html|js)|analysis-(zh)|index-basic|query-(basic|site|url)|summary-basic|scoring-opic|urlnormalizer-(pass|regex|basic)</value>
<description>
</description>
</property>

作用是让nutch能加载到我们即将生成的analysis-zh.jar包。

第三步:修改Nutch源码
修改org.apache.nutch.analysis.NutchDocumentAnalyzer,
在private static Analyzer CONTENT_ANALYZER;下加上private static Analyzer PAODING_ANALYZER;
把public NutchDocumentAnalyzer(Configuration conf) {
this.conf = conf;
CONTENT_ANALYZER = new ContentAnalyzer(conf);
ANCHOR_ANALYZER = new AnchorAnalyzer();
}
改成
public NutchDocumentAnalyzer(Configuration conf) {
this.conf = conf;
CONTENT_ANALYZER = new ContentAnalyzer(conf);
ANCHOR_ANALYZER = new AnchorAnalyzer();
PAODING_ANALYZER=new PaodingAnalyzer();
}
把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);
}
改成 public TokenStream tokenStream(String fieldName, Reader reader){
Analyzer analyzer;
/* if ("anchor".equals(fieldName))
analyzer = ANCHOR_ANALYZER;
else
analyzer = CONTENT_ANALYZER; */
analyzer=PAODING_ANALYZER;
return analyzer.tokenStream(fieldName, reader);
}

修改NutchAnalysis.jj,把130行的| <SIGRAM: <CJK> >修改成| <SIGRAM: (<CJK>)+ >
修改org.apache.nutch.indexer.lucene.LuceneWriter
把public void write(NutchDocument doc) throws IOException {
final Document luceneDoc = createLuceneDoc(doc);
........
}
改成public void write(NutchDocument doc) throws IOException {
final Document luceneDoc = createLuceneDoc(doc);
String lang = null;
if(luceneDoc.get("lang") == null) {
lang = "zh";
}
final NutchAnalyzer analyzer = analyzerFactory.get(lang);
if (Indexer.LOG.isDebugEnabled()) {
Indexer.LOG.debug("Indexing [" + luceneDoc.get("url")
+ "] with analyzer " + analyzer + " (" + luceneDoc.get("lang")
+ ")");
}
writer.addDocument(luceneDoc, analyzer);

}
修改org.apache.nutch.analysis.NutchAnalysis,加上方法:
final public Query parseByLucene(Configuration conf) throws ParseException {

Query query = new Query(conf);

if (queryString.length() > 0) {
// lucene分词
org.apache.lucene.queryParser.QueryParser parserLucene =
new org.apache.lucene.queryParser.QueryParser("", analyzer);
org.apache.lucene.search.Query q = null;
try {
q = parserLucene.parse(queryString);
} catch (org.apache.lucene.queryParser.ParseException e) {
e.printStackTrace();
}

String termStrings = q.toString();
if (termStrings.indexOf(" ") > -1)
termStrings = termStrings.substring(1, termStrings.length()-1);
String[] terms = termStrings.split(" ");

for (int i = 0; i < terms.length; i++) {
String[] tems = {terms[i]};
query.addRequiredPhrase(tems, Clause.DEFAULT_FIELD);
}
}

return query;
}
然后修改这个类的方法blic static Query parseQuery(){}里面的return parser.parse(conf); 修改为return parser.parseByLucene(conf);

第四步,修改Nutch的build.xml文件
修改根目录的build.xml,在target war的<lib> </lib>里加上
<include name="paoding-analysis.jar"/>
修改150行处的<target name="job" depends="compile">,改为<target name="job" depends="compile,war"> 这样编译后能自动在bulid文件夹下生成nutch -1.0.job,nutch-1.0.war,nutch-1.0.jar文件了。
修改nutch-1.0/src/plugin下的build.xml文件,加上
<target name="deploy">
<ant dir="analysis-zh" target="deploy"/>
<ant dir="lib-paoding-analyzers" target="deploy"/>
.......
</target>

<target name="clean">
<ant dir="analysis-zh" target="clean"/>
<ant dir="lib-paoding-analyzers" target="clean"/>
.......
</target>

最后就可以用 ant进行编译了,编译后的结果测试:http://www.huisou.com
编译后把/nutch-1.0/build下出现的analysis-zh和lib-paoding-analyzers两文件夹复制到/nutch-1.0/plugins下面,删掉里面的classes和test文件。并把/nutch-1.0/src/plugin下相同文件夹下的plugin.xml复制过去。在用ant编译一次。把/nutch-1.0/build下的nutch -1.0.job,nutch-1.0.war,nutch-1.0.jar复制到nutch的根目录下替代原来的文件。

用Nutch抓取来的1T数据,通过hadoop的MapReduce进行存储实现,然后建立倒索引,配置Tomcat,就可以体验分词带来的搜索效果了。

山寨级别的搜索引擎自然不能与Google、百度相提并论。在搜索引擎实现的每一个步骤(数据抓取、存储、索引、分词系统、分布式查询等)每一个都是巨大的课题,值得我们去深入研究。同时欢迎更多的搜索引擎互联网爱好者加入我们的团队,一起学习进步。
诚寻人才加盟:http://search.51job.com/job/42244754,c.html