基于Java的搜索引擎Nutch中文搜索技术

摘要:Nutch是一个优秀的基于Java的开放源码搜索引擎,为了使它能够支持中文搜索,本文在分析了Nutch结构的基础上,采用词表分词技术和前向匹配分词算法对中文信息进行分词,以JavaCC脚本实现上下文相关文法中文分析模块,成功实现了Nutch中文搜索功能。

关键词:搜索引擎分词正规式

前言

搜索引擎[1]是当今网络应用的核心问题,已经受到各企业和研究部门的广泛关注。Lucene和Nutch是针对国外英文系统环境的搜索引擎,本文在研究了中文分词技术和JavaCC技术的基础上,成功地实现了Lucene和Nucth的中文分析模块,使Lucene和Nucth能够实现中文信息检索。

2 Nutch分析

Lucene是开放源码的基于Java的全文检索引擎,其贡献者Doug Cutting是一位资深全文索引/检索专家。作为一个全文检索系统,在进行检索之前需要建立索引,索引的过程是先读取文章中的词语,然后一一存放在称为倒排索引文件的索引数据库(Index Database)中。索引数据库记录了词语出现的位置,频率等相关信息,以备后面读取。Nutch是Cutting创建的另一个Java开源项目,目的是提供全功能的搜索引擎,其底层借助了Lucene的部分功能,并且索引结构与Lucene兼容。Lucene和Nutch并没有规定数据源的格式,而只提供了一个通用的结构(Document对象)来接受索引的输入,因此输入的数据源可以是数据库、WORD文档、PDF文档和HTML文档,只要能够设计相应的解析转换器将数据源构造成Docuement对象即可进行索引。对于大批量的数据索引,还可以通过调整IndexerWrite的文件合并频率属性(MergeFactor)来提高批量索引的效率。用户输入查询字符串(Query String),然后经过分析器的分析,就会产生一个Query对象。真正搜索时,使用IndexSearcher类的search方法,它返回Hits对象。通过遍历Hits对象的所有文档(document),就可以找到所有被搜索到的文章(页面)。查询字符串的语法定义为:Query ::= ( Clause )*Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")")中间的逻辑包括:and or + - &&||等符号,而且还有"短语查询"和针对西文的前缀/模糊查询等。总的来说,这是其他很多搜索引擎都不具备的功能。通过修改QueryParser的语法生成脚本,还可以修改或扩展查询分析器的功能,使它更加适用于中文环境。所有的问题都通过一个额外抽象层来方便以后的扩展和重用,通过重新实现来达到自己的目的,而对其他模块而不需要。可以简单的应用入口Searcher, Indexer,并调用底层一系列组件协同的完成搜索任务。所有的对象的任务都非常专一,比如搜索过程QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构IndexReader进行索引的读取,并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高,因此可以通过重新实现而不需要修改其他模块。除了灵活的应用接口设计,Lucene和Nutch还提供了一些适合大多数应用的语言分析器实现(SimpleAnalyser,StandardAnalyser),这也是新用户能够很快上手的重要原因之一。 

3 Nutch中文搜索

3.1 中文分词

