《解密搜索引擎技术实战:Lucene&Java精华版》是猎兔搜索开发团队的软件研发和教学实践的经验汇总。
《解密搜索引擎技术实战:Lucene&Java精华版》总结搜索引擎相关理论与实际解决方案,并给出了Java实现,其中利用了流行的开源项目Lucene和Solr,而且还包括原创的实现。
《解密搜索引擎技术实战:Lucene&Java精华版》主要包括总体介绍部分、爬虫部分、自然语言处理部分、全文检索部分以及相关案例分析。爬虫部分介绍了网页遍历方法和如何实现增量抓取,并介绍了从网页等各种格式的文档中提取主要内容的方法。自然语言处理部分从统计机器学习的原理出发,包括了中文分词与词性标注的理论与实现以及在搜索引擎中的实用等细节,同时对文档排重、文本分类、自动聚类、句法分析树、拼写检查等自然语言处理领域的经典问题进行了深入浅出的介绍并总结了实现方法。
在全文检索部分,结合Lucene 3.0介绍了搜索引擎的原理与进展。用简单的例子介绍了Lucene的最新应用方法。包括完整的搜索实现过程:从完成索引到搜索用户界面的实现。《《解密搜索引擎技术实战:Lucene&Java精华版》》还进一步介绍了实现准实时搜索的方法,展示了Solr 1.4版本的用法以及实现分布式搜索服务集群的方法。最后介绍了在地理信息系统领域和户外活动搜索领域的应用。
有个经典笑话:护士看到病人在病房喝酒,就走过去小声叮嘱说:小心肝!病人微笑道:小宝贝。在这里,“小心肝!”这句话有歧义,从护士的角度理解是“小心/肝”,在病人的角度理解是“小心肝”。如果使用中文分词切分成“小心/肝”则可以消除这种歧义。
因为中文文本中词和词之间不像英文一样存在边界,所以中文分词是一个专业处理中文信息的搜索引擎首先面对的问题。英语、法语和德语等西方语言通常采用空格或标点符号将词隔开,具有天然的分隔符,所以词的获取比较简单。但是中文、日文和韩文等东方语言,虽然句子之间有分隔符,但词与词之间没有分隔符,所以需要靠程序切分出词。另外,除了可以用于全文查找,中文分词的方法也被应用到英语手写体识别中。因为在识别手写体时,单词之间的空格就不很清楚了。
要解决中文分词准确度的问题,是否提供一个免费版本的分词程序供人下载使用就够了?而像分词这样的自然语言处理领域的问题,很难彻底解决。例如,通用版本的分词也许需要做很多修改后才能用到手机上。所以需要让人能看懂其中的代码与实现原理,并参与到改进的过程中才能更好地应用。
本章的中文分词和下章介绍的文档排重和关键词提取等技术都属于自然语言处理技术的范围。因为在中文信息处理领域,中文分词一直是一个值得专门研究的问题,所以单独作为一章。
4.1 Lucene中的中文分词
Lucene中处理中文的常用方法有三种。以“咬死猎人的狗”这句话的输出结果为例:
单字方式:[咬] [死] [猎] [人] [的] [狗];
二元覆盖的方式:[咬死] [死猎] [猎人] [人的] [的狗];
分词的方式:[咬] [死] [猎人] [的] [狗]。
Lucene中的StandardTokenizer采用了单字分词的方式。 CJKTokenizer采用了二元覆盖的实现方式。笔者开发的CnTokenizer采用了分词的方式,本章将介绍部分实现方法。
4.1.1 Lucene切分原理
Lucene中负责语言处理的部分在org.apache.lucene.analysis包。其中TokenStream类用来进行基本的分词工作,Analyzer类是TokenStream的外围包装类,负责整个解析工作。有人把文本解析比喻成人体的消化过程,输入食物,分解出有用的氨基酸和葡萄糖等。Analyzer类接收的是整段文本,解析出有意义的词语。
通常不需要直接调用分词的处理类analysis,而是由Lucene内部来调用,其中:
在做索引阶段,调用addDocument(doc)时,Lucene内部使用Analyzer来处理每个需要索引的列
IndexWriter index = new IndexWriter(indexDirectory,
new CnAnalyzer(), //用支持分词的分析器
!incremental,
IndexWriter.MaxFieldLength.UNLIMITED);
在搜索阶段,调用QueryParser.parse(queryText)来解析查询串时,QueryParser会调用Analyzer来拆分查询字符串,但是对于通配符等查询不会调用Analyzer。
Analyzer analyzer = new CnAnalyzer(); //支持中文的分词
QueryParser parser = new QueryParser(Version.LUCENE_CURRENT,""title"",
analyzer);
因为在索引和搜索阶段都调用了分词过程,索引和搜索的切分处理要尽量一致,所以分词效果改变后需要重建索引。另外,可以用个速度快的版本,用来在搜索阶段切分用户的查询词,另外用一个准确切分的慢速版本用在索引阶段的分词。
为了测试Lucene的切分效果,下面是直接调用Analysis的例子:
Analyzer analyzer = new CnAnalyzer(); //创建一个中文分析器
//取得Token流
TokenStream ts = analyzer.tokenStream(""myfield"",new StringReader
(""待切分文本""));
while (ts.incrementToken()) {//取得下一个词
System.out.println(""token: ""+ts));
}
……