Nutch中使用自定义中文分词器
Nutch对中文查询时默认采用的分词器为NutchAnalyzer,对中文默认采用单字切分.这种效果不是很理想,我们可以自定义切词器,以实现对中文支持.
通常可以采用的两种方式添加对中文的支持:
1.采用插件的方式,不修改系统代码的基础上,编写中文分词插件实现对中文分词的支持。
2.直接修改nutch的系统代码,对默认的分词器代码进行修改使其使用自定义中文分词程序.
1.插件方式
1.1实现思路
通过nutch插件机制实现。根据nutch提供的NutchAnalyzer扩展点,实现自定义的扩展。在插件中实现自定义的中文分词逻辑。
1.2插件流程
Nutch根据不同的语言使用不同的分词器来分词。Nutch如何判定现在分析的网页是那个语言的网页?Nutch使用一个外部Plugin:language-identifier来确定当前分析的网页是那种语言的网页。然后在当前的Document中放入一个名为lang的Field。在进行索引创建和搜索的时候,都会根据lang选择对应语言的Analyzer,如果没有lang的信息,那么Nutch会使用默认的Analyzer对Document建立索引。通过这样的过程就实现了Nutch使用不同的分词器分析不同的网页。
NutchAnalyzer analyzer = factory.get(doc.get("lang"));
Factory 为AnalyzerFactory会根据不同的语言获取切词器而这里的lang属性则通过 Languageidentifier来实现。
1.3实现步骤
1.3.1建立插件类
public class MyAnalyzer extends NutchAnalyzer{
private final static Analyzer ANALYZER = new SimpleAnalyzer();
public static final Log LOG = LogFactory.getLog(MyAnalyzer.class);
@Override
public TokenStream tokenStream(String fieldName, Reader reader) {
System.out.println("调用了自已写的中文分词插件....");
return ANALYZER.tokenStream(fieldName, reader);
}
}
这里为了演示方便只是简单的调用了lucene中的SimpleAnalyzer分词器的分词方法。
1.3.2建立插件配置文件:plugin.xml
<plugin id="MyAnalyzer-zh" name="Analyzer_self"
version="1.0.0" provider-name="nutch.org">
<runtime>
<library name="MyAnalyzer-zh.Jar"><export name="*"/></library>
</runtime>
<requires>
<import plugin="nutch-extensionpoints"/>
</requires>
<extension id="com.gpower.nutch.plugin.MyAnalyzer"
name="Self Nutch Plugin Analyzer"
point="org.apache.nutch.analysis.NutchAnalyzer">
<implementation id="myAnalyzer_"
class="com.gpower.nutch.plugin.MyAnalyzer">
<parameter name="lang" value="zh"/>
</implementation>
</extension>
</plugin>
1.3.3修改nutch配置信息 :nutch-site.xml(加入新建立的插件)
<property>
<name>plugin.includes</name> <value>protocol-http|urlfilter-regex|parse-(text|html|js|tika)|index-(basic|anchor)|query-(basic|site|url)|response-(json|xml)|summary-basic|scoring-opic|urlnormalizer-(pass|regex|basic)|MyAnalyzer-zh</value>
<description>……</description>
</property>
1.3.4将插件打包成jar文件,与plugin.xml文件一并放入plugins/下,如图:
1.3.5调用流程分析
我们先来看看nutch中是如何调用获得分词插件的。
AnalyzerFactory类中有以下几个相关方法:
public NutchAnalyzer get(String lang) {
NutchAnalyzer analyzer = DEFAULT_ANALYZER;//获得nutch默认的分词器
Extension extension = getExtension(lang);//根据参数获得相应扩展
if (extension != null) {
try {
//获得扩展的实例,也就是我们实现的那个插件了。
analyzer = (NutchAnalyzer) extension.getExtensionInstance();
} catch (PluginRuntimeException pre) {
analyzer = DEFAULT_ANALYZER;
}
}
return analyzer;
}
private Extension getExtension(String lang) {
ObjectCache objectCache = ObjectCache.get(conf);
if (lang == null) { return null; }
Extension extension = (Extension) objectCache.getObject(lang);//先去缓存里找相应扩展,如果有则直接从缓存返回。
if (extension == null) {
extension = findExtension(lang);
if (extension != null) {
objectCache.setObject(lang, extension);
}
}
return extension;
}
private Extension findExtension(String lang) {
if (lang != null) {
Extension[] extensions = this.extensionPoint.getExtensions();//获得该扩展点(NutchAnalyzer)下所有扩展
for (int i=0; i<extensions.length; i++) {
if (lang.equals(extensions[i].getAttribute("lang"))) {//该 处getAttribute的值就是plugin.xml中扩展点配置的lang属性
return extensions[i];
}
}
}
return null;
}
以上就完成了一个自定义中文分词的插件,运行nutch后就应该使用的是笔者自定义的中文分词插件了,但经过笔者测试,这时的中文分词插件并没有起作用。什么原因呢?
原来调用get(lang)方法时,lang的值为空,所以就自动使用还是nutch默认的分词了,因此笔者又作了如下的改到,以建立索引时的调用为例:
LuceneWriter类:
public void write(NutchDocument doc) throws IOException {
final Document luceneDoc = createLuceneDoc(doc);
//change by me
String lang = null;
if(luceneDoc.get("lang")==null){
lang = "zh";
}
final NutchAnalyzer analyzer = analyzerFactory.get(lang);
// end
// final NutchAnalyzer analyzer = analyzerFactory.get(luceneDoc.get("lang"));
if (Indexer.LOG.isDebugEnabled()) {
Indexer.LOG.debug("Indexing [" + luceneDoc.get("url")
+ "] with analyzer " + analyzer + " (" + luceneDoc.get("lang")
+ ")");
}
writer.addDocument(luceneDoc, analyzer);
}
改成这样后程序可以按预测结果正常运行,如下图内容;具体原因大约和language-identifier插件对中文的支持有关吧。还有待后续的研究吧。不管怎样,总算是可以实现nutch中加入自定义中文分词插件了。
1.3.6测试结果
2.修改nutch默认分词器
Nutch 本身提供了一个默认的分词器:NutchDocumentAnalyzer。我们可通过修改该类的具体实现来改用我们自定义的分词器,部分代码如下:
private static Analyzer MY_ANALYZER;
public NutchDocumentAnalyzer(Configuration conf) {
//
MY_ANALYZER = new MyAnalyzer();
}
public TokenStream tokenStream(String fieldName, Reader reader) {
Analyzer analyzer;
/*
if ("anchor".equals(fieldName))
analyzer = ANCHOR_ANALYZER;
else
analyzer = CONTENT_ANALYZER;
*/
analyzer = MY_ANALYZER;
return analyzer.tokenStream(fieldName, reader);
}
说白了就是将默认分词器换成我们自定义的分词器调用就可以了。