在搜索引擎和各种语言处理的需要中,分词可以说是最基本的操作。汉语句子是由词语组成的,人们在使用汉语时,可以直接理解并使用它。对于计算机,是不可能达到人类的智能的,也不能理解人类语言。但是,由于人类仍然希望计算机能理解人类的语言,并且迫切的希望使用在各种商业和技术领域中,因此提出了计算机形式文法[2]。但是现有形式文法是建立在事先分词的基础上的。对于某些语言,单词之间有特定的符号隔开(一般是空格),所以没有任何分词的困难。而汉语与其他语言都有很大的不同,汉字之间没有空格。如果想继续沿用西方的形式文法理论处理汉语,那么必然涉及到中文分词问题[3]在系统实现中使用词表分词。词表中文分词的原理,是根据现有词库进行字符串模式匹配,把长的字符串分割为若干个词库中已经存在的词语即可。因此,制作词库成为必须的,词库中词语的选择也要慎重。系统选择的是一个大小为53301个中文词语,按照拼音顺序排列的文本格式的词库,词语之间使用回车符隔开。这个词库在使用时,要全部调入内存。为此,使用哈希表来实现。这是因为词库是使用最频繁的公有资源,把词库的调入和查词工作封装到WordDataBase类中,这是一个静态类作为公有资源使用,不允许产生多个实例。分词系统同时为Lucene索引器、Lucene查询分析器和Nutch分析器提供服务。词库选择的好坏,直接影响着Lucene和Nutch的表现。对于Lucene来说,是否收入长词语并没有多大关系。因为Lucene可以将相对短的词语进行索引,查询时,不会造成什么影响。例如,“中华人民共和国”这个词语并不存在于词库中,而是“中华”,“人民”,“共和国”三个词语存在。在索引时,这三个词语被连续的索引。Lucene查询分析器将“中华人民共和国”解释为一个短语查询对象(PhraseQuery),由三个TermQuery组成,分别是“中华”,“人民”,“共和国”。由于PhraseQuery查询要求索引中的词语顺序必须与组成它的TermQuery的顺序一致且必须连续,因此在查询时同样能正确的查到。如此一来,似乎可以不再需要词库,直接对每一个汉字做索引就可以了。这样做当然没有任何问题,有些搜索引擎就是这么做的。但是为了查准率,这样做就有一个缺点,即分词中的交叉歧义和包含歧义问题。由于是在无语义的情况下分词,只能作某些字符串运算(实际上是字符串模式匹配)来进行分词,因此出现了不同的分词策略。每一种策略的分词结果可能不同,这完全依赖于词库。所有的分词方法为:前向递增最大匹配分词、前向递减最大匹配分词、前向递增最小匹配分词、前向递减最小匹配分词、后向递增最大匹配分词、后向递减最大匹配分词、后向递增最小匹配分词和后向递减最小匹配分词,共计8种方法。系统实现了4种前向分词方法,基本上就可以满足需要。由于现代汉语文章中,充斥着大量的英文单词甚至句子,尤其是计算机方面。鉴于英文是世界上使用最广泛的语言,中文文章中含有另外的语言的可能性不大。英文分词是很简单的,根据空格作为分隔符即可。但是,还有一些特殊的单词,必须要考虑。另外涉及到一个重要问题,也是最复杂的一个问题,就是汉字之间的空格如何处理。一般来讲,汉字之间不可能出现空格。在计算机中,有时候为明确区分汉字,也人为地加上空格。处理方法是决不可以把空格当作中文的词语分隔符,因为空格一般恰恰是出现在词语的中间。对于整个分词系统来说,还应该允许用户自由选择需要的词语,即提供过滤功能。系统允许用户设置中文词语,英文词语,中文停止词,英文停止词分别是否要加入结果Word列表中。停止词表示一种语言中的大量出现且无关紧要的通用词语,例如助词、叹词和介词等。这些信息预先定义在WordDataBase中。对于中文来说,“的”,“地”,“得”等都可以作为停止词。对于英文,则有“this”,“are”,“the”等。

3.2 JavaCC分析

JavaCC是集分词和语法分析与一身的针对Java语言的文本自动分词软件包[4],类似于Unix系统中的LEX和YACC工具。JavaCC把这两者的功能结合,形成了一个功能强大的分析工具。用户只需写出分析脚本,JavaCC就会生成符合用户要求的类,用来进行词法和语法分析。JavaCC使用了自动机的理论,而不是递归下降分析,Lucene和Nutch正是利用JavaCC这个十分强大的工具,生成系统的分词器。JavaCC的语法定义是由正规式(Regular Expression)来完成的。在这里,指的是上下文无关文法。理解形式文法的定义,才能更好的理解和使用JavaCC。在一种语言中,存在非终结符和终结符两种单词。正规式就定义了一个非终结符怎样被替换为另一个字符串。正规式可以描述一种语言,符合该正规式定义的所有句子都是这个语言的句子。或者,正规式描述了正规文法,又称线性文法,或上下文无关文法[5]。称为线性,是因为这种文法可以从前到后顺序的被指定为一个句子。JavaCC的语法定义功能十分强大,可以做几乎所有的限制和指定。它提供了四种正规式类型:regexpr_kind ::= "TOKEN"  | "SPECIAL_TOKEN"  | "SKIP"  | "MORE" TOKEN: 它表示语法中的单词(Token),这个段中的正规式规定了这种语言中单词的语法,即分词的依据。单词管理器(TokenManager)依据每一个正规式来匹配下一个单词,这是按照最大匹配规则进行的。如果有多个匹配,那么选择最长的单词返回。如果有几种正规式产生了相同长度的最长单词,那么以较笨重正规式的顺序,返回最先定义的正规式产生的单词。单词管理器匹配出要返回的单词后,即返回给语法分析器。SPECIAL_TOKEN: 在SPECIAL_TOKEN段写出的正规式规定了特殊单词。特殊单词也是一种单词,但是并不起实际的作用,也不能从getNextToken中访问到。它的访问方式是从Token类的specialToken属性来读取。对于一种语言,如果某些单词不起语法的作用,但也是句子的一部分,那么可以使用特殊单词。例如编程语言中的注释。SKIP: 由SKIP段产生的单词被跳过(即忽略)。当我们不惜望出现某种模式的单词时,即可使用SKIP段。MORE: 当一个单词不能一次被产生,而必须逐渐产生时,则使用MORE。未完成的Token被存储在一个StringBuffer对象中,我们可以任意修改。所谓正规式,实际就是产生式。它的语法格式如下:javacode_production ::= "JAVACODE"     java_return_type java_identifier "(" java_parameter_list ")"     java_block  JAVACODE产生式可以写入任何Java代码,也可以写EBNF(扩展的Backus-Naur范式)。实际上,在某种程度上上下文无关语言就变成了上下文相关语言,因为Java代码可以处理有关语境的信息。例如,在修改的脚本加入了代码以后,就是一个上下文相关语言。有时候,当EBNF无法自行描述语法时,也可以借助“无所不能的”Java代码。例如下面的代码,非终结符 "skip_to_matching_brace"的作用是跳过完整匹配的括号。实际上,这个工作是EBNF无法完成的,因为它描述的不是线性文法。但是,使用Java代码可以很容易的解决:    

