http://liyanblog.cn/李岩的博客 李岩 java lucene 搜索 nosql hadoop 博客 mongodb, 搜索2024-03-19T16:32:56+08:00李岩的博客搜索改进问题savagertnullhttp://liyanblog.cn/articles/2013/12/19/1387432503670.html2013-12-19T13:55:03+08:00<p>1.分词规则算法。</p>
<p>2.索引和搜索,数据分布式存储和计算。</p>
<p>3.网页内容去重规则和算法。</p>
<p>4.相关搜索词算法。</p>
<p>5.搜索词汇纠错算法。</p>
<p>6.多语言处理。</p>
<p>7.服务器规划,不同系统对服务器的配置要求和数量。</p>
<p>8.用户搜索行为分析。根据用户输入的关键词分析用户意图。给出更准确的结果</p>
<p>9.排序规则。参与排序的因素和权重因子。</p>
<p>10.整个系统高可用、高并发实现。<br /> <br />11.死链检测的方法</p>
<p>12.迅速下线的方法</p>
<p>13.如何评估搜索效果</p>
<p>14.屏蔽词机制</p>
<p>15.搜索结果去重的机制方法</p>
<p>16.结果页中的特殊展示结果是如何运作的,包括展现样式是合作方提供还是自己设计开发</p>
<p>17.是否有先进的技术可以帮助央视搜索处理无任何文字标签的视频数据</p>用户搜索行为及其意图浅析savagertnullhttp://liyanblog.cn/articles/2013/05/07/1367892964838.html2013-06-06T14:58:33+08:00<p>搜索行为目前已经成为每个上网的人的基本需求,但是用户的搜索行为是怎样一个过程?隐藏在用户查询背后的搜索意图是什么?这都是需要站长、营销人员仔细研究的领域,只有这样才能提供更好的用户体验。</p>
<h3>用户搜索行为</h3>
<p>用户之所以会产生搜索行为,往往是在解决任务时遇到自己不熟悉的概念或者问题,由此产生了对特定信息的需求,之后用户会在头脑中逐步形成秒速需求的查询词,将查询提交给搜索引擎,然后对搜索结果进行浏览,如果发现搜索结果不能完全解决用户的信息需求,则会根据搜索结果的启发,改写查询,以便更精确地描述自己的信息需求,之后重新构造新的查询需求,提交搜索引擎,如此形成用户和搜素引擎交互的闭合回路,直到搜索结果已经解决了自己的需求或尝试几次无果而终。</p>
<p><a href="http://lusongsong.com/upload/502-1.gif" target="_blank"><img title="用户搜索行为的示意图" src="http://lusongsong.com/upload/502-1.gif" alt="用户搜索行为及其意图浅析-卢松松博客" /></a></p>
<p>(描述这种用户搜索行为的示意图)</p>
<p>从上述过程可以看出,从用户产生信息需求到最终形成用户查询,中间有很大的不确定性,用户未必能够一开始就找到合适的查询词,即使是找到了,也可能存在查询词不能完全描述信息需求的情形,即在形成查询的过程中存在信息丢失的问题。所以后续循环中的查询改写就是用户逐步澄清搜索需求的一个过程。</p>
<h3>用户搜索意图分类</h3>
<p>用户发出的每个搜索请求背后都隐含着潜在的搜索意图,如果搜索引擎能够根据查询词汇自动找出背后的用户搜索意图,然后针对不同的意图,提供不同的检索方法,将更符合用户意图的搜索结果排在前列,无疑会增加搜索引擎用户的搜索体验。目前搜索引擎已经部分实现了这种搜索模式,比如用户搜索“北京 天气”的时候会主动将当天的气温等情况列在搜索结果最前面。如图所示,这种方法的一个结构示意图。</p>
<p><a href="http://lusongsong.com/upload/502-2.gif" target="_blank"><img title="用户搜索意图分类" src="http://lusongsong.com/upload/502-2.gif" alt="用户搜索行为及其意图浅析-卢松松博客" /></a></p>
<p>(用户搜索意图分类)</p>
<h3>Broader将搜索意图分为3个类别</h3>
<p>应该将用户意图分为哪些类型,目前并没有明确标准可言,不过很多工作都受到了Broader等人的意图分类工作的影响,他们通过人工分析查询,将搜索意图分为3个大的类别:</p>
<p><strong>1, 导航型搜索</strong></p>
<p>这种搜索请求的目的是要查找具体的某个网址,比如某公司的主页等,其特点是想要去某个网页。</p>
<p><strong>2, 信息型搜索</strong></p>
<p>这种搜索请求的目的是为了获取某种信息,比如“如何做宫保鸡丁”、“美国现任总统是谁”等,其特点是用户想要学到一些新知识。</p>
<p><strong>3, 事务性搜索</strong></p>
<p>这话总搜索请求的目的是为了完成一个目标明确的人物,比如下载MP3、下载软件或者淘宝购物等,其特点是想要在网上做一些事情。</p>
<h3>雅虎研究人员在做的细化</h3>
<p>Broader的搜索意图三分法非常有影响力,但过于粗糙。而雅虎的研究人员在此基础上做了细化,将用户搜索意图划分如下类别:</p>
<p><strong>1, 导航类:</strong>用户知道要去哪里,但是为了免于输入URL或不知道具体网址,所以用搜索引擎查找。</p>
<p><strong>2, 信息类:</strong>又可以细分为如下几种子类型。</p>
<blockquote>
<p>直接性:用户想知道关于一个话题某个方面明确的信息,比如“为何星星会发光”、“哪些食物隔夜后不能吃”。</p>
<p>间接型:用户想了解关于某个话题的任意方面的信息,比如粉丝搜索“李宇春”。</p>
<p>建议型:用户希望能够搜索到一些建议、意见或者某方面的指导,比如“如何才能戒烟”。</p>
<p>定位型:用户希望了解在现实生活中哪里可以找到某些产品或服务,比如“买电脑”。</p>
<p>列表型:用户希望找到一批能够满足需求的信息,比如“中关村附近的饭店”。</p>
</blockquote>
<p><strong>3, 资源类:</strong>这种类型的搜索目的是希望能够从网上获取某种资源,又可以细分为以下几种子类型。</p>
<blockquote>
<p>下载型:希望从网络某个地方下载想要的产品或者服务,比如“windows7下载”。</p>
<p>娱乐型:用户出于消遣的目的希望获得一些有关信息,比如“电影下载”。</p>
<p>交互型:用户希望使用某个软件或服务提供的结果,用户希望找到一个网站,这个网站上可以查询北京的天气情况。</p>
<p>获取型:用户希望获取一种资源,这种资源的使用场合不限于电脑,比如“折扣券”,用户希望搜到某个产品的折扣券,打印后在现实生活中使用。</p>
</blockquote>
<h3>写在最后:</h3>
<p>上述是对用户意图的人工整理分类,如果站长、推广人员能准确了解搜索用户到底想要什么的时候,提供满意的服务才会成为可能。</p>
<p>原文:http://lusongsong.com/reed/502.html</p>转:搜索的境界savagertnullhttp://liyanblog.cn/articles/2013/02/28/1362023658169.html2013-02-28T11:55:18+08:00<p>做好搜索需要什么样的境界,怎样才能做好搜索?虽然题目可能有些大,但这是我做搜索这段时间来一些很深但又很浅的感悟。说浅,看起来似乎很普通,说深,做好真的很难。</p>
<p><span>经常和很多公司做搜索的朋友们聊天,也会和人讨论一些新的形态,其中看到一些观点</span></p>
<p>1.<span><span style="font-family: Arial;"> </span></span><span>现有的搜索会被颠覆,颠覆其的多种多样,有</span>app<span>、有社交搜索、也有其他形态</span></p>
<p>2. <span>搜索的技术会出现重大革新,远远领先于现有的产品一个时代</span></p>
<p><span style="font-size: 12px;">3. </span><span style="font-size: 12px;">某某搜索要努力在短时间快速提升,达到某某的水准</span><span style="font-size: 12px;"> </span></p>
<p><span>现有网页搜索本身会不会被颠覆呢?其实看过很多用户的使用行为,研究过用户的查询需求,琢磨过后续点击,会感受到其真正的内在魅力以及用户价值和社会价值。其他的形式有可能会成为其很好的补充,但绝对不会取代现有搜索的地位,而网页搜索的技术和产品形态本身还会不断的提升和革新,以顺应时代的需求。</span></p>
<p><span>最近一直在思考,做好搜索需要怎么样的境界?而这个境界不是无休止的耐心,不是超大的投入,也不是所谓的基因。而怎样才能做好搜索,回味着自己过去做过思考过的很多细节,想从另外一些角度去思考这个问题。</span></p>
<p><span>首先做好搜索没有万能药,超牛的架构不是,超强的用户数据不是,近年来大红大紫的</span>Lambdamart<span>不是,更好的产品及形态也不是,执着于某一点,也许可以在短期内取得突破性的提高,但做到一定程度,却很难达到更高的高度。</span></p>
<p><span>网页搜索本身可以说是最复杂的产品之一了,想要将效果做好,在排序前要做好抓取、页面分析、选取、索引、链接分析、召回,做好效果要考虑相关性、权威性、时效性、点击,</span>NLP<span>要考虑分词、去词、同义词、重要性、紧密性,此外还要有开放平台、垂搜整合、</span>Hint<span>、</span>Suggestion<span>等。如果涉及到产品革新还要有更多的领域和工作。</span></p>
<p><strong><span>其中在任何局部突破,都是术的领域,而想要做好搜索,需要从道的角度有所突破。</span></strong></p>
<p><span>以前,我讨论搜索结果效果究竟是什么?其不仅仅是技术,也不仅仅是产品,而更是一种思想,一种想要不断探索和研究用户需求,一颗最自己产品永不满足的心。</span></p>
<p><span>做搜索首先一定要接地气,一定要真正的钻到</span>case<span>中,去看用户的真实查询,去研究用户的点击,去与竞争对手去比较,去找到自己的差距和不足。</span></p>
<p><span>想要单纯的依靠牛架构、牛算法或者牛数据做好搜索,是很不现实的。当做的越深,就越会发现,做到一定程度,想要做到极致,靠的不是长板有多长,而是要努力没有短板。</span></p>
<p><span>也许有时候会发现,当面对</span>10<span>个要解决的</span>Case<span>,可能问题出在</span>10<span>个不同的模块中。搜索做到一定程度后,没有太速成的方法,更重要不是技术有多牛,而是细节做得有多好。有可能当做出一次改进时,改进不足</span>0.1%<span>,甚至只有</span>0.01%<span>,但很多时候,体验就是靠这些不断的小改进堆出来的。</span></p>
<p><strong><span>搜索的需求非常复杂,只有真正的去接地气,认真研究需求,做好每一个细节,这是道之一也。</span></strong></p>
<p><span>很多人都会对</span>KPI<span>有想法,不合适的</span>KPI<span>或过于注重</span>KPI<span>很容易出现,每个团队都在为了自己的</span>KPI<span>去工作,团队之间互相推诿,最后大家各自使劲,最后发现团队的目标与公司的目标无法吻合,投入了很多却无法获得相应的收益。而有的小团队,拧成一股劲朝着一个方向努力,不用投入很多却能做的很好。</span></p>
<p><span>而对于搜索这么大的工程而言,同样也是这个情况,那么多的团队,那么多复杂的算法,那么多的交互,如果不能很好的整合,也会出现那种劲使不到一处去的情况。很可能会出现类似最好的分词不适用于算相关性这种情况。</span></p>
<p><strong><span>任何一块强都不是真的强,只有整体做的好才是真的好。将团队拧成一个整体,让项目拥有同一个目标,让每个好的算法能够很好的整合,这是道之二也。</span></strong></p>
<p><span>现在的搜索引擎做到什么程度了,也许只从找网页来说,几个主流的都不算差了,但是你会发现,搜索引擎还在不断的进化,其发展永远没有止境。</span></p>
<p><span>也许做了很长时间,你会发现,不管你做的有多努力,总有一些需求满足的不好。或者以目前的形态下足够好了,但是结果对用户并不完美。而在这种情况下,走在前面的会去思考探索产品和技术解决之道,而带来一次又一次的进步。</span></p>
<p><span>从后来的框计算、应用平台,再到知识图谱,搜索总是在不断的创新着。而也在不断有人探索着个性化、社交化、交互性甚至门户化的发展方向,不是所有的创新都会成功,但是成功却离不开不断的探索和创新。</span></p>
<p><strong><span>一颗永不知足的心,持续探索着产品和技术的创新,追随时代的前行,这是道之三也。</span></strong></p>
<p><span>一件事情做的有多牛,不是看你使用的技术有多强大,而是能否选用最恰当的技术,并且将所有细节做到位了,达到最好的效果。</span></p>
<p><span>一个项目想要做好,不是每个局部都取得好成绩就可以的,而是需要所有人拧成一股劲,去为共同的目标努力。</span></p>
<p><span>一个方向能够走多远,不是看今天站的有多高,而是你能够看得更远,能去不断探索,追随甚至引领时代发展的脚步。</span></p>
<p><strong><span>精诚团结、做好细节、迎接趋势,这是在我心中做好搜索需要达到的境界。说深不深,说浅不浅,知易行难,想要做好,需要付出更多更深的努力。</span></strong></p>
<p><span>以上,是我对搜索的一些妄想。</span></p>ik分词学习savagertnullhttp://liyanblog.cn/articles/2012/11/01/1351757296884.html2012-11-01T16:08:16+08:00<div class="content-head clearfix">
<h2 class="title content-title">ik分词学习</h2>
</div>
<div id="content" class="content mod-cs-content text-content clearfix">
<p>词典结构。<br />1.词典是以树状结构存储的。<br />2.每个节点是一个字。<br />3.每个节点还包含所有子节点的集合。<br />4.最根节点是(char)0 <br />5. 每个节点中存贮子结点的方式有两种<br /> //Map存储结构 子节点个数>3<br /> private Map<Character , DictSegment> childrenMap;<br /> //数组方式存储结构 字节点个数<=3<br /> private DictSegment[] childrenArray;<br />6.每个节点用 private Character nodeChar; 标示<br />7.当前节点存储的Segment数目 private int storeSize = 0; 递增<br />8.当前DictSegment状态 ,默认 0 , 1表示从根节点到当前节点的路径表示一个词<br /> private int nodeState = 0; <br /> <br />*******************************************************************<br />装载词库<br />public synchronized void fillSegment(char[] charArray , int begin , int length){<br /> //获取字典表中的汉字对象<br /> Character beginChar = new Character(charArray[begin]);<br /> Character keyChar = charMap.get(beginChar);<br /> //字典中没有该字,则将其添加入字典<br /> if(keyChar == null){<br /> charMap.put(beginChar, beginChar);<br /> keyChar = beginChar;<br /> }<br /> <br /> //搜索当前节点的存储,查询对应keyChar的keyChar,如果没有则创建<br /> DictSegment ds = lookforSegment(keyChar);<br /> //处理keyChar对应的segment<br /> if(length > 1){<br /> //词元还没有完全加入词典树<br /> ds.fillSegment(charArray, begin + 1, length - 1);<br /> }else if (length == 1){<br /> //已经是词元的最后一个char,设置当前节点状态为1,表明一个完整的词<br /> ds.nodeState = 1;<br /> }</p>
<p> }<br /> <br /> /**<br /> * 查找本节点下对应的keyChar的segment<br /> * 如果没有找到,则创建新的segment<br /> * @param keyChar<br /> * @return<br /> */<br /> private DictSegment lookforSegment(Character keyChar){<br /> <br /> DictSegment ds = null;</p>
<p> if(this.storeSize <= ARRAY_LENGTH_LIMIT){<br /> //获取数组容器,如果数组未创建则创建数组<br /> DictSegment[] segmentArray = getChildrenArray(); <br /> //搜寻数组<br /> for(DictSegment segment : segmentArray){<br /> if(segment != null && segment.nodeChar.equals(keyChar)){<br /> //在数组中找到与keyChar对应的segment<br /> ds = segment;<br /> }<br /> } <br /> //遍历数组后没有找到对应的segment<br /> if(ds == null){<br /> //构造新的segment<br /> ds = new DictSegment(keyChar); <br /> if(this.storeSize < ARRAY_LENGTH_LIMIT){<br /> //数组容量未满,使用数组存储<br /> segmentArray[this.storeSize] = ds;<br /> }else{<br /> //数组容量已满,切换Map存储<br /> //获取Map容器,如果Map未创建,则创建Map<br /> Map<Character , DictSegment> segmentMap = getChildrenMap();<br /> //将数组中的segment迁移到Map中<br /> migrate(segmentArray , segmentMap);<br /> //存储新的segment<br /> segmentMap.put(keyChar, ds);<br /> //释放当前的数组引用<br /> this.childrenArray = null;<br /> }<br /> //segment数目+1<br /> this.storeSize++;<br /> } <br /> <br /> }else{<br /> //获取Map容器,如果Map未创建,则创建Map<br /> Map<Character , DictSegment> segmentMap = getChildrenMap();<br /> //搜索Map<br /> ds = (DictSegment)segmentMap.get(keyChar);<br /> if(ds == null){<br /> //构造新的segment<br /> ds = new DictSegment(keyChar);<br /> segmentMap.put(keyChar , ds);<br /> //当前节点存储segment数目+1<br /> this.storeSize ++;<br /> }<br /> }</p>
<p> return ds;<br /> }<br /> </p>
<p><br />****************************************************************<br />//判断某个词是否匹配<br />public Hit match(char[] charArray , int begin , int length){<br /> Character keyChar = new Character(charArray[begin]);<br /> DictSegment ds = null;<br /> <br /> //引用实例变量为本地变量,避免查询时遇到更新的同步问题<br /> DictSegment[] segmentArray = this.childrenArray;<br /> Map<Character , DictSegment> segmentMap = this.childrenMap; <br /> <br /> //STEP1 在节点中查找keyChar对应的DictSegment<br /> if(segmentArray != null){<br /> //在数组中查找<br /> for(DictSegment seg : segmentArray){<br /> if(seg != null && seg.nodeChar.equals(keyChar)){<br /> //找到匹配的段<br /> ds = seg;<br /> }<br /> }<br /> }else if(segmentMap != null){<br /> //在map中查找<br /> ds = (DictSegment)segmentMap.get(keyChar);<br /> }<br /> //STEP2 找到DictSegment,判断词的匹配状态,是否继续递归,还是返回结果<br /> if(ds != null){ <br /> if(length > 1){<br /> //词未匹配完,继续往下搜索<br /> return ds.match(charArray, begin + 1 , length - 1);<br /> }else if (length == 1){//搜索最后一个char<br /> Hit searchHit= new Hit();<br /> <br /> if(ds.nodeState == 1){<br /> //添加HIT状态为完全匹配<br /> searchHit.setMatch();<br /> }<br /> if(ds.hasNextNode()){<br /> //添加HIT状态为前缀匹配<br /> searchHit.setPrefix();<br /> }<br /> return searchHit;<br /> }<br /> <br /> } <br /> //STEP3 没有找到DictSegment, 将HIT设置为不匹配<br /> return new Hit(); <br /> }<br />*********************************************************</p>
<p> <br /> public class Hit {<br /> //Hit不匹配<br /> private static final int UNMATCH = 0x00000000;<br /> //Hit完全匹配<br /> private static final int MATCH = 0x00000001;<br /> //Hit前缀匹配<br /> private static final int PREFIX = 0x00000010;<br /> <br /> <br /> //该HIT当前状态,默认未匹配<br /> private int hitState = UNMATCH;<br /> /**<br /> * 判断是否完全匹配<br /> */<br /> public boolean isMatch() {<br /> return (this.hitState & MATCH) > 0;<br /> }<br /> public void setMatch() {<br /> this.hitState = this.hitState | MATCH;<br /> }<br /> /**<br /> * 判断是否是词的前缀<br /> */<br /> public boolean isPrefix() {<br /> return (this.hitState & PREFIX) > 0;<br /> }<br /> public void setPrefix() {<br /> this.hitState = this.hitState | PREFIX;<br /> }<br /> /**<br /> * 判断是否是不匹配<br /> */<br /> public boolean isUnmatch() {<br /> return this.hitState == UNMATCH ;<br /> }<br /> public void setUnmatch() {<br /> this.hitState = UNMATCH;<br /> } <br />} <br />二进制 int 状态<br />00 0 不匹配<br />01 1 匹配<br />10 2 是前缀。(ds.hasNextNode())<br />11 3 比配并且是前缀</p>
<p>*******************************************************<br />Lexeme 语义单元(词元) <br /> //lexemeType常量<br /> //普通词元<br /> public static final int TYPE_CJK_NORMAL = 0;<br /> //姓氏<br /> public static final int TYPE_CJK_SN = 1;<br /> //尾缀<br /> public static final int TYPE_CJK_SF = 2;<br /> //未知的<br /> public static final int TYPE_CJK_UNKNOWN = 3;<br /> //数词<br /> public static final int TYPE_NUM = 10;<br /> //量词<br /> public static final int TYPE_NUMCOUNT = 11;<br /> //英文<br /> public static final int TYPE_LETTER = 20;<br /> <br /> //词元的起始位移<br /> private int offset;<br /> //词元的相对起始位置<br /> private int begin;<br /> //词元的长度<br /> private int length;<br /> //词元文本<br /> private String lexemeText;<br /> //词元类型<br /> private int lexemeType;<br /> <br /> //当前词元的前一个词元<br /> private Lexeme prev;<br /> //当前词元的后一个词元<br /> private Lexeme next;<br /> <br /> **********************************************************************************<br /> /**<br /> * 分词器上下文状态<br /> * @author 林良益<br /> *<br /> */<br />public class Context{<br /> <br /> //是否使用最大词长切分(粗粒度)<br /> private boolean isMaxWordLength = false; <br /> //记录Reader内已分析的字串总长度<br /> //在分多段分析词元时,该变量累计当前的segmentBuff相对于reader的位移<br /> private int buffOffset; <br /> //最近一次读入的,可处理的字串长度<br /> private int available;<br /> //最近一次分析的字串长度<br /> private int lastAnalyzed; <br /> //当前缓冲区位置指针<br /> private int cursor; <br /> //字符窜读取缓冲<br /> private char[] segmentBuff;<br /> /*<br /> * 记录正在使用buffer的分词器对象<br /> * 如果set中存在有分词器对象,则buffer不能进行位移操作(处于locked状态)<br /> */<br /> private Set<ISegmenter> buffLocker;<br /> /*<br /> * 词元结果集,为每次游标的移动,存储切分出来的词元<br /> */<br /> private IKSortedLinkSet lexemeSet;<br /> <br /> <br />*******************************************************************************<br />private class IKSortedLinkSet{<br /> //链表头<br /> private Lexeme head;<br /> //链表尾<br /> private Lexeme tail;<br /> //链表的实际大小<br /> private int size;<br /> <br /> /**<br /> * 向链表集合添加词元 是一个有序的队列。排序方式。是//起始位置优先,再//词元长度优先<br /> * @param lexeme<br /> */<br /> private void addLexeme(Lexeme lexeme){。。}<br /> /**<br /> * 取出链表集合的第一个元素<br /> * @return Lexeme<br /> */<br /> private Lexeme pollFirst(){。。。}<br /> /**<br /> * 取出链表集合的最后一个元素<br /> * @return Lexeme<br /> */<br /> private Lexeme pollLast(){。。。}<br /> /**<br /> * 剔除集合汇总相邻的切完全包含的lexeme<br /> * 进行最大切分的时候,过滤长度较小的交叠词元<br /> */<br /> private void excludeOverlap(){。。。}<br />***********************************************************************************<br />分词器接口<br />public interface ISegmenter {<br /> <br /> /**<br /> * 从分析器读取下一个可能分解的词元对象<br /> * @param segmentBuff 文本缓冲<br /> * @param context 分词算法上下文<br /> */<br /> void nextLexeme(char[] segmentBuff , Context context);<br /> <br /> /**<br /> * 重置子分析器状态<br /> */<br /> void reset();<br />}</p>
<p>***************************************************************************************<br />/**<br /> * IK Analyzer v3.2<br /> * IK主分词器<br /> * 注:IKSegmentation是一个lucene无关的通用分词器<br /> * @author 林良益<br /> *<br /> */<br />public final class IKSegmentation{</p>
<p> <br /> private Reader input; <br /> //默认缓冲区大小<br /> private static final int BUFF_SIZE = 3072;<br /> //缓冲区耗尽的临界值<br /> private static final int BUFF_EXHAUST_CRITICAL = 48; <br /> //字符窜读取缓冲<br /> private char[] segmentBuff;<br /> //分词器上下文<br /> private Context context;<br /> //分词处理器列表<br /> private List<ISegmenter> segmenters;<br /> <br /> /**<br /> * 获取下一个语义单元<br /> * @return 没有更多的词元,则返回null<br /> * @throws IOException<br /> */<br /> public synchronized Lexeme next() throws IOException {。。。}<br /> /**<br /> * 取出词元集合中的下一个词元<br /> * @return Lexeme<br /> */<br /> private Lexeme buildLexeme(Lexeme lexeme){。。。}<br />***************************************************************************************<br />private class CSeg{<br /> /*<br /> * 词段开始位置<br /> */<br /> private int start;<br /> /*<br /> * 词段的结束位置<br /> */<br /> private int end;<br /> }<br /> public void reset() {<br /> //重置已处理标识<br /> doneIndex = -1;<br /> _CSegList.clear();<br /> }<br />}<br />**************************************************************************************</p>
<p>/**<br /> * 中文词元处理子分词器,涵盖一下范围<br /> * 1.中文词语<br /> * 2.姓名<br /> * 3.地名<br /> * 4.未知词<br /> * @author 林良益<br /> *<br /> */<br />public class ChineseSegmenter implements ISegmenter {<br /> /*<br /> * 已完成处理的位置<br /> */<br /> private int doneIndex;<br /> /*<br /> * 词段处理队列<br /> */<br /> List<CSeg> _CSegList;<br /> <br /> <br /> public void nextLexeme(char[] segmentBuff , Context context) {</p>
<p> //读取当前位置的char <br /> char input = segmentBuff[context.getCursor()];<br /> Hit hit = null;<br /> if(CharacterHelper.isCJKCharacter(input)){//是中文(CJK)字符,则进行处理<br /> if(_CSegList.size() > 0){<br /> //处理词段队列<br /> List<CSeg> tmpList = new ArrayList<CSeg>(_CSegList);<br /> for(CSeg seg : tmpList){<br /> hit = Dictionary.matchInMainDict(segmentBuff, seg.start, context.getCursor() - seg.start + 1);<br /> if(hit.isMatch()){//匹配成词<br /> //判断是否有不可识别的词段<br /> if(seg.start > doneIndex + 1){<br /> //输出并处理从doneIndex+1 到 seg.start - 1之间的未知词段<br /> processUnknown(segmentBuff , context , doneIndex + 1 , seg.start - 1);<br /> }<br /> //输出当前的词<br /> Lexeme newLexeme = new Lexeme(context.getBuffOffset() , seg.start , context.getCursor() - seg.start + 1 , Lexeme.TYPE_CJK_NORMAL);<br /> context.addLexeme(newLexeme);<br /> //更新goneIndex,标识已处理<br /> if(doneIndex < context.getCursor()){<br /> doneIndex = context.getCursor();<br /> }<br /> <br /> if(hit.isPrefix()){//同时也是前缀<br /> //更新seg.end、seg.type,<br /> seg.end = context.getCursor();<br /> //seg.type = CSeg.TYPE_MATCH;<br /> <br /> }else{ //后面不再可能有匹配了<br /> //移出当前的CSeg<br /> _CSegList.remove(seg);<br /> }<br /> <br /> }else if(hit.isPrefix()){//前缀,未匹配成词<br /> //更新seg.end、seg.type<br /> seg.end = context.getCursor();<br /> //seg.type = CSeg.TYPE_PRE;<br /> <br /> }else if(hit.isUnmatch()){//不匹配<br /> //移出当前的CSeg<br /> _CSegList.remove(seg);<br /> }<br /> }<br /> }<br /> <br /> //处理以input为开始的一个新CSeg<br /> hit = Dictionary.matchInMainDict(segmentBuff, context.getCursor() , 1);<br /> if(hit.isMatch()){//匹配成词<br /> //判断是否有不可识别的词段<br /> if(context.getCursor() > doneIndex + 1){<br /> //输出并处理从doneIndex+1 到 context.getCursor()- 1之间的未知<br /> processUnknown(segmentBuff , context , doneIndex + 1 , context.getCursor()- 1);<br /> }<br /> //输出当前的词<br /> Lexeme newLexeme = new Lexeme(context.getBuffOffset() , context.getCursor() , 1 , Lexeme.TYPE_CJK_NORMAL);<br /> context.addLexeme(newLexeme);<br /> //更新doneIndex,标识已处理<br /> if(doneIndex < context.getCursor()){<br /> doneIndex = context.getCursor();<br /> }</p>
<p> if(hit.isPrefix()){//同时也是前缀<br /> //向词段队列增加新的词段<br /> CSeg newCSeg = new CSeg();<br /> newCSeg.start = context.getCursor();<br /> newCSeg.end = newCSeg.start;<br /> //newCSeg.type = CSeg.TYPE_MATCH;<br /> _CSegList.add(newCSeg);<br /> }<br /> <br /> }else if(hit.isPrefix()){//前缀,未匹配成词<br /> //向词段队列增加新的词段<br /> CSeg newCSeg = new CSeg();<br /> newCSeg.start = context.getCursor();<br /> newCSeg.end = newCSeg.start;<br /> //newCSeg.type = CSeg.TYPE_PRE;<br /> _CSegList.add(newCSeg);<br /> <br /> }else if(hit.isUnmatch()){//不匹配,当前的input不是词,也不是词前缀,将其视为分割性的字符<br /> //输出从doneIndex到当前字符(含当前字符)之间的未知词<br /> processUnknown(segmentBuff , context , doneIndex + 1 , context.getCursor());<br /> //更新doneIndex,标识已处理<br /> doneIndex = context.getCursor();<br /> }<br /> <br /> }else {//输入的不是中文(CJK)字符<br /> if(_CSegList.size() > 0<br /> && doneIndex < context.getCursor() - 1){<br /> for(CSeg seg : _CSegList){<br /> //判断是否有不可识别的词段<br /> if(doneIndex < seg.end){<br /> //输出并处理从doneIndex+1 到 seg.end之间的未知词段<br /> processUnknown(segmentBuff , context , doneIndex + 1 , seg.end);<br /> }<br /> }<br /> }<br /> //清空词段队列<br /> _CSegList.clear();<br /> //更新doneIndex,标识已处理<br /> if(doneIndex < context.getCursor()){<br /> doneIndex = context.getCursor();<br /> }<br /> }<br /> <br /> //缓冲区结束临界处理<br /> if(context.getCursor() == context.getAvailable() - 1){ //读取缓冲区结束的最后一个字符 <br /> if( _CSegList.size() > 0 //队列中还有未处理词段<br /> && doneIndex < context.getCursor()){//最后一个字符还未被输出过<br /> for(CSeg seg : _CSegList){<br /> //判断是否有不可识别的词段<br /> if(doneIndex < seg.end ){<br /> //输出并处理从doneIndex+1 到 seg.end之间的未知词段<br /> processUnknown(segmentBuff , context , doneIndex + 1 , seg.end);<br /> }<br /> }<br /> }<br /> //清空词段队列<br /> _CSegList.clear();<br /> }<br /> <br /> //判断是否锁定缓冲区<br /> if(_CSegList.size() == 0){<br /> context.unlockBuffer(this);<br /> <br /> }else{<br /> context.lockBuffer(this);<br /> <br /> }<br /> }</p>
</div>IK的整个分词处理过程savagertnullhttp://liyanblog.cn/articles/2012/11/01/1351757272208.html2013-03-05T17:04:43+08:00<div class="content-head clearfix">
<h2 class="title content-title">IK的整个分词处理过程</h2>
</div>
<div id="content" class="content mod-cs-content text-content clearfix">
<p><strong>首先,介绍一下IK的整个分词处理过程:</strong></p>
<p>1. Lucene的分词基类是Analyzer,所以IK提供了Analyzer的一个实现类IKAnalyzer。首先,我们要实例化一个IKAnalyzer,它有一个构造方法接收一个参数isMaxWordLength,这个参数是标识IK是否采用最大词长分词,还是采用最细粒度切分两种分词算法。实际两种算法的实现,最大词长切分是对最细粒度切分的一种后续处理,是对最细粒度切分结果的过滤,选择出最长的分词结果。<span style="font-size: 12px;"> </span></p>
<p>2. IKAnalyzer类重写了Analyzer的tokenStream方法,这个方法接收两个参数,field name和输入流reader,其中filed name是Lucene的属性列,是对文本内容进行过分词处理和创建索引之后,索引对应的一个名称,类似数据库的列名。因为IK仅仅涉及分词处理,所以对field name没有进行任何处理,所以此处不做任何讨论。</p>
<p>3. tokenStream方法在Lucene对文本输入流reader进行分词处理时被调用,在IKAnalyzer的tokenStream方法里面仅仅实例化了一个IKTokenizer类,该类继承了Lucene的Tokenizer类。并重写了incrementToken方法,该方法的作用是处理文本输入流生成token,也就是Lucene的最小词元term,在IK里面叫做Lexeme。</p>
<p>4. 在IKtokenizer的构造方法里面实例化了IK里面最终要的分词类IKSegmentation,也称为主分词器。它的构造方法接收两个参数,reader和isMaxWordLength。</p>
<p>5. IKsegmentation的构造方法里面,主要做了三个工作,创建上下文对象Context,加载词典,创建子分词器。</p>
<p>6. Contex主要是存储分词结果集和记录分词处理的游标位置。</p>
<p>7. 词典是作为一个单例被创建的,主要有量词词典、主词典和停词词典。词典是被存储在字典片段类DictSegment 这个字典核心类里面的。DictSegment有一个静态的存储结构charMap,是公共词典表,用来存储所有汉字,key和value都是一个中文汉字,目前IK里面的charMap大概有7100多的键值对。另外,DictSegment还有两个最重要的数据结构,是用来存储字典树的,一个是DictSegment的数组childrenArray,另一个是key为单个汉字(每个词条的第一个汉字),value是DictSegment的HashMap childrenMap。这两个数据结构二者取其一,用来存储字典树。</p>
<p>8. 子分词器才是真正的分词类,IK里面有三个子分词器,量词分词器,CJK分词器(处理中文),停词分词器。主分词器IKSegmentation遍历这三个分词器对文本输入流进行分词处理。</p>
<p>9. IKTokenizer的incrementToken方法调用了IKSegmentation的next方法,next的作用是获得下一个分词结果。next在第一次被调用的时候,需要加载文本输入流,并将其读入buffer,此时便遍历子分词器,对buffer种的文本内容进行分词处理,然后把分词结果添加到context的lexemeSet中。</p>
<p><strong>下面,以CJKSegmenter子分词器为例介绍一下生成分词结果集的流程:</strong></p>
<p>1. IKSegmentation遍历Segmenter时,调用CJKSegmenter的nextLexeme方法,该方法接收两个参数,segmentBuf和context,segmentBuf是一个Character数组,文件输入流分解后得到,context即IK的Context类实例,用来记录segmentBuf游标以及存储切分后的词元lexeme。</p>
<p>2. 进入nextLexeme方法,首先判断是否中文字符,然后判断是否存在词段队列,举例来说“中华人民共和国”,这个词条,就会存在一个词段队列,分别存储“中华”、“中华人民”、“中华人民共和”、“中华人民共和国”,前面词段依次是后面词段的前缀。这个词段队列也是在nextLexeme方法中填充的。当一个词条和字典中的词匹配成功,并且也是字典中某个词条的前缀,则被加入队列,当一个词条不再是某个词条的前缀,移除出队列。</p>
<p>3. 如果词段队列里面不存在词段,把当前的Character与字典中的词匹配,创建一个新的hit,如果字典种存在这个Character,把hit添加进词段队列。如果词段队列里面已经存在词段,遍历词段队列,判断当前Character是否可以以词段队列中的词为前缀,组成一个字典中存在的词,如果可以,则加入lexemeSet中,如果不能匹配成词,则将这个前缀hit从队列种移除,因为以后也不会匹配成词了,故删除。</p>
<p>4. 如此循环执行nextLuxeme方法,最终将文本输入流切分成的词元lexeme完全放入context的lexemeSet中。这时,再次调用IKSegmentation类的next方法时,则会直接读取lexemeSet中已经切分好的词元。所以所有的切分工作都在第一次调用IKSegmentation的next的时候完成。</p>
</div>Senseisavagertnullhttp://liyanblog.cn/articles/2012/10/23/1350962280733.html2013-03-05T16:43:29+08:00<div class="content-head clearfix">
<h2 class="title content-title">Sensei</h2>
</div>
<div id="content" class="content mod-cs-content text-content clearfix">
<p>在未出现开源搜索引擎以前, Doug Cutting整了个Lucene, 随后Yonik Seeley写了一个Solr, 在2010年 Shay Banon发布了ElasticSearch, 大概在两年前, 我们迎来了Sensei, 最近他们发布了1.0版本, 下面通过 @sematext对LinkedIn的搜索架构师John Wang的一个采访. 来大致了解一下Sensei. </p>
<p> </p>
<p>Sensei是什么?<br />开源, 灵活, 实时, 分布式数据库, 原生支持搜索, 能操作非结构化文本和结构化数据. 它主要用户处理海量复杂半结构化查询和经常变化的数据结构. 它广泛用于支持LinkedIn.com的搜索功能. <br /><br />为什么没有用solr?<br />1.更新量大 <br />2.分布式. 目前solr的分布式在master上还存在单点问题(SPOF), 让Solr Cloud还没出来 <br />3.复杂的facet支持. <br /><br />Sensei的独到之处有哪些?<br />支持海量更新 <br />支持半结构化查询 <br /><br />Sensei最大的缺点以及计划何时解决?<br />1. Sensei的文档必须有一个long类型的唯一标示符. 这个主要是处于性能的考虑. 目前没考虑解决. <br />2. 数字类型不支持负数, 该功能即将实现. <br />3. 静态schema. 稍后支持. <br /><br />接下来即将实现的关键功能有哪些?<br />1.相关的工具 <br />2.内建对时间和地理信息类型字段支持 <br />3.parent node类型文档支持 <br />4.支持属性类型facet(名字对) <br />5.在线均衡重置 <br />6.在线索引重建 <br />7.参数化二级存储 <br />8.动态schema <br />9.支持聚合函数, 比如AVG, MIN, MAX <br /><br />Norbert在Sensei中的作用?<br />一个集群管理器, 负责Broker和Sensei node之间的RPC调用. Broker是内置在Sensei节点的Servlet. Norbert用于Sensei Node之间的消息传输. 同时它内置了Zookeeper来进行集群管理. 下一步将对其进行抽象, 允许以plugin的方式集成其他集群功能. <br /><br />对BQL介绍一下?<br />BQL – Browse Query Language. 作为SQL变种支持Sensei查询. 它将成为Sensei的标准查询. <br /><br />Sensei相关的性能测试数据?<br />测试数据参见:http://senseidb.com/performance.html <br />相关测试代码参见:https://github.com/kwei/search-perf <br /><br />Sensei是否有单点问题(SPOF)?<br />没有. <br /><br />什么情况下会导致数据丢失?<br />除非所有副本节点都丢失了数据 <br />Sensei要求添加的所有数据都是有序而且带版本的, 从而实现数据回放恢复. <br /><br />如果集群中的一个节点挂了会怎样?<br />有ZooKeeper呢. <br /><br />如果达到集群容量上限会如何处理? 自动扩展还是手工重置负载均衡?<br />取决于数据的Sharding方式. <br />新加入的节点需要在sensei.properties配置文件中指定处理那些分片的数据, 比如: <br />sensei.node.partitions=1,3,8 <br /><br />如果sharding策略不需要迁移数据, 比如根据时间和连续UID sharding, 那么扩展集群将非常方便 <br />如果sharding策略需要迁移数据. 比如采用取模的方式sharding, 这时集群就需要重置负载均衡. 下一步将实现在线重置负载均衡. 而现在需要对所有数据重建索引. <br /><br />作为最终一致性的Sensei, 如何保证多次查询结果一致?<br />通过一个路由参数, Sensei采用一致性hash路由参数, 保证每次查询都定位到同一个节点. <br /><br />如何升级? 是否需要停机整个集群? 是否向后兼容?<br />部分升级. 不需要停机整个集群. 向后兼容. <br /><br />sensei是否需要shema?<br />需要, 下一步支持动态shema. <br /><br />其他搜索功能的支持情况?<br />通过相关工具支持Function query <br />计划支持SpatialSearch和Parent-Child数据 <br />不计划支持Join. <br /><br />如何访问Sensei?<br />提供两个接口: REST/JSON和BQL <br />提供Java和Python客户端. <br /><br />Sensei的运维工具做的怎样?<br />自带有一个web应用来管理集群. <br />提供了JMX支持, <br />还支持修改配置来调整输出, 比如日志输出. </p>
</div>IK Analyzer 3.0 中文分词器 - Lucene索引savagertnullhttp://liyanblog.cn/articles/2012/10/19/1350639801056.html2013-03-05T16:12:05+08:00<div class="content-head clearfix">
<h2 class="title content-title"> <strong style="font-size: 12px;">IK Analyzer 3.0 </strong><strong style="font-size: 12px;">中文分词器 - Lucene索引</strong></h2>
</div>
<div id="content" class="content mod-cs-content text-content clearfix">
<p><br /><strong>1.IK Analyzer 3.0</strong><strong>介绍</strong><br /><br />IK Analyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始, IKAnalyzer已经推出了3个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。新版本的IK Analyzer 3.0则发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。 <br /><br /><strong>1.1 IK Analyzer 3.0</strong><strong>结构设计</strong><br /><br /><br /><strong>1.2 IK Analyzer 3.0</strong><strong>特性</strong></p>
<span>采用了特有的“正向迭代最细粒度切分算法“,具有50万字/秒的高速处理能力。(IK3.1以上版本已优化至65万字/秒) 采用了多子处理器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数量词,罗马数字,科学计数法),中文词汇(姓名、地名处理)等分词处理。 优化的词典存储,更小的内存占用。支持用户词典扩展定义 针对Lucene全文检索优化的查询分析器IKQueryParser(作者吐血 推荐);采用歧义分析算法优化查询关键字的搜索排列组合,能极大的提高Lucene检索的命中率。</span>
<p><br /><br /><strong>1.3 </strong><strong>分词效果示例</strong><br /><br /><strong>文本原文1:</strong><br />IK-Analyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始, IKAnalyzer已经推出了3个大版本。 <br /><strong>分词结果:</strong><br />ik-analyzer | 是 | 一个 | 一 | 个 | 开源 | 的 | 基于 | java | 语言 | 开发 | 的 | 轻量级 | 量级 | 的 | 中文 | 分词 | 工具包 | 工具 | 从 | 2006 | 年 | 12 | 月 | 推出 | 1.0 | 版 | 开始 | ikanalyzer | 已经 | 推出 | 出了 | 3 | 个大 | 个 | 版本 <br /><br /><strong>文本原文2:</strong><br />永和服装饰品有限公司 <br /><strong>分词结果:</strong><br />永和 | 和服 | 服装 | 装饰品 | 装饰 | 饰品 | 有限 | 公司 <br /><br /><strong>文本原文3:</strong><br />作者博客:linliangyi2007.javaeye.com 电子邮件:linliangyi2005@gmail.com <br /><strong>分词结果:</strong><br />作者 | 博客 | linliangyi2007.javaeye.com | 2007 | 电子邮件 | 电子 | 邮件 | 地址 | linliangyi2005@gmail.com | 2005 <br /><br /><strong>2.</strong><strong>使用指南</strong><br /><br /><strong>2.1</strong><strong>下载地址</strong><br />GoogleCode开源项目 :http://code.google.com/p/ik-analyzer/<br />GoogleCode SVN下载:http://ik-analyzer.googlecode.com/svn/trunk/<br /><br /><strong>2.2</strong><strong>安装部署</strong><br />IK Analyzer安装包包含: <br />1. 《IKAnalyzer中文分词器V3.0使用手册》(即本文档) <br />2. IKAnalyzer3.0GA.jar <br />3. IKAnalyzer.cfg.xml <br />它的安装部署十分简单,将IKAnalyzer3.0GA.jar部署于项目的lib目录中;IKAnalyzer.cfg.xml文件放置在代码根目录(对于web项目,通常是WEB-INF/classes目录,同hibernate、log4j等配置文件相同)下即可。 <br /><br /><br /><strong>2.3 Lucene</strong><strong>用户快速入门</strong><br /><br /><strong>代码样例</strong></p>
<p>Java代码<a href="http://www.javaeye.com/topic/429960"></a></p>
<span>/** * IK Analyzer Demo * @param args */ import java.io.IOException; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.RAMDirectory; //引用IKAnalyzer3.0的类 import org.wltea.analyzer.lucene.IKAnalyzer; import org.wltea.analyzer.lucene.IKQueryParser; import org.wltea.analyzer.lucene.IKSimilarity; /** * @author linly * */ public class IKAnalyzerDemo { public static void main(String[] args){ //Lucene Document的域名 String fieldName = "text"; //检索内容 String text = "IK Analyzer是一个结合词典分词和文法分词的中文分词开源工具包。它使用了全新的正向迭代最细粒度切分算法。"; //实例化IKAnalyzer分词器 Analyzer analyzer = new IKAnalyzer(); Directory directory = null; IndexWriter iwriter = null; IndexSearcher isearcher = null; try { //建立内存索引对象 directory = new RAMDirectory(); iwriter = new IndexWriter(directory, analyzer, true , IndexWriter.MaxFieldLength.LIMITED); Document doc = new Document(); doc.add(new Field(fieldName, text, Field.Store.YES, Field.Index.ANALYZED)); iwriter.addDocument(doc); iwriter.close(); //实例化搜索器 isearcher = new IndexSearcher(directory); //在索引器中使用IKSimilarity相似度评估器 isearcher.setSimilarity(new IKSimilarity()); String keyword = "中文分词工具包"; //使用IKQueryParser查询分析器构造Query对象 Query query = IKQueryParser.parse(fieldName, keyword); //搜索相似度最高的5条记录 TopDocs topDocs = isearcher.search(query , 5); System.out.println("命中:" + topDocs.totalHits); //输出结果 ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (int i = 0; i < topDocs.totalHits; i++){ Document targetDoc = isearcher.doc(scoreDocs[i].doc); System.out.println("内容:" + targetDoc.toString()); } } catch (CorruptIndexException e) { e.printStackTrace(); } catch (LockObtainFailedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ if(isearcher != null){ try { isearcher.close(); } catch (IOException e) { e.printStackTrace(); } } if(directory != null){ try { directory.close(); } catch (IOException e) { e.printStackTrace(); } } } } } </span>
<p> <br /><strong>执行结果:</strong><br />命中:1 <br />内容:Document<stored/uncompressed,indexed,tokenized<text:IK Analyzer是一个结合词典分词和文法分词的中文分词开源工具包。它使用了全新的正向迭代最细粒度切分算法。>> <br /><br /><br /><strong>2.4 </strong><strong>关键API说明</strong><br /><br /><strong> </strong><strong>类org.wltea.analyzer.lucene.IKAnalyzer</strong><strong>l</strong><br />说明:IK分词器的主类,是IK分词器的Lucene Analyzer类实现。 <br />该类使用方法请参考 “代码样例”章节 <br /><br /><strong> </strong><strong>类org.wltea.analyzer.lucene.IKQueryParser</strong><strong>l</strong><br /> publicn static Query parse(String field , String query) throws IOException <br />说明:单条件,单Field查询分析 <br />参数1 :String field, 查询的目标域名称 <br />参数2 :String query , 查询的关键字 <br />返回值:构造一个单条件,单Field查询器 <br /><br /> public static Queryn parseMultiField(String[] fields , String query) throws IOException <br />说明:多Field,单条件查询分析 <br />参数1 :String[] fields, 多个查询的目标域名称的数组 <br />参数2 :String query , 查询的关键字 <br />返回值:构造一个多Field,单条件的查询器 <br /><br /> public static Queryn parseMultiField(String[] fields , String query , BooleanClause.Occur[] flags) throws IOException <br />说明:多Field,单条件,多Occur查询分析 <br />参数1 :String[] fields, 多个查询的目标域名称的数组 <br />参数2 :String query , 查询的关键字 <br />参数3 :BooleanClause.Occur[] flags , 查询条件的组合方式(Or/And) <br />返回值:构造一个多Field,单条件,多Occur的查询器 <br /><br /> publicn static Query parseMultiField(String[] fields , String[] queries) throws IOException <br />说明:多Field,多条件查询分析 <br />参数1 :String[] fields, 多个查询的目标域名称的数组 <br />参数2 :String[] queries , 对应多个查询域的关键字数组 <br />返回值:构造一个多Field,多条件的查询器 <br /><br />n public static Query parseMultiField(String[] fields , String[] queries , BooleanClause.Occur[] flags) throws IOException <br />说明:多Field,多条件,多Occur查询 <br />参数1 :String[] fields, 多个查询的目标域名称的数组 <br />参数2 :String[] queries , 对应多个查询域的关键字数组 <br />参数3 :BooleanClause.Occur[] flags , 查询条件的组合方式(Or/And) <br />返回值:构造一个多Field, 多条件, 多Occur的查询器 <br /><br /><strong>l</strong><strong>类org.wltea.analyzer.lucene.IKSimilarity</strong><br />说明: IKAnalyzer 的相似度评估器。该类重载了DefaultSimilarity的coord方法,提高词元命中个数在相似度比较中的权重影响,即,当有多个词元得到匹配时,文档的相似度将提高。 <br />该类使用方法请参考 “代码样例”章节 <br /><br /><strong>l</strong><strong>类org.wltea.analyzer.IKSegmentation</strong><br />说明: 这是IK分词器的核心类。它是真正意义上的分词器实现。IKAnalyzer的3.0版本有别于之前的版本,它是一个可以独立于Lucene的Java分词器实现。当您需要在Lucene以外的环境中单独使用IK中文分词 组件时,IKSegmentation正是您要找的。 <br /> public Lexeme next() throws IOExceptionn <br />说明:读取分词器切分出的下一个语义单元,如果返回null,表示分词器已经结束。 <br />返回值:Lexeme 语义单元对象,即相当于Lucene的词元对象Token <br /><br /><strong>l</strong><strong>类org.wltea.analyzer.Lexeme</strong><br />说明: 这是IK分词器的语义单元对象,相当于Lucene中的Token词元对象。由于3.0版本被设计为独立于Lucene的Java分词器实现,因此它需要Lexeme来代表分词的结果。 <br /> public int getBeginPosition()n <br />说明:获取语义单元的起始字符在文本中的位置 <br />返回值:int , 语义单元相对于文本的绝对起始位置 <br /><br /> public int getEndPosition()n <br />说明:获取语义单元的结束字符的下一个位置 <br />返回值:int , 语义单元相对于文本的绝对终止位置的下一个字符位置 <br /><br /> public int getLength()n <br />说明:获取语义单元包含字符串的长度 <br />返回值:int , 语义单元长度 = getEndPosition – getBeginPosition <br /><br /> public String getLexemeText()n <br />说明:获取语义单元包含字符串内容 <br />返回值:String, 语义单元的实际内容,即分词的结果 <br /><br /><br /><strong>3.</strong><strong>词表扩展</strong><br /><br />目前,IK分词器自带的主词典拥有22万左右的汉语单词量。由于作者个人的精力有限,并没有对搜集到的词库进行全范围的筛选、清理。此外,对于分词组件应用场景所涉及的领域的不同,也需要各类专业词库的支持。为此,IK分词器提供了对词典的扩充支持。 <br /><br /><strong>基于API的词典扩充 </strong><br />IK分词器支持使用API编程模型扩充您的词典。如果您的词典是存储与数据库中,这个方式应该对您适用。API如下: <br /><br /><strong> </strong><strong>类org.wltea.analyzer.dic.Dictionary</strong><strong>l</strong><br />说明: IK分词器的词典对象。它负责中文词汇的加载,内存管理和匹配检索。 <br /> public static voidn loadExtendWords(List<String> extWords) <br />说明:加载用户扩展的词汇列表到IK的主词典中,增加分词器的可识别词语。 <br />参数1:List<String> extWords , 扩展的词汇列表 <br />返回值:无 <br /><br /><strong>3.2</strong><strong>基于配置的词典扩充 </strong><br />IK分词器还支持通过配置IKAnalyzer.cfg.xml文件来扩充您的专有词典。 <br /><br /><strong>1. </strong><strong>部署IKAnalyzer.cfg.xml</strong><br />IKAnalyzer.cfg.xml部署在代码根目录下(对于web项目,通常是WEB-INF/classes目录)同hibernate、log4j等配置文件相同。 <br /><br /><strong>2. </strong><strong>词典文件的编辑与部署</strong><br />分词器的词典文件格式是无BOM的UTF-8编码的中文文本文件,文件扩展名不限。词典中,每个中文词汇独立占一行,使用\r\n的DOS方式换行。(注,如果您不了解什么是无BOM的UTF-8格式, 请保证您的词典使用UTF-8存储,并在文件的头部添加一空行)。您可以参考分词器源码org.wltea.analyzer.dic包下的.dic文件。 <br /><br />词典文件应部署在Java的资源路径下,即ClassLoader能够加载的路径中。(推荐同IKAnalyzer.cfg.xml放在一起) <br /><br /><br /><strong>3. IKAnalyzer.cfg.xml</strong><strong>文件的配置</strong></p>
<p>Xml代码<a href="http://www.javaeye.com/topic/429960"></a></p>
<strong><?xml</strong><span> version="1.0" encoding="UTF-8"</span><strong>?></strong><span> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"</span><strong>></strong><span> </span><strong><properties></strong><span> </span><strong><comment></strong><span>IK Analyzer 扩展配置</span><strong></comment></strong><span> <!--用户可以在这里配置自己的扩展字典--> </span><strong><entry</strong><span> key="ext_dict"</span><strong>></strong><span>/mydict.dic ; /mypack/mydict2.dic ; /com/mycompany/dic/mydict3.dic ;</span><strong></entry></strong><span> </span><strong></properties></strong><span> </span>
<p><br />在配置文件中,用户可一次配置多个词典文件。文件名使用“;”号分隔。文件路径为相对java包的起始根路径。</p>
</div>转:关于搜索引擎及其开发savagertnullhttp://liyanblog.cn/articles/2012/10/19/1350624370174.html2013-03-05T14:47:24+08:00<div class="content-head clearfix">
<h2 class="title content-title"><span style="font-size: 12px;"> 托google、百度们成功的福,搜索引擎火了半边天。很多人都想跨到这个行业里边来。前两天在公司里边面试了一些人,基本上没有感到满意。不是说从业经验不够,有些也已经工作了三年、四年。不过我估计,或者说是猜想,是不是做应用做的时间太长了,把数据结构、算法,时间、效率都扔到一边去了;然后平时的工作又太忙,平时自己工作的做的可以,但对工作相关的、稍微扩展的知识没有时间或者说是懒得去看了。。。。。</span></h2>
</div>
<div id="content" class="content mod-cs-content text-content clearfix">
<p> 我的想法是,如果有兄弟姐妹要进入这个行业,最好对这个行业流行的想法、做法了解一些,如果不了解这些,就把数据结构和算法的基础课好好看看,或者说好好练练编程的基本功。搜索引擎开发涉及到了我们上学时学到的所有的东西。而且有些特别新的东西是没有书可以看得,有的只是经验,经过传承到了后进入者的手里;好的话,就是有论文,不过有价值的都是英文论文,国内的都是抄来抄去,不如直接去看他的原文(比如,也不知道哪个神仙把robust翻译成鲁棒性,放着健壮性之类的已经有的好词不用,非得翻译成这么一个上下不着地的词,而且现在还有逐渐流行的趋势。。。。)。不过,有本书叫做<<现代信息检索>>(MODERN INFORMATION RETRIEVAL),写的挺好。<br /> 呵呵。其实我在搜索引擎领域做的时间也不是多长,从进中搜到现在有两年多一点。我把我的所学到的一点经验和平时的思考的东西写出来,与大家共同的切磋。写到哪里算是哪里,您就将就去看。<br /> 首先声明:我不是牛人,离我所认为的牛人还差不少,大家一块讨论,口下留人,大家拍砖头肯定可以把我拍死^ _ ^。<br /> 按照目的的不同,搜索引擎分了两种:通用搜索(业内称为大搜索)和垂直搜索。网页搜索是大搜索,新闻搜索、论坛搜索是垂直搜索,酷讯的生活搜索之类的都是垂直搜索。<br /> 有人问我,现在google,baidu都在做搜索,而且做的很成功,他的胳膊伸长一点,哪还有其他的小厂商生存的余地?其实不光是搜索这个行业,其他的行业也存在、我们也可以提出相同的问题。但是,每年都有新的公司冒起来—即便是它所做的领域已经有了成功的公司,但是走在前边的成功公司并没有挡住后边新兴公司的路。我认为,市场的容纳能力和瞄准的市场不一样决定了这个。单纯以搜索领域来讲,google国外做的很成功并且不断扩张的的时候,中国的市场做的不是很好,百度抓住这个时间差让“百度更懂中文“成功占据了大部分的份额;当大搜索起来的时候,众多的垂直搜索已经开始兴起,特别是本地搜索部分,这是有每个人的日常生活密切相关的部分,是一个很大的市场。搜索领域的领头羊会把全部的精力都放在垂直搜索方面吗?我看不会。他们的任务是把自己在搜索领域的优势继续保持下去,君不见后来者居多,竞争非常激烈,特别是大搜索,搜狐的搜狗,QQ的,网易的有道。如果他们保持不了这个在行业内的领先地位,他们离着关门不远了。这是他们的已得市场,已得利益。这当然不是说他们不会扩张,他们会买很多的公司补充自己的不足,像google为了和百度的贴吧和百度知道竞争,投资迅雷,和天涯合作,花钱买来流量;百度忍受不了自己的用户在看着自己的贴吧的时候却在用QQ和msn交流,据说要做自己的即时通了。呵呵。而且,现在各个巨头也在开始做本地搜索,手机搜索。。。。不管行业如何,留给各个后来者的机会还有很多,市场很大,永远都有新想法诞生的可能。前提只有一个:不要跟巨头面对面的竞争,跟他们在他们的优势领域竞争不是找死吗?我们的老祖宗早就有以己之长,攻彼之短,只要你能跑在大船的前面,被收购也算是一个不错的选择,况且还有很多成为巨头的机会!巨头的优势在于资金和技术积累;劣势在于,庞大了以后掉头不易,要做的太多,还要小心翼翼的保持第一不被超越。而且,行业的繁荣才能有巨头的繁荣。珠穆朗玛峰只能出现在青藏高原上。</p>
<p> 一般说,搜索引擎包括这几部分:下载(爬虫)子系统,数据预处理子系统,搜索服务,控制(及监控)部分。搜索引擎是一个比较新的行业,国内也掌握了核心的技术。所以,和做OA,做工作流,做核心层上的应用有些不同,系统无论是从规模还是性能上讲都有比较高的要求。咱们一个一个的说。<br /> 下载。互联网通过超链,每一个网页是一个点,然后点和点之间有关联,关联的注释就是链接文本,所有的链接组成了一个巨大的网。大搜索下载的任务就是把这些网的每一个点都保存到本地(当然这是说理论上,现在互联网的规模,恐怕google也无法把所有的东西都保存到本地)。这带来几个问题:1. 网页如何尽量不重复的下载。网络带宽资源是有限的,本地硬盘资源也是有限的,下载的时间也是有限制的,重复的下载系统受不了。2. 互联网是不断更新的,这种更新需要反映到本地硬盘上的数据。如何尽量及时的更新?<br /> 一个Url就是一个字符串,而且这个字符串比较长。给保存和比较都带来了麻烦。我们对Url做md5映射,把它映射成一个64位的整形值。现在大多用的是64位。Md5本来算出来时128位,一般是取前64位。Md5的重复率很低。我曾经做过70亿Url的实验,我尽量模拟Url的组成生成了70亿Url,然后取md5值,排重,没有发现一个重的;而且md5的散列非常好,他本身可以认为是一个哈希散列,但是分布的非常随机。我把70亿的结果值分散到多个文件里边,每个文件里边的ID的个数基本上是一样的。我们可以认为,每一个Url取了md5值以后,他的这个ID值是唯一的。它可以用来比较两个字符串是否相同。也称作信息指纹。是判断是否相同的重要的办法。对ID值取模(mod)是一个非常重要的思想。依赖于md5值的散列性,你可以把大量的数据分成小堆,存到文件,各个小堆的数据是控制到可以放到内存中处理。这是查找、排重、以及后边建索引的基本的方法。它使得我们可以把大任务分解成可以并发解决。这样的并发系统是一个明显的木桶理论模型:决定于处理速度最慢的一个。但是,md5的散列性非常好,各个处理部分处理的数据在规模上相差不大,或者说基本上是一样的。比如上边所说的例子,70亿ID值。70亿*8=56G,放到内存里边不太现实。可以把他们对200取模,放到文件,每个文件280M,然后对每一个文件进行处理。那就随你的便了。<br /> 下载分成Server端和Client端。Server端指定下载的种子,他把这些url发给Client端,client端把server端给的url下载下来以后,分析页面把分析出来的url发给server端。。。。如此循环。有了上边的介绍,判断是否下载就好说了。都是64位值得比较。最简单的办法,你可以利用平衡二叉树,这是一个非常方便的动态查找的办法。不过,数据量一大,64位值也会占用很多的内存。我们可以算一下。1亿*8约等于800M,而且树节点左右指针也要占8个字节,平衡二叉树还有一个标记变量,这样算下来,放完1亿大约需要1.8G。不过他的查询速度可以忍受,而且是绝对的不重复,没有误差。假设我们可以探</p>
<p>测到的Url是100亿网页,按照这种办法,一台机器按照放2亿计算,需要100/2=50台server端。这样的确是要不少的机器,当然还有其他的办法。需要仔细的去想。呵呵。垂直搜索的下载和大搜索的下载是不一样的。垂直搜索针对一些特定的网站。以新闻为例:新闻下载会针对新闻源,比如新浪,china.com等新闻发布网站定时、定向抓取;寻找他们的新闻发布的规律,把那些网页下载下来。这就没有上边所说的大图的遍历和更新机制的问题了。难度不可同日而语。<br /> 不过,不要看了这些就认为下载很简单,这是一个与经验关系非常密切的活。上边说的都好说,但是,下载过程中碰到的问题可就多了。你需要解决大量的意想不到的事情,我在这里也不能多说,不过可以举几个例子:如果你把一个网站访问的太过频繁,重则把这个网站给弄瘫痪了(特别是小网站,大网站的抗打击能力还可以),轻则。。。让管理员注意到了太多的访问就把你的IP给封了。下载需要礼貌。毕竟是访问别人的网站。这是到别人家里做客啊!可不比自己家!还有作弊网页,大量的SEO作弊网页,下载的时候需要去掉一部分;还有,不是所有的网站都是可以在任何时候访问的,新浪首页下载不下来的时候你怎么办?得多想想后边如何处理。下载完了的数据需要进行处理。抽取标题,正文,父链接,字连接,链接文本。这是后边的数据基础。所有这些以及后边的其他的部分,难度大多集中于数据量。正文解析。把一个html中的正文解析出来,不是很轻松。</p>
<p>这个一般有两种方法:</p>
<p>1. 基于模板。这个用在新闻正文的解析上比较多。一个网站的新闻在一个时间段内往往有基本固定的格式,可以基本按照这个格式把不光是正文,各个值域,比如发布时间,作者等的统统解析出来。只是需要在网页结构改变的时候,人工做很多的模板。</p>
<p>2. VIPS:基于视觉的Web页面分页算法。呵呵。简单讲来,就是人看一篇网页的时候,人觉得网页正文应该在什么地方,然后让程序去根据位置模拟人的视觉判断,把正文解析出来。说到这里多说两句,微软很有意思,VIPS以及后边要提到的相关性的BM25公式都是微软研究院提出来的,而且这两种办法在搜索引擎上用的都是很广泛,事实证明,这两个办法是非常有效的办法。但是,呵呵,搜索他还是没有干过google。不光这样,msn的livesearch在各个搜索结果质量排名的时候基本都是倒数的。真是奇了怪了。不知道微软工程院是怎么干的。呵呵。<br /> 排重。也叫网页指纹,这个针对于网页的正文。网页有大量的转载网页。他们的正文区别就是一个或者是几个字的区别,剩下的不同都是乱七八糟的广告,flash。如何才能判断是否重复?一般有两种:</p>
<p>1. 关键字Top10</p>
<p> 2. 文本向量的方法。 这些网上都有很多的讨论,也有人做了很多的教程。你在网上多搜一下,以后有时间我也可以总结一下。而且,这些我们认为重复的网页是否做索引?索引怎么放?是个问题。你可以看看各个大搜索引擎的相似网页功能。<br /> PageRank,基于链接分析。最早是google的创始人提出来的。你可以看他们的论文<< The Anatomy of a Large-Scale Hypertextual Web Search Engine>>; 有意思的是,百度的创始人李彦宏在链接分析上也有专利,是否有点巧合?呵呵。不过,可以说对链接的分析开启了第二代搜索。第一代搜索是以雅虎的目录式,第二代以google为代表。第三代?我认为那是概念。还没有看到,还没有看到搜索的革命性变化。PageRank给出了一个静态的表示网页质量的方法,他认为,被链接的次数越多的网站,他的质量越好,重要程度越高。有点类似于越是明星,认识他的人也就越多一样。当然还有其他的办法分析链接。你可以找找看。很多这样的论文,简直太多了,不过够用就好。挖地三千尺应该是google们干的事情。</p>
<p> 互联网构成的这个巨大的图还有这样的特性,如果我们截取的是一块,假设类似一个圆,那么直径越大,理论上边际节点越多。 所谓的边际节点就是他的链接我们有,但是没有这些链接对应的网页。有点类似于谁的名言来着:知道的越多,不知道的也越多。随着互联规模的扩大,这种趋势也越来越明显,特别是我们只下载其中一部分网页的情况下。这种边际节点给理论上的PageRank计算带来了不小的问题:大量的边际节点的存在,让原始的PageRank计算难以发挥用,不过,很快就有人提出了新的PageRank算法。你可以看一下相应的论文。</p>
<p> 快照。下载的时候,把网页down到本地来,然后,当一个Url对应的网页打不开的时候,你可以打开搜索引擎做上的快照,看看爬虫当时爬下来的网页的样子,不过一般都是把网页上乱七八糟的东西比如</p>
<p>flash广告都去掉了---为了节省硬盘空间。说到这里就需要说一下全局ID以及什么时候需要这样的ID值。每一个网页都有一个ID值,这个ID就是他的url的Md5值。如果我们的数据有 10亿,20亿,几十亿。。。。。把所有的数据都放到一台机器上肯定不可能。我们用上边提到的取模的办法,分散到各个机器上。什么时候需要这样的全局唯一ID值那?那就是你在机器间需要唯一标志的时候。这不是废话吗?我们慢慢讲。一般快照是不和索引数据放到一块的。 我们取快照的时候,其实是给定一个ID值,然后从另外的机器上找到这个值对应的东西显示给用户。如果,我说的是如果,我们的数据都是按照取模的办法分散开来,相应的数据放到了一台机器上,那就不需要全局唯一了,只要本台机器上唯一就可以了。不是吗?</p>
<p>倒排索引。所谓的倒排是相对于我们的平时的习惯来的。平时的习惯是看到文章的标题,正文,然后是文章的内容的词。倒排反了过来,他是这样的,关键词,位置,其他属性,文章。有点类似于,我们写信的时候,地址是从大到小,然后到人,美国人是从人开始直到他的国家。呵呵。这样的好处是什么那?文章的集合是一个无穷尽的集合,但是,关键词的集合是相对小的多的集合,是可以枚举的,是基本有上限的词的集合构成了无穷尽的文章集合。而且,同一个关键词可以存在于不很多的文章里边,这么做可以节省内存,也可以方便做针对关键词的文章间的横向分析;同时,用户输入的是关键词,需要的是结果是文章,这样就在用户和文章之间建立一个通道。现在的搜索都是基于关键词的,所谓的推送,还有用户习惯的分析都是基于关键词的,只不过比用户自己输入关键词做了一层包装,方便了一点。这就是我为什么上边说搜索上革命性的变化还没有看到的原因。如果说关键词搜索是搜索2.0,那些概念做的好的话充其量也就是2.01,比第三代还差的远。当然,也许是我知道的太少,如果您看到了这样的东西,不妨告诉我,我也非常想知道。我认为,第三代是基于内容的分析和理解上的。不过,这只是猜想。我还远没有达到大师的级别。 </p>
<p>分词。基于关键词的检索有一个前提条件,那就是,把文章分成一个个关键词。分成怎样的关键词集合,标准是人来制定的。人在看完一篇文章以后,读懂了他的意思,然后把各个句子分开成一个个的词。当网页到了如此大规模的时候,用人工的方法断句分词当然是不现实的。用程序分词就需要告诉程序,怎样才算是一个词。有几种办法。我也知道的不是很多,没有对这个深入的研究过。不过都需要词典的支持,这个词典是人工生成的。基于词典的最大匹配办法很常用。还有一个方法是人工对一堆的文章进行断词,然后计算机统计一个词在另外一个词后边存在的概率。然后在处理新的文章的时候,他可以根据这个确定是否是把字符串这样分开。关键词也是字符串,他们的比较我们一般还是用md5的办法,称之为WordID。整数的比较、存放比长度变化的字符串之间的比较自然是方便了很多。<br /> 如果搜索引擎只是返回包含这个关键词的文章集合,那不叫信息检索,那叫查询。就跟在数据库中写一个sql语句查询一样。呵呵。之所以叫做信息检索,就因为有了对结果的排序,也叫做相关性排序。一个关键词在一个文章中的比重是不同的,甚至是出现在不同的位置,标题,正文的各个位置,链接文本。。。。。。带来的效果都是不同的。也可以说这篇文章对因为关键词所得的分数是不一样的,然后根据这些给文章打分,根据分数把文章排序。上边提到的<<现代信息检索>>讲基础部分讲的很好,BM25公式提出的办法是现在用的最广泛的;当然都需要在实际当中针对自己的方法做出相应的改变。一般认为这和大规模数据的检索程序一起视为搜索引擎的核心技术。任何的数据处理,检索办法都是为了后续的排序做准备的。没有相关性排序不能称之为搜索引擎。当其他的工程部分趋向于定式的时候,唯有分词和相关性排序需要加大后期的投入。</p>
<p> 其实,仔细想想就可以发现,现在所有的技术都是基于统计的。没有真正意义上的基于内容的分析和理解。计算机还是一如既往的死板,直肠子,不会拐弯,还只会按照人设定的规则去做,还远没有做到像人一般的联想,自己设定智能意义的规则,我认为,对内容的理解是基于对事物之间的关系以及对它们之间的联想上的。现阶段的内存和硬盘简单的数组、矩阵关系显然难以达到这一点。他的发展应该是向着仿生的方向发展的,特别是人的脑袋,不是说人的脑袋全部开发可以存下美国最大的图书馆吗?呵呵。不过,一旦真正意义上的智能出现,他的发展当然是指数级别的。智能计算机的生产自己的下一代,我的天,那是迭代的。呵呵。这是随便说着玩的。您也就姑且当作一乐。</p>
<p>当然,在全面的解决办法临之前,并不是说我们就堵在这里就没有办法前进了。社区和知识库是现在的搜索引擎的非常好的补充。当大搜索,相关性排序大家做的差不多趋于一致的的时候,百度贴吧、百度知道的有利补充让百度搜索凸显了了和其他公司的大搜索的不同。你搜索的时候可以注意一下,你在百度上搜的东西那些是来自于贴吧和知道的。特别是那些与日常生活关系很大的信息,比如,在北京我到哪里买黄金(前两天跟媳妇去买结婚戒指了!),输入这个关键词“北京 买黄金”。。。。他的问题是人提出来的,人回答的,还有内部的答案筛选机制。大家的参与,一旦形成了规模效应,效果立马显现。就好像,百度搜什么搜的都很准,用户体验非常的好,久而久之形成了搜索的习惯。习惯一旦养成,想改变是一个困难的事情。呵呵。而且现在能做到输入一句话全句飘红的也仅此一家而已。大搜索雅虎中国在做,搜狐在做,QQ也在做。如果不能在这个方面把百度比下去的话,想把头把交椅抢过来是一个很困难的事情,其他的方面弄大家做到最后都差不多嘛!Google就跟天涯这样的社区合作,很漂亮的战略和战术,天涯内容发展好了,对google来讲,跟百度贴吧有的一拼。不过还有一个有潜力的,QQ。他的人气,是任何人都不能小觑的,而且本身又有钱。只是发力晚了4、5年。暂且不对现有的秩序造成大的冲击,过两年就不一定了。 </p>
<p>建索引,检索,控制(监控),应该是算是一块的。他们的难度都在于数据量。数据量的规模上去了,所有以前可能忽略的问题都会变成问题。</p>
<p>1. 7*24小时服务。这包含两部分,一部分是软件的,一部分是硬件的。软件部分。数据不停的来,不停的处理;搜索请求不停的来,服务程序不停的响应。一旦程序出现了什么问题,这个流水线的处理就出现了中断。我们需要知道,程序为什么出现了问题。任何的程序都不会是第一次运行就会天生可以稳定高效运行的。我需要实时知道的状态,他在干什么;它死了的时候我需要知道为什么玩完了。这些都需要日志的支持。而且还需要不同的日志记录到不同的文件里边,以便于后续的人工统计和分析。比如,查询日志。用户的查询日志是一个非常重要的资源,是可以用来进行后续分析的重要资源。而对于数据处理程序来讲,可以断点重启,就像flashget的断点重下一样,是非常方便和重要的。这些都有赖于不同的日志和断点环境的保存。 </p>
<p>一般的c,c++程序容易出现的问题是内存泄露和内存的越界访问。Linux的内存泄露问题可以通过一个开源工具valgrind来测试;windows下边的程序也有相应的头文件进行内存的判断(调用微软的API函数),你可以在网上查到。一个不断的流血(泄露内存)的程序是不能指望他长期运行的。内存很快就耗光了。内存分配、文件打开的成对编写程序(编写了分配,直接编写释放语句,然后把中间需要处理的内容在这两个语句之间插入)是非常难过管用的技巧。不过,即便这样,也需要非常注意借个非常容易出现内存不释放的地方。</p>
<p>1. 循环的continue。</p>
<p>2. 函数中间的返回return。</p>
<p>本来后边有内存释放的,但是,让你的那两个语句给挡住了。呵呵。我也放过很多这样的错误。这种错误是一旦不注意就会犯的。特别是你的一个函数写的比较长的时候、一屏显示不开的时候(将函数小型化是非常重要的,但需要有一个度,过而不及!)。 </p>
<p>越界访问一般出现在数组、下标的溢出这样的地方。前两天写下载程序的时候就犯了一个致命的错误,让我调了一下午才找到错误的原因。我在定义Url字符串的时候,认为他的长度不会超过2048,超过2048的很变态,也很少,不过让我碰到了一个11K的。呵呵。一个strcpy让我的栈信息都被冲的无影无踪了。这才是最麻烦的,如果是一般的数组溢出,gdb可以直接定位到哪一行。而这样的错误,你是很难找到的错误点的。废了半天力气我才把错误定位到这一行上。当你发现程序错误错的很奇怪的时候,你会嘀咕,不应该有这样的错误的发生,不可能啊!你不妨考虑一下是否内存被冲了,变量的内容变了或者是运行到不该运行的地方(前者居多),然后程序出错了。那样的话,gdb或者VC给出定位是不对的。他应该是发生在其他的地方,而且严重的是这样的错误很难找。所以,在内存拷贝这样的操作的时候,不要想当然认为如何就如何。比如,strcpy,还是用strncpy吧。其他的错误总是好找,内存错误会让你中头彩。Gdb给出的错误文件是core.*****,如果让VC给出调试文件,需要做点设置。你可以看看我以前转载的那篇VC下发布的Release版程序的异常捕捉。<br /> 硬件部分。机器不停的提供服务,但是,不能指望他们可以忙一辈子。他们是有寿命的。比如说,损耗的最快的东西是什么?硬盘。搜索引擎一般采用1.4万转的硬盘。不停的读写对他们的寿命造成了很大的损害。呵呵。一般搜索引擎公司买硬盘都是上百块的买。当然,硬件7*24小时的服务需要软件的支持。比如说,搜索程序在写的时候就需要考虑到硬件当机的情况。当这台服务机器不能正常工作,监控程序需要及时报告维护人员,让他们及时维修;还有重要的一点是,或者是常用的做法是,一份数据,两个到三个相同的镜像。平时是几个镜像同时提供服务,紧急时刻,如果一个镜像不能联系,那么把流量导入另外的镜像。一般两台机器同时出现错误的概率是很低的。出现错误也要快修,别忘了我们的木桶理论。 </p>
<p> 有一个特殊的东西,叫做GFS。就是google的分布式文件系统。为什么把它单独的提出来,因为他这两年实在是太火了。搜索引擎需要的数据实在是太大了,需要大量的硬盘来存储;他对数据处理的要求又很高,需要分布式的并行的处理。Google利用一批刀锋式PC,成功用低成本解决了这个问题。所说的低成本是每存储1G数据需要的花费的资金,以及能处理如此规模的用户请求的基础上每处理一个用户请求花费的资金。呵呵。说的有点绕口。从外边看来,就是人家把一堆普通PC机,一堆硬盘变成了一个巨大的机器。而且各个节点非常非常的便宜。比廉价磁盘阵列都便宜,也好用(当然是软件的支持)。单说一点,这个机器的内存和硬盘是?差不多是各个节点的内存以及相应硬盘的和。诱人吧。不过,没有钱往里边砸,还是不要做这样的系统。他的论文是公开的,看了之后谁都有点。。。那个蠢蠢欲动。。。呵呵。。。因为,看起来,不是很难嘛!不过,你是否发现,论文上所说的东西都是原理,一个错误报告都没有。文件系统是一个底层的服务,没有千锤百炼谁敢把自己的系统放到一个不稳定的文件系统上运行?与原理相比,我更感兴趣的是,按照这种思路进行软件编写,他们在运行的过程中碰到的意外问题是什么?怎么解决的。而且,谁又有这么多的财力向redhat公司定制自己的单机文件系统。呵呵。这样想来,这样的一个程序需要多长的时间去完善?一般的真是耗不起。不过,现在有个开源的东西,Nutch。有时间需要好好看看。 </p>
<p>2. 多个机器之间的合作 多个机器一块去处理这么一大堆数据,相应服务请求,总得有个指挥,以便可以做到此起彼落,默契配合。这就是控制和监控做的。说起来也是平平无奇,每一个工作的机器都是服务器端,然后总控程序是一个客户端,他告诉服务器端什么时间,应该做什么。这个时候,一组机器都是统一去做的,你就得将就那个最“无能的”。呵呵。典型的木桶理论的诠释。 不过控制需要注意的是,一组机器作为一个控制单元,如果他们没有都做完你指定动作,你就不能要求他们做下一步动作。他们是一个完整的整体,就把他们看作一台机器好了,一荣不能俱荣,但是一废俱废。 监控端,关于他的有个名词叫做心跳。你在看其他的资料的时候会看到这个。每一个间隔都向服务器端报告自己的状态(通过socket)。一般是间隔一秒。呵呵。其他的也没有什么可以说的,本来是一个简单的东西。 </p>
<p>3. cache很多地方都用到了cache技术。依据数据访问的时间局部性和内容局部性原理,cache的出现极大的提高了数据访问的效率。特别是互联网上的搜索。扎堆的现象更为严重。这就是为什么会有搜索排行榜的原因了。前两天弄得沸沸扬扬的白领裸照事件。。。很多人搜的都是相同的关键词。在搜索引擎里边,cache作为独立的一部分出现,它能挡住75%以上的访问?也就是说,能进入到后台需要检索的访问占很小的一部分。大部分都在cache里边命中了。Cache的应用当然不止这一点。在所有你认为有数据需要缓冲访问的地方都可以用。用点内存换来访问的高效率有的时候还是很值得的。不过需要注意cache里边的数据的及时更新的问题,不能访问过时的数据。也就是数据何时更新的问题。 </p>
<p>排序,归并,排重。在这里,计算机恢复到了本来的面目,排序。你还记得数据结构和算法里边关于排序的介绍吗?我曾经问过一个面试的兄弟。他已经什么都记不起来了。只记住一个冒泡和快排。我以前听过小乔(中搜架构师)讲解数据结构和算法,让我大开眼界,终于知道什么时候,为什么用这些玩意了。前两天公司面试的时候,一个兄弟做排序,写了一个冒泡。我问他,能不能快点,他说快排。我说还能不能在快点,他说没有了。呵呵,把常数时间的排序都扔了(不过,也可能真的没有学)。下边这个程序是前段时间我在看基数排序的时候写的。工作的变更,一直都没有把它派上用场(以后说不定)。现在,这不会涉及到任何公司的机密,可以给大家看。他用了两倍于快排的内存,但是,我的测试是,可以有将近一倍的排序速度。 </p>
<p>平衡二叉树和快排有什么区别吗?从cpu的利用率上说。前者对cpu的使用基本上是平的;后者,在一个段时间把cpu用到100%,如果同一台机器上还有别人的程序在跑,那么他们都得等你,也就是你的资源利用不合理,造成了这段时间,cpu是程序的瓶颈;从动静方面来说,一个是动态的,可以边建,边检索,另外一个是静态的;从内存利用上说,前者耗费的远大于后者,大约一倍多点。 </p>
<p>template<class tType><br />long BucketSort(tType * buf,long len,tType * tempbuf)<br />...{ <br /> tType * src=tempbuf;<br /> tType * temp=buf;<br /> long lTemp=0;<br /> long entry_point=0;<br /> long count[256]=...{0};<br /> long i;<br /> long k;<br /> tType * p;<br /> for (i=0;i<sizeof(tType);i++)<br /> ...{<br /> for(k=0;k<len;k++)<br /> ...{<br /> ++count[(temp[k]>>(8*i))&0xFF];<br /> }<br /> entry_point =0;<br /> for(k=0;k<256;k++)<br /> {<br /> lTemp=count[k];<br /> count[k]=entry_point; //把相应数据个数变成了相应数据在数组中的位置<br /> entry_point+=lTemp; //显然是要把BASE ENTRY_POINT推后的~<br /> }<br /> for(k=0;k<len;k++)<br /> ...{<br /> src[count[(temp[k]>>(8*i))&0xFF]++]=temp[k];<br /> }<br /> memset(count,0,1024);<br /> p=temp;<br /> temp=src;<br /> src=p;<br /> }<br /> return 0;<br />}</p>
<p>template<class tType><br />CheckIt(tType * buf,long lNum)<br />...{<br /> long i;<br /> for(i=0;i<lNum-1;i++)<br /> ...{<br /> if(buf[i]>=buf[i+1])<br /> i=i;//用来设置断点,调试用。<br /> }</p>
<p> return 0;<br />}</p>
<p>template<class tType><br />long ReadIt(tType * buf)<br />...{<br /> unsigned __int64 * bufbak=buf;<br /> long lNum=0;<br /> FILE * fpR=fopen("ValidDocID.dat","rb");<br /> while(fread(bufbak,8,1,fpR))<br /> ...{<br /> bufbak++;<br /> lNum++;<br /> }<br /> fclose(fpR);<br /> return lNum;<br />}</p>
<p>int main()<br />...{<br /> UINT64 * buf=new UINT64[15<<20];<br /> UINT64 * tempbuf=new UINT64[15<<20];<br /> long lNum=ReadIt(buf); <br /> long begin=GetTickCount();<br /> BucketSort(buf,lNum-1,tempbuf);<br /> long end=GetTickCount();<br /> printf("%d ",end-begin);<br /> CheckIt(buf,lNum);<br /> U_Arithmetic<UINT64>cSort;<br /> lNum=ReadIt(buf);<br /> begin=GetTickCount();<br /> cSort.QuickSort_U_S(0,lNum-1,buf);//快排,你可以找一个现成的替代<br /> end=GetTickCount();<br /> printf("%d ",end-begin);<br /> //这里需要做内存释放<br /> return 0;<br />}</p>
<p>哈希表,基本上算是最快的查找办法。每一个单元下边挂指针,解决冲突。而且,还有很多变体,你可以用内存中的一位表示一个桶。呵呵。这是一个非常好的利用内存的办法。数据量的增大,让我们每省一个字节都是非常有意义。我们称他为内存位图。其实,数组就是一个用下标做的不会有冲突的哈希。 没有一个方法可以包打天下,但是,你要知道他们在什么样的环境下使用才是更合适的,什么样的环境下是无所谓的。让我现在默写一个快排,我会出错,也得调试一段时间。总是不能完全的记住每一个语句。呵呵。不过,我认为这不重要。这样的函数库我都已经写好了,或者是有现成的了,我只需要记住何时选用他们就好了。直接调用不就得了。单纯的记住,没有作用。但是,你要知道他的思想。 </p>
<p>Strlen. Strlen函数是用的很多的函数,你可测试过他的时间耗费?每调用一次,程序都需要遍历一次你的字符串,直到碰到0为止。字符串短,次数少的时候还看不出来,相反的情况下尽量少用,为什么不能一次调用,保存结果,到处使用那? </p>
<p>你可以注意一下你的那些函数。关注的不只是功能,而是他们耗费的时间本身(很多时候我们对时间的要求大于对内存的要求)。能不能让你的程序更快点(当然是在程序稳定运行的基础上),而这种快仅限于对算法的选择,不包括语言的换用。你能把所有的C程序都改成汇编?没有必要。投入产出比太低了。不过这种追求也要看环境和用途。不可太过。过犹不及!什么东西都是这样,选择最合适,达到要求就好。过犹不及。还有很多需要做的东西,不是仅仅这一个玩意。 </p>
<p>如果你说自己精通c,c++,那么如果你不知道,不了解STL,那是不可想象的。以前我也曾犯过这样的错误,认为靠我们自己写的那些类,就不用看STL了。其实不然。如果你跳槽来到另外一家公司,原来的代码统统原封不动的搬过来?如果,这家公司没有太多的技术积累,能够直接利用的、效率比较高的就是STL了。当然,没有你为特定的应用编写的效率高;另外,STL之所以能够受到大家的交口称赞,他的设计思路还的确是有一套的。理解了会非常的有意义! </p>
<p>好了,罗里罗嗦讲了很多,其实也还没有做到点到为止。上面涉及到的每一个部分,仔细写写估计都可以写一本书,至少也是一章。只是希望给对这个行业、开发不熟悉的人介绍一下相应的东西。没有任何的东西是可以速成的,经验只会来自于对实际开发的例子的体会,介绍的内容只会让你有一个印象。你如果确实想进入这个行业的话,把涉及到的各个部分起码应该大体看看吧。只有大公司才有耐心慢慢的培养你,等你起来,很多小点的公司都是需要较短时间内能够接手工作的(我进中搜的一个星期后,开始负责外链处理程序,是同时进入公司的人中接手最快的,我一直都是很骄傲这个事情,虽然在进中搜之前没有做过搜索引擎开发)。我出来找工作的时候,工作很好找,原因在于我的经验。公司是花钱买断我们的经验。呵呵。不过,关于基础知识部分,没有什么好说的。做的越多,你就会越来越发现其中包含的那个叫做“数学的美”。Googl的黑板报上就出了这么一系列文章叫做“数学的美”。我的大学数学学的不好,基本没有听课,有时间得补补。呵呵。其他的各个搜索引擎多半都会做一个他自己的博客,发布一下自己的消息,散布一些自己的论文和研究成果,有时间得看看。 </p>
<p>每次写关于一点关于搜索引擎的东西的时候,都会提起中搜(爱之深,恨之切,虽然我已经离开),人总是需要感恩的。能从在中搜锻炼两年,与很多技术人员相比,我是非常幸运的。在这里,我发现了和以前做应用绝对不同的技术世界,我在这里见识了大规模数据的处理,学会了计算自己的硬、软、人资源的需求,学会了处理多个人之间的接口,和很多人通力合作:你需要相信你的队友,相信他们能和你一样的努力,相信他们可以和你做的同样的好,如果你干的不错,你需要努力的多做一点,就如同篮球队里边的协防,是的,你的队友有时需要你的协防,我们都多为他人、多为项目想一点,接口问题、配合问题将不成为问题。一个项目的完成、胜利才是我们想要的。。。。。。。。。我们同时又是不幸的,我们努力过,非常非常的努力过,却没有看到中搜如百度般的成功。向现在仍能坚守在中搜的人致敬! </p>
<p>关于涉及到的内容,如果以后时间允许,我会各个多总结一点。 不过,写的越具体详细,离不能说的东西也就越近(不过,随着这个行业规模的扩大,不能说的越来越少,呵呵,这是我们大家都希望看到的)。我上边所提到的大多是我的思想和思考,总结,这样省下很多的麻烦!好在很多的基础知识都是相同的,从那些方面入手,不会给人造成误解。但愿看的人能有收获!不枉我写了这么多!</p>
</div>搜索引擎技术 资源savagertnullhttp://liyanblog.cn/articles/2012/10/19/1350624345668.html2013-03-05T14:47:10+08:00<div class="content-head clearfix">
<h2 class="title content-title">搜索引擎技术 资源</h2>
</div>
<div id="content" class="content mod-cs-content text-content clearfix">
<p><strong>一、搜索引擎技术/动态资源</strong></p>
<p><strong><一>、综合类</strong></p>
<p>1、卢亮的搜索引擎研究 http://www.wespoke.com/</p>
<p>卢亮属于搜索引擎开发上的专家,以前开发过一个搜索引擎"博索"(http://booso.com/),好像现在已经停止开发了,目前他服务于博客网。在他的这个blog上可以了解许多搜索引擎开发的技术和经验,值得持续关注。</p>
<p>2、laolu'blog </p>
<p>有不少来自国外的关于搜索引擎方面的资料,偏重于资料和数字</p>
<p>3、哈斯日志http://www.loverty.org/</p>
<p>在这里可以看到国内外几大搜索引擎的最新动态,值得关注搜索发展形势的人多看看</p>
<p>4、北京奕天锐新科技有限公司 http://www.21cnbj.com/</p>
<p>搜索引擎、SEO、SEM等行业新闻动态</p>
<p>5、<strong>中文搜索引擎指南网 </strong>http://www.sowang.com/</p>
<p>搜索引擎最新动态,各种搜索技巧、方法</p>
<p>6、中文全文检索网 http://www.fullsearcher.com/</p>
<p>FullSearcher.Com是有两个对搜索爱好的年轻人创办,我们的目标是让中文互联网全面进入搜索时代,让搜索无处不在。通过搜索改变人们的生活。<br /> FullSearcher提供全文检索的相关知识、垂直搜索引擎知识、搜索的相关新闻等搜索相关内容。</p>
<p>7、周博——每天9点档的搜索引擎动态 </p>
<p>8、李彦宏的博客 http://hi.baidu.com/liyanhong</p>
<p>9、中科院软件所-张俊林博客 http://blog.csdn.net/malefactor/</p>
<p>10.李岩的技术文章(专注搜索技术)博客http://hi.baidu.com/savagert/blog</p>
<p>搜索引擎技术研究</p>
<p><strong><二>、Google动态</strong></p>
<p><strong>Google官方博客</strong>:Google 黑板报 http://googlechinablog.com/</p>
<p> <strong>Google 中国的博客网志,走近我们的产品、技术和文化</strong></p>
<p> </p>
<p><strong>1、Gfans</strong> http://gfans.org/</p>
<p> 一群Google的粉丝</p>
<p>这里没有 PageRank,没有 HillTop,没有 SEO。如果 Google 是龙井,我希望这里便是虎跑,去化开那馥郁如兰之香。观于沧海者难为水,搜于 Google 者难为言,Google 已不只是文化,他是我的信仰。</p>
<p>本站文章约法三章: </p>
不讨论 SEO 及相关; <br />不得无聊转载; <br />严禁侮辱百度。
<p><strong>2、G速客 http://www.gseeker.com/</strong></p>
<p><strong>Google最新动态</strong></p>
<p><strong><二>、其他搜索引擎动态</strong></p>
<p><strong>1、雅虎搜索日志</strong> http://ysearchblog.cn/</p>
<p>记录雅虎搜索引擎的动态、产品、技术等</p>
<p><strong>2、搜狗实验室</strong> http://www.sogou.com/labs/</p>
<p>搜 狗实验室(Sogou Labs)是搜狗搜索核心研发团队对外交流的窗口,包含创意产品、原型演示、资料下载、学术论文四个栏目。实验室热烈欢迎一直以来关注搜狗、支持搜狗的各 位互联网玩家;对于致力于中文互联网研究的学术界同仁们的经常来访,也予以热切的期待。我们期望通过这个平台,展现搜狗研发团队强大的研发、创新能力;推 动学术界和产业界的交互;了解用户对新产品的需求。我们的目标:为中文网民的互联网生活提供更加全面、更加优质的服务。</p>
<p><strong>搜狗实验室博客</strong> http://labs.blog.sohu.com/</p>
<p><strong>3、百度的空间</strong>http://hi.baidu.com/baidu</p>
<p>百度的动态</p>
<p><strong>4、有道搜索博客</strong> http://i.yodao.com/</p>
<p>网易新推搜索引擎--有道搜索的近期动态</p>
<p> </p>
<p><strong>5、Live Search's WebLog</strong> http://blogs.msdn.com/livesearch/</p>
<p>Microsoft <strong>Live Search's news</strong></p>
<p><strong>二、搜索引擎代码资源</strong></p>
<p><strong>一>、搜索引擎/网络蜘蛛程序代码</strong></p>
<p>国外开发的相关程序</p>
<p><strong>1、Nutch</strong></p>
<p>官方网站 http://www.nutch.org/<br />中文站点 http://www.nutchchina.com/<br />最新版本:Nutch 0.7.2 Released</p>
<p>Nutch 是一个开源Java 实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具,可以建立自己内部网的搜索引擎,也可以针对整个网络建立搜索引擎。自由(Free)而免费(Free)。</p>
<p><strong>2、Lucene</strong></p>
<p>官方网站 http://lucene.apache.org<br />中文站点 http://www.lucene.com.cn/</p>
<p>Lucene 是apache软件基金会 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包[用Java写的],即它不是一个完整的全文检索引擎,而是一个全文检索引擎的 架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以 方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。</p>
<p><strong>3、Larbin</strong>: http://larbin.sourceforge.net/index-eng.html</p>
<p>larbin是一种开源的网络爬虫/网络蜘蛛,由法国的年轻人 Sébastien Ailleret独立开发。larbin目的是能够跟踪页面的url进行扩展的抓取,最后为搜索引擎提供广泛的数据来源。</p>
<p>国内开发的相关程序</p>
<p><strong>1、SQLET - 开放源码的中文搜索引擎</strong> </p>
<p>官方网站 http://www.sqlet.com/</p>
<p>SQLET, 是Search & Query &Link, 加后缀 let,表示小的,小型的意思.打算建立一个能搜上亿张网页的基于主题功能的中文搜索引擎.支持3种索引方式:MySql_table_Index, Lucene_Index,SQLET_Index.网页抓取可以保存在文件系统及数据库里。自带WebServer.</p>
<p><strong>2、菲度垂直搜索引擎代码</strong></p>
<p>菲度http://www.faydu.net 为一个垂直在线搜索的演示版,主要对国内一些购物站点进行搜索整理,</p>
<p>现在开源测试版本的代码,供大家讨论。下载说明:</p>
<p>1》因为本程序是在服务器上运行,是在多个处理器下运行的,个人电脑上请控制线程数量</p>
<p>2》包含一个data 的数据库 还原到sql server</p>
<p>3》收集完毕默认在bin目录有licene生成的反排的索引文件</p>
<p>4》下载地址:http://www.faydu.net/download/code.rar</p>
<p>开放日期:2006-4-18 来源:http://blog.csdn.net/faydu/archive/2006/04/18/667997.aspx<br />语言:VB.net(c#)</p>
<p><strong>二>、中文分词程序代码</strong></p>
<p><strong>1、计算所汉语词法分析系统 ICTCLAS</strong></p>
<p>中 国科学院计算技术研究所在多年研究基础上,耗时一年研制出了基于多层隐马模型的汉语词法分析系统 ICTCLAS(Institute of Computing Technology, Chinese Lexical Analysis System),该系统的功能有:中文分词;词性标注;未登录词识别。分词正确率高达97.58%(最近的973专家组评测结果),基于角色标注的未登录 词识别能取得高于90%召回率,其中中国人名的识别召回率接近98%,分词和词性标注处理速度为31.5KB/s。ICTCLAS 和计算所其他14项免费发布的成果被中外媒体广泛地报道,国内很多免费的中文分词模块都或多或少的参考过ICTCLAS的代码。</p>
<p>下载页面:http://www.nlp.org.cn/project/project.php?proj_id=6</p>
<p>由于 ICTCLAS 是由 C 语言写成的,现在主流的开发工具用起来不太方便,于是有一些热心的程序员把 ICTCLAS 改为 Java 和 C# 等其他语言。</p>
<p>(1)fenci,Java 的 ICTCLAS,下载页面:http://www.xml.org.cn/printpage.asp?BoardID=2&id=11502</p>
<p>(2)AutoSplit,另一个 Java 的 ICTCLAS,已经找不到下载页面,点击本地下载</p>
<p>(3)小叮咚中文分词,曾经有下载页面,现在找不到了。据作者介绍,从 ICTCLAS 中改进,有 Java,C# 和 C++ 三个版本,介绍页面:http://www.donews.net/accesine</p>
<p><strong>2、海量智能分词研究版</strong></p>
<p>海量智能计算技术研究中心为了使中文信息处理领域的研究者们能够共同分享海量智能中心的研究成果,共同提高中文信息处理水平,特此发布《海量智能分词研究版》,供专家、学者和爱好者进行研究。</p>
<p>下载页面:http://www.hylanda.com/cgi-bin/download/download.asp?id=8</p>
<p><br /><strong>3、其他</strong></p>
<p>(1)CSW中文智能分词组件</p>
<p>运行环境:Windows NT、2000、XP 或更高,可以在 ASP,VB 等微软的开发语言中调用。</p>
<p>简介: CSW中文智能分词DLL组件,可将一段文本自动的按常规汉语词组进行拆分,并以指定方式进行分隔,且可对其拆分后的词组进行语义、词频标注。其广范应用于各行各业的信息资料检索、分析。</p>
<p>下载页面:http://www.vgoogle.net/</p>
<p>(2) C# 写的中文分词组件</p>
<p>据作者介绍,一个 DLL 文件,可以做中英文分词组件。完全C#托管代码编写,独立开发。</p>
<p>下载页面:http://www.rainsts.net/article.asp?id=48</p>
<p><strong>三>、</strong></p>
<p>spider是搜索引擎的必须模块.spider数据的结果直接影响到搜索引擎的评价指标.</p>
<p>第一个spider程序由MIT的Matthew K Gray操刀该程序的目的是为了统计互联网中主机的数目</p>
<p>Spier定义(关于Spider的定义,有广义和狭义两种).</p>
狭义:利用标准的http协议根据超链和web文档检索的方法遍历万维网信息空间的软件程序.广义:所有能利用http协议检索web文档的软件都称之为spider.
<p>其中Protocol Gives Sites Way To Keep Out The 'Bots Jeremy Carl, Web Week, Volume 1, Issue 7, November 1995 是和spider息息相关的协议,大家有兴趣参考<a href="http://www.robotstxt.org/wc/robots.html" target="_blank">robotstxt.org</a>.</p>
Heritrix
<p>Heritrix is the Internet Archive's open-source, extensible, web-scale, archival-quality web crawler project.</p>
<p><em>Heritrix</em> (sometimes spelled <em>heretrix</em>, or misspelled or missaid as <em>heratrix</em>/<em>heritix</em>/ <em>heretix</em>/<em>heratix</em>) is an archaic word for <em>heiress</em> (woman who inherits). Since our crawler seeks to collect and <em>preserve</em> the digital artifacts of our culture for the benefit of future researchers and generations, this name seemed apt.</p>
<p>语言:JAVA, (下载地址)http://sourceforge.net/project/showfiles.php?group_id=73833&package_id=73980</p>
<strong>WebLech URL Spider<br /></strong>
<p>WebLech is a fully featured web site download/mirror tool in Java, which supports many features required to download websites and emulate standard web-browser behaviour as much as possible. WebLech is multithreaded and comes with a GUI console.</p>
<p>语言:JAVA, (下载地址)http://sourceforge.net/project/showfiles.php?group_id=38170</p>
<p><strong>JSpider</strong></p>
<p>A Java implementation of a flexible and extensible web spider engine. Optional modules allow functionality to be added (searching dead links, testing the performance and scalability of a site, creating a sitemap, etc ..</p>
<p>语言:JAVA, (下载地址)http://sourceforge.net/project/showfiles.php?group_id=65617</p>
<p><strong>WebSPHINX</strong></p>
<p>WebSPHINX is a web crawler (robot, spider) Java class library, originally developed by Robert Miller of Carnegie Mellon University. Multithreaded, tollerant HTML parsing, URL filtering and page classification, pattern matching, mirroring, and more.</p>
<p>语言:JAVA, (下载地址)http://sourceforge.net/project/showfiles.php?group_id=48810</p>
<p><strong>PySolitaire</strong></p>
<p>PySolitaire is a fork of PySol Solitaire that runs correctly on Windows and has a nice clean installer. PySolitaire (Python Solitaire) is a collection of more than 300 solitaire and Mahjongg games like Klondike and Spider.</p>
<p>语言:Python , (下载地址)http://sourceforge.net/project/showfiles.php?group_id=86107</p>
<p><strong>The Spider Web Network Xoops Mod Team</strong> </p>
<p>The Spider Web Network Xoops Module Team provides modules for the Xoops community written in the PHP coding language. We develop mods and or take existing php script and port it into the Xoops format. High quality mods is our goal.</p>
<p>语言:php , (下载地址)http://sourceforge.net/projects/tswnmoddev</p>
<p><strong>Fetchgals</strong></p>
<p>A multi-threaded web spider that finds free porn thumbnail galleries by visiting a list of known TGPs (Thumbnail Gallery Posts). It optionally downloads the located pictures and movies. TGP list is included. Public domain perl script running on Linux.</p>
<p>语言:perl , (下载地址)http://sourceforge.net/project/showfiles.php?group_id=110338</p>
<p><strong>Where Spider</strong></p>
<br />
<p>The purpose of the Where Spider software is to provide a database system for storing URL addresses. The software is used for both ripping links and browsing them offline. The software uses a pure XML database which is easy to export and import.</p>
<p>语言:XML , (下载地址)http://sourceforge.net/project/showfiles.php?group_id=115931</p>
<p><strong>Sperowider</strong></p>
<p>Sperowider Website Archiving Suite is a set of Java applications, the primary purpose of which is to spider dynamic websites, and to create static distributable archives with a full text search index usable by an associated Java applet.</p>
<p>语言:Java , (下载地址)http://sourceforge.net/project/showfiles.php?group_id=90254</p>
<p><strong>SpiderPy</strong></p>
<p>SpiderPy is a web crawling spider program written in Python that allows users to collect files and search web sites through a configurable interface.</p>
<p>语言:Python , (下载地址)http://sourceforge.net/project/showfiles.php?group_id=55531</p>
<p><strong>Spidered Data Retrieval</strong></p>
<p>Spider is a complete standalone Java application designed to easily integrate varied datasources. * XML driven framework * Scheduled pulling * Highly extensible * Provides hooks for custom post-processing and configuration</p>
<p>语言:Java , (下载地址)http://sourceforge.net/project/showfiles.php?group_id=90769</p>
<p><strong>webloupe</strong></p>
<p>WebLoupe is a java-based tool for analysis, interactive visualization (sitemap), and exploration of the information architecture and specific properties of local or publicly accessible websites. Based on web spider (or web crawler) technology.</p>
<p>语言:java , (下载地址)http://sourceforge.net/project/showfiles.php?group_id=121963</p>
<p><strong>ASpider</strong></p>
<p>Robust featureful multi-threaded CLI web spider using apache commons httpclient v3.0 written in java. ASpider downloads any files matching your given mime-types from a website. Tries to reg.exp. match emails by default, logging all results using log4j.</p>
<p>语言:java , (下载地址)http://sourceforge.net/project/showfiles.php?group_id=126578</p>
<p><strong>larbin</strong></p>
<p>Larbin is an HTTP Web crawler with an easy interface that runs under Linux. It can fetch more than 5 million pages a day on a standard PC (with a good network).</p>
<p>语言:C++, (下载地址)http://sourceforge.net/project/showfiles.php?group_id=42562</p>
</div>正向最大匹配法 &逆向最大匹配法 原理对比savagertnullhttp://liyanblog.cn/articles/2012/10/19/1350624315902.html2012-10-19T13:25:15+08:00<div class="content-head clearfix">
<h2 class="title content-title">正向最大匹配法 &逆向最大匹配法 原理对比</h2>
</div>
<div id="content" class="content mod-cs-content text-content clearfix">
<p>下面介绍的分词算法中最简单的正向最大匹配和反向最大匹配。<br />这种两种方法都是机械分词方法,它是按照一定的策略将待分析的汉字串与一个”充分大的”机器词典中的词条进行配,若在词典中找到某个字符串,则匹配成功(识别出一个词)。<br />按照扫描方向的不同,串匹配分词方法可以分为正向匹配和逆向匹配;按照不同长度优先匹配的情况,可以分为最大(最长)匹配和最小(最短)匹配;按照是否与词性标注过程相结合,又可以分为单纯分词方法和分词与标注相结合的一体化方法。常用的几种机械分词方法如下:<br />1)正向最大匹配法(由左到右的方向);<br />2)逆向最大匹配法(由右到左的方向);<br />3)最少切分(使每一句中切出的词数最小)。<br />还可以将上述各种方法相互组合,例如,可以将正向最大匹配方法和逆向最大匹配方法结合起来构成双向匹配法。由于汉语单字成词的特点,正向最小匹配和逆向最小匹配一般很少使用。一般说来,逆向匹配的切分精度略高于正向匹配,遇到的歧义现象也较少。统计结果表明,单纯使用正向最大匹配的错误率为1/169,单纯使用逆向最大匹配的错误率为1/245。但这种精度还远远不能满足实际的需要。实际使用的分词系统,都是把机械分词作为一种初分手段,还需通过利用各种其它的语言信息来进一步提高切分的准确率。<br />一种方法是改进扫描方式,称为特征扫描或标志切分,优先在待分析字符串中识别和切分出一些带有明显特征的词,以这些词作为断点,可将原字符串分为较小的串再来进机械分词,从而减少匹配的错误率。另一种方法是将分词和词类标注结合起来,利用丰富的词类信息对分词决策提供帮助,并且在标注过程中又反过来对分词结果进行检验、调整,从而极大地提高切分的准确率</p>
<p>定义比较抽象,举个例子来说明正向最大匹配和反向最大匹配。</p>
<p>例子:’今天来了许多新同事’<br />1.正向最大匹配方式,最大长度为5<br />今天来了许<br />今天来了<br />今天来<br />今天 ====》 得到一个词–今天<br />来了许多新<br />来了许多<br />来了许<br />来了<br />来 ====》 得到一个词–来<br />了许多新同<br />了许多新<br />了许多<br />了许<br />了 ====》 得到一个词–了<br />许多新同事<br />许多新同<br />许多新<br />许多 ====》得到一个词– 许多<br />新同事<br />新同<br />新 ====》得到一个词– 新<br />同事 ====》得到一个词– 同事</p>
<p>最后正向最大匹配的结果是:</p>
<p>/今天/来/了/许多/新/同事/</p>
<p>2.反向最大匹配方式,最大长度为5<br />许多新同事<br />多新同事<br />新同事<br />同事 ====》得到一个词– 同事<br />来了许多新<br />了许多新<br />许多新<br />多新<br />新 ====》得到一个词– 新<br />天来了许多<br />来了许多<br />了许多<br />许多 ====》得到一个词– 许多<br />今天来了<br />天来了<br />来了<br />了 ====》得到一个词– 了<br />今天来<br />天来<br />来 ====》得到一个词– 来<br />今天 ====》得到一个词– 今天</p>
<p>最后反向最大匹配的结果是:</p>
<p>/今天/来/了/许多/新/同事/</p>
<p>正向最大匹配和反向最大匹配的结果并不一定相同</p>
<p>例子:’我一个人吃饭’<br />1.正向最大匹配方式,最大长度为5<br />我一个人吃<br />我一个人<br />我一个<br />我一<br />我 ====》得到一个词– 我<br />一个人吃饭<br />一个人吃<br />一个人<br />一个 ====》得到一个词– 一个<br />人吃饭<br />人吃<br />人 ====》得到一个词– 人<br />吃饭 ====》得到一个词– 吃饭</p>
<p>最后正向最大匹配的结果是:</p>
<p>/我/一个/人/吃饭/</p>
<p>2.反向最大匹配方式,最大长度为5<br />一个人吃饭<br />个人吃饭<br />人吃饭<br />吃饭 ====》得到一个词– 吃饭<br />我一个人<br />一个人<br />个人 ====》得到一个词– 个人<br />我一<br />一 ====》得到一个词– 一<br />我 ====》得到一个词– 我</p>
<p>最后反向最大匹配的结果是:</p>
<p>/我/一/个人/吃饭/</p>
<p>这次两种方式的结果就不一致了。</p>
</div>