replace the default Tokenizer of carrot2 lingo filter with ICTCLAS |
nutch (0.7 release) 中的clustering plugin 采用的是Dawid Weiss 主持开发的carrot2,主要用到了其中的local controller 和 lingo filter 两个 compenent。lingo filter 缺省的Tokenizer (com.stachoodev.carrot.filter.lingo.tokenizer.Tokenizer)缺乏对中文的分词处理,这使得用这个filter 处理中文结果时效果往往不很让人满意!于是开始为nutch 选择合适的中文分词模块,经过一周左右的挑选、分析和比较,最终决定采用中国科学院计算技术研究所汉语词法分析系统——ICTCLAS。
接下来用了一周多的学习和练习:
经过一周多的时间终于用ICTCLAS 调换掉了lingo filter 缺省的 Tokenizer (com.stachoodev.carrot.filter.lingo.tokenizer.Tokenizer) :*)
nutch (0.7 release) 中carrot2 的版本略低于carrot2 官方网站对外公布的版本。
swith on the clustering plugin of nutch |
the following works for the 0.7 release
edit $NUTCH_HOME/WEB-INF/classes/nutch-default.xml
<property> <name>plugin.includes</name> <value>clustering-carrot2|protocol-httpclient|urlfilter-regex|parse-(text|html|js)|index-basic|query-(basic|site|url)</value> ... </property>
<property> <name>extension.clustering.extension-name</name> <value>Carrot2-Lingo</value> ... </property>
Carrot2-Lingo: see $NUTCH_HOME/plugins/clustering-carrot2/plugin.xml for detail.
Now, it‘s ok! add an parameter ‘clustering=yes‘ onto your search url and try it.
hack NutchAnalysis.jj further so as to perform third-part segmentation |
下面是我想到的一个蹩脚方法,之所以说这个方法蹩脚是因为它在执行切分过程中产生了 sizeof(tokens[])次input_stream.backup(1) 操作和副作用:-(。但这个方法也还是有些可取之处的——把不同语言的切分工作交给不同的Tokenizer 去处理,这样可以产生比较好的细粒度切分效果。
org.apache.nutch.analysis.NutchAnalysis.jj
@@ -33,6 +33,7 @@import org.apache.nutch.searcher.Query.Clause;import org.apache.lucene.analysis.StopFilter;+import org.apache.lucene.analysis.cjk.CJKTokenizer;import java.io.*;import java.util.*;@@ -81,6 +82,14 @@PARSER_END(NutchAnalysis)TOKEN_MGR_DECLS : {+ /** use CJKTokenizer to process cjk character */+ private CJKTokenizer cjkTokenizer = null;++ /** a global cjk token */+ private org.apache.lucene.analysis.Token cjkToken = null;++ /** start offset of cjk sequence */+ private int cjkStartOffset = 0;/** Constructs a token manager for the provided Reader. */public NutchAnalysisTokenManager(Reader reader) {@@ -106,7 +115,46 @@}// chinese, japanese and korean characters-| <SIGRAM: <CJK> >+| <SIGRAM: (<CJK>)+ >+ {+ /**+ * use an instance of CJKTokenizer, cjkTokenizer, hold the maximum+ * matched cjk chars, and cjkToken for the current token;+ * reset matchedToken.image use cjkToken.termText();+ * reset matchedToken.beginColumn use cjkToken.startOffset();+ * reset matchedToken.endColumn use cjkToken.endOffset();+ * backup the last char when the next cjkToken is valid.+ */+ if(cjkTokenizer == null) {+ cjkTokenizer = new CJKTokenizer(new StringReader(image.toString()));+ cjkStartOffset = matchedToken.beginColumn;+ try {+ cjkToken = cjkTokenizer.next();+ } catch(IOException ioe) {+ cjkToken = null;+ }+ }++ if(cjkToken != null && !cjkToken.termText().equals("")) {+ //sometime the cjkTokenizer returns an empty string, is it a bug?+ matchedToken.image = cjkToken.termText();+ matchedToken.beginColumn = cjkStartOffset + cjkToken.startOffset();+ matchedToken.endColumn = cjkStartOffset + cjkToken.endOffset();+ try {+ cjkToken = cjkTokenizer.next();+ } catch(IOException ioe) {+ cjkToken = null;+ }+ if(cjkToken != null && !cjkToken.termText().equals("")) {+ input_stream.backup(1);+ }+ }++ if(cjkToken == null || cjkToken.termText().equals("")) {+ cjkTokenizer = null;+ cjkStartOffset = 0;+ }+ }
例子中所用的CJKTokenizer 取自Weblucene
关键词:nutch, segmentation, chinese, cjk
参考:
hack NutchAnalysis.jj so as to perform bi-gram segmentation |
109,110c109,112< | <SIGRAM: <CJK> ><---> | <SIGRAM: <CJK><CJK> >> {> input_stream.backup(1);> }
简单测试一下效果:
$ echo $LANG
en_US.UTF-8
$ LANG=zh_CN
$ ./bin/nutch org.apache.nutch.analysis.NutchDocumentTokenizerText: 克己服人,礼智谦让
Tokens: 克己 己服 服人 礼智 智谦 谦让
Text: 中华人民共和国
Tokens: 中华 华人 人民 民共 共和 和国
Text: 中文&English&英文
Tokens: 中文 &english& 英文
Text: 写这些内容的时候,作者运行的是nutch 的0.7 release;如果你运行的是nutch 的其他版本,那么下面的内容可能并不适合你:
Tokens: 写这 这些 些内 内容 容的 的时 时候 作者 者运 运行 行的 的是 nutch 0 7 release 如果 果你 你运 运行 行的 的是 nutch 的其 其他 他版 版本 那么 么下 下面 面的 的内 内容 容可 可能 能并 并不 不适 适合 合你
关键词: nutch, segmentation, chinese, cjk, bi-gram
参考:
understand the tokenizer of nutch |
注:写这些内容的时候,作者运行的是nutch 的0.7 release;如果你运行的是nutch 的其他版本,那么下面的内容可能并不适合你:*)
$ echo $LANG
en_US.UTF-8
$ LANG=zh_CN
$ ./bin/nutch org.apache.nutch.analysis.NutchDocumentTokenizerText: I.B.M.
Tokens: ibm
Text: I.B.M
Tokens: ib m
Text: AT&T
Tokens: at&t
Text: AT_T
Tokens: at_t
Text: 中华人民共和国
Tokens: 中 华 人 民 共 和 国
Text: 中文&English&英文
Tokens: 中 文 &english& 英 文
为什么会有这种切分效果呢?看看下面的 org.apache.nutch.analysis.NutchAnalysis.jj 文件片断也许就能明白了:
TOKEN : {// token regular expressions// basic word -- lowercase it<WORD: ((<LETTER>|<DIGIT>|<WORD_PUNCT>)+ | <IRREGULAR_WORD>)> { matchedToken.image = matchedToken.image.toLowerCase(); }// special handling for acronyms: U.S.A., I.B.M., etc: dots are removed| <ACRONYM: <LETTER> "." (<LETTER> ".")+ > {// remove dots for (int i = 0; i < image.length(); i++) { if (image.charAt(i) == ‘.‘) image.deleteCharAt(i--); } matchedToken.image = image.toString().toLowerCase(); }// chinese, japanese and korean characters| <SIGRAM: <CJK> >// irregular words| <#IRREGULAR_WORD:(<C_PLUS_PLUS>|<C_SHARP>)>......
当前的tokenizer 把单个的CJK 字符当成了一个完整的Token—— 并未对连续的CJK 字符做切分词处理,于是就出现了上面的结果。
crawl-urlfilter.txt vs. regex-urlfilter.txt in nutch |
注:写这些内容的时候,作者运行的是nutch 的0.7 release;如果你运行的是nutch 的其他版本,那么你可能需要自己去分析二者的区别:*)
首先有一种说法是:crawl-urlfilter.txt 是供intranet 抓取用的,而regex-urlfilter.txt 是供internet 抓取用的——因为两者抓取的重点不同,从而导致过滤规则不同。如果你只是想知道两者的简单区别的话,那么看到这里也就可以了,但是如果你想知道更多细节那么下面的内容还是值得一看的:)
crawl-urlfilter.txt 和 regex-urlfilter.txt 都是用来保存过滤url 的正则表达式的。类 RegexURLFilter(org.apache.nutch.net.RegexURLFilter) 通过 NutchConf.get().get("urlfilter.regex.file") 来从中(当然如果你装载了其他配置文件的话,也可能有其他的候选者)进行选择。
regex-urlfilter.txt 是 urlfilter.regex.file 属性的缺省值(1),定义在 $NUTCH_JAVA_HOME/conf/nutch-default.xml(如果你设置了环境变量NUTCH_CONF_DIR 的话,那就是 $NUTCH_CONF_DIR/nutch-default.xml)中。该值可以被后续加载的配置文件所覆盖,例如如果你装载了类 CrawlTool(org.apache.nutch.tool.CrawlTool),那么缺省值就会被 crawl-tool.xml 中的urlfilter.regex.file 属性覆盖(2)。也就是说,如果你在操作过程中调用了类CrawlTool,那么类RegexURLFilter 将会采用crawl-tool.xml 中指定的文件,否则就用缺省的文件。
当然如果nutch-site.xml 中也定义了urlfilter.regex.file 属性的话,那么 NutchConf.get().get("urlfilter.regex.file") 返回的值就以nutch-site.xml 所指定的值为准。(关于nutch 的resource chain)
注:(1)NutchConf 初始化时会装载nutch-default.xml 和nutch-site.xml
public class NutchConf {...... public NutchConf() { resourceNames.add("nutch-default.xml"); resourceNames.add("nutch-site.xml"); }......}
(2)装载类CrawlTool 时,crawl-tool.xml 会被插入到resource chain 中public class CrawlTool {...... static { NutchConf.get().addConfResource("crawl-tool.xml"); }......}
参见: hack URLFilters so as to test RegexURLFilter |
public static void main(String args[]) throws IOException, MalformedPatternException, Exception { BufferedReader in=new BufferedReader(new InputStreamReader(System.in)); String line; while((line=in.readLine())!=null) { String out=URLFilters.filter(line); if(out!=null) { System.out.print("+"); System.out.println(out); } else { System.out.print("-"); System.out.println(line); } }}
这样测试url filter rule 就方便多了:)$ ./bin/nutch org.apache.nutch.net.URLFilters......http://www.lhelper.org/blog/using fitler 0:org.apache.nutch.net.RegexURLFilter+http://www.lhelper.org/blog/http://www.lhelper.org/blog/?q=lhelperusing fitler 0:org.apache.nutch.net.RegexURLFilter-http://www.lhelper.org/blog/?q=lhelper
联系客服