JAVACODE    void skip_to_matching_brace() {      

Token tok;     

 int nesting = 1;      

while (true) {       

 tok = getToke(1);      

 if (tok.kind == LBRACE)

 nesting++;        

if (tok.kind == RBRACE) {         

 nesting--;         

 if (nesting == 0) break;        

}        

tok = getNextToken();     

 }    }

3.2利用JavaCC构造中文分析模块

JavaCC是根据西方语言的形式文法理论设计的,不能直接解决中文问题。当仔细研究后,发现所谓的“中文问题”实际上就是如何把上下文无关文法转变为上下文相关文法。EBNF当然不能解决这个问题。通过写入Java代码,用各种对象和标志变量制作特殊的“上下文”环境,就可以实现JavaCC的中文分词。Lucene和Nutch原来的脚本在TOKEN段进行了精细的刻画,对于英文单词、主机地址、电子邮件地址、数字、缩写等各种格式都进行了考虑。只需利用中文分词功能,直接传入中文句子,得到ArrayList类型的返回结果。因此,唯一的工作就是事先分出一个全部是中文的字符串,这一点通过下面的定义实现:<CHINESE: (<CJK>)+ (" " | <CJK>)* >< #CJK:      [       "/u3040"-"/u318f",       "/u3300"-"/u337f",       "/u3400"-"/u3d2d",       "/u4e00"-"/u9fff",       "/uf900"-"/ufaff"      ]

上面的CHINESE的定义为:由汉字开头,包含汉字或空格的最长的字符串,而汉字则定义为CJK。CJK即中国、日本、朝鲜和韩国使用的中国汉字的总称,全称为CJK Ideographs(CJK象形文字),这是Unicode标准所定义的。在Lucene中,一旦实现了中文字符串的提取,就可以把它进行分词,然后在next方法中返回。但是,在Nutch中QueryParser已经取消,与NutchAnalysis合并到一起,这样就增加了修改的难度。因为next方法也被取消,需要取词时直接通过调用TokenManager的getNextToken方法来取得,而这个方法是JavaCC自动产生的,无法自行定制。解决的办法是利用JavaCC语法提供的TOKEN_MGR_DECLS段,在TokenManager中加入用户定义的代码通过重写getNextToken方法,来判断返回的单词的种类。如果不是CHINESE(中文)类型,则直接返回,否则进行分词,分别返回分词的结果。与英文分词不同的是,中文分词有多种方式。例如有前向递增最大分词,前向递增最小分词等8种方法,适用的范围是不相同的。在Lucene或Nutch中,如果是为了制作索引,那么应该使用全部的4种前向分词,即前向递增最大分词、前向递增最小分词、前向递减最大分词、前向递减最小分词。这是因为索引词库要尽量全,使用全部的分词可以保证所有的词都被提取出来。还有一种情况是为了使用查询分析器,即对查询字符串进行分析,这时,只需使用前向递增最大分词。

结论  

JavaCC是一个强大的词法和语法分析工具,LuceneNutch利用JavaCC构造语言分析模块的设计架构,使系统具有良好的可扩展性,为构造不同语种的语言分析模块奠定了基础。实验表明,利用JavaCC和中文词表分词技术实现的中文分析模块与原系统具有良好的兼容性,相对于商用的搜索引擎, Nutch作为开放源代码搜索引擎将会更加透明,从而更值得广大Internet用户信赖。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值