手把手教你做关键词匹配项目(搜索引擎)---- 第二十一天
客串:屌絲的坑人表單神器、數(shù)據(jù)庫(kù)那點(diǎn)事兒
面向?qū)ο笊A:面向?qū)ο蟮恼J(rèn)識(shí)----新生的初識(shí)、面向?qū)ο蟮姆?---思想的夢(mèng)游篇(1)、面向?qū)ο蟮恼J(rèn)識(shí)---如何找出類(lèi)
負(fù)載均衡:負(fù)載均衡----概念認(rèn)識(shí)篇、負(fù)載均衡----實(shí)現(xiàn)配置篇(Nginx)
?
吐槽:現(xiàn)在欠的文章有面向?qū)ο蟮恼J(rèn)識(shí)----類(lèi)的轉(zhuǎn)化、面向?qū)ο蟮姆?--思想的夢(mèng)游篇(2)、負(fù)載均衡 ---- 文件服務(wù)策略、手把手教你做關(guān)鍵詞匹配項(xiàng)目(搜索引擎)。真心太多了,能不能讓我休息一會(huì)兒。
?
第二十一天
起點(diǎn):手把手教你做關(guān)鍵詞匹配項(xiàng)目(搜索引擎)---- 第一天
回顧:手把手教你做關(guān)鍵詞匹配項(xiàng)目(搜索引擎)---- 第二十天
今天有個(gè)理論知識(shí)要理解的,叫做測(cè)試驅(qū)動(dòng)編程,之前我提到過(guò)概念,在:手把手教你做關(guān)鍵詞匹配項(xiàng)目(搜索引擎)---- 第十一天?
今天小帥帥秀逗了一回,使用了這個(gè)思想。
好了,以下正文開(kāi)始。
?
話(huà)說(shuō)小帥帥把自己寫(xiě)的業(yè)務(wù)拆詞方法給了于老大看,于老大很高興。
但是業(yè)務(wù)拆詞的詞組都是有限的,還有就是當(dāng)業(yè)務(wù)拆詞的數(shù)據(jù)量越來(lái)越大的時(shí)候,就會(huì)造成運(yùn)算時(shí)間增加。
于老大就提到,是否可以用其它分詞擴(kuò)展來(lái)彌補(bǔ)拆詞的不足。
畢竟人家專(zhuān)業(yè)人士做的,比較靠譜點(diǎn)。
于老大很有經(jīng)驗(yàn),就推薦小帥帥去了解SCWS的用法.
SCWS 是 Simple Chinese Word Segmentation 的首字母縮寫(xiě)(即:簡(jiǎn)易中文分詞系統(tǒng))。
官方網(wǎng)址:http://www.xunsearch.com/scws/index.php
小帥帥聽(tīng)了當(dāng)然很開(kāi)心羅,因?yàn)橛钟行碌闹R(shí)點(diǎn)了。
小帥帥照著SCWS的安裝文檔安裝了SCWS。
并把php擴(kuò)展裝好了,并嘗試寫(xiě)了個(gè)測(cè)試代碼:
<?php class TestSCWS {public static function split($keyword){if (!extension_loaded("scws")) {throw new Exception("scws extension load fail");}$so = scws_new();$so->set_charset('utf8');$so->send_text($keyword);$ret = array();while ($res = $so->get_result()) {foreach ($res as $tmp) {if (self::isValidate($tmp)) {$ret[] = $tmp;}}}$so->close();return $ret;}public static function isValidate($scws_words){if ($scws_words['len'] == 1 && ($scws_words['word'] == "\r" || $scws_words['word'] == "\n")) {return false;}return true;}}var_dump(TestSCWS::split("連衣裙xxl裙連衣裙"));測(cè)試通過(guò),跟理想中的一摸一樣,小帥帥很高興,就去問(wèn)于老大:于老大我會(huì)用SCWS了,下一步該怎么辦?
于老大也不慌,就對(duì)小帥帥說(shuō): 你先寫(xiě)個(gè)ScwsSplitter來(lái)拆分關(guān)鍵詞吧。
小帥帥非常高興,因?yàn)樗麑W(xué)到了新的知識(shí),就對(duì)于老大說(shuō)到好的。
小帥帥說(shuō)到做到,代碼如下:
class ScwsSplitter {public $keyword;public function split(){if (!extension_loaded("scws")) {throw new Exception("scws extension load fail");}$keywordEntity = new KeywordEntity($this->keyword);$so = scws_new();$so->set_charset('utf8');$so->send_text($this->keyword);while ($res = $so->get_result()) {foreach ($res as $tmp) {if ($this->isValidate($tmp)) {$keywordEntity->addElement($tmp["word"]);}}}$so->close();return $keywordEntity;}public function isValidate($scws_words){if ($scws_words['len'] == 1 && ($scws_words['word'] == "\r" || $scws_words['word'] == "\n")) {return false;}return true;}}小帥帥又跑去找于老大,說(shuō)到:我把Scws的分詞代碼寫(xiě)好了。
于老大也佩服小帥帥的高效率。
又說(shuō)到:如果我兩個(gè)同時(shí)用了,我先用業(yè)務(wù)分詞,遺留下來(lái)的詞用Scws分詞,小帥帥有好的方案嗎?
小帥帥就問(wèn)到: 為啥要這樣,這不是多此一舉。
于老大就說(shuō)到:業(yè)務(wù)有些專(zhuān)有名詞,SCWS分不出來(lái)丫,那怎么辦好?
小帥帥又說(shuō)到:我看文檔的時(shí)候看到有詞庫(kù)和規(guī)則文件的設(shè)置,我們用它好不好?
于老大又說(shuō)到:這個(gè)是可以,但是我們?nèi)绾伪WC讓運(yùn)營(yíng)人員維護(hù),我們要學(xué)會(huì)把這些事情交出去丫。
小帥帥: …….
小帥帥沉默了片刻,覺(jué)得現(xiàn)在兩個(gè)類(lèi)都寫(xiě)了,一起用是最快的方案,就答應(yīng)到:好吧,我回去改改….
首先小帥帥根據(jù)測(cè)試驅(qū)動(dòng)編程的思想寫(xiě)了入口代碼:
class SplitterApp {public static function split($keyword,$cid){$keywordEntity = new KeywordEntity($keyword);#業(yè)務(wù)分詞$termSplitter = new TermSplitter($keywordEntity);$seg = new DBSegmentation();$seg->cid = $cid;$termSplitter->setDictionary($seg->transferDictionary());$termSplitter->split();#SCWS分詞$scwsSplitter = new ScwsSplitter($keywordEntity);$scwsSplitter->split();#后續(xù)遺留單詞或者詞組處理$elementWords = $keywordEntity->getElementWords();$remainKeyword = str_replace($elementWords, "::", $keywordEntity->keyword);$remainElements = explode("::", $remainKeyword);foreach($remainElements as $element){if(!empty($element))$keywordEntity->addElement($element);}return $keywordEntity;} }
小帥帥嘿了一聲,有了測(cè)試入口,還怕其他的搞不定。
首先KeywordEntity的getElementWords,先搞定他.
class KeywordEntity {public $keyword;public $elements = array();public function __construct($keyword){$this->keyword = $keyword;}public function addElement($word, $times = 1){if (isset($this->elements[$word])) {$this->elements[$word]->times += $times;} else$this->elements[$word] = new KeywordElement($word, $times);}public function getElementWords(){$elementWords = array_keys($this->elements);usort($elementWords, function ($a, $b) {return (UTF8::length($a) < UTF8::length($b)) ? 1 : -1;});return $elementWords;}/*** @desc 計(jì)算UTF8字符串權(quán)重* @param string $word* @return float*/public function calculateWeight($word){$element = $this->elements[$word];return ROUND(strlen($element->word) * $element->times / strlen($this->keyword), 3);} }class KeywordElement {public $word;public $times;public function __construct($word, $times){$this->word = $word;$this->times = $times;} }其次就是分詞了,首先先抽出公用類(lèi)先,Splitter變成了公用類(lèi),有哪些方法呢?
1. 抽象split方法
? ? ? 2. 獲取關(guān)鍵詞待拆分的詞組
? ? ? 3. 是否需要拆分
按照這寫(xiě),小帥帥寫(xiě)出了以下代碼:
abstract class Splitter {/*** @var KeywordEntity $keywordEntity*/public $keywordEntity;public function __construct($keywordEntity){$this->keywordEntity = $keywordEntity;}public abstract function split();/*** 獲取未分割的字符串,過(guò)濾單詞** @return array*/public function getRemainKeywords(){$elementWords = $this->keywordEntity->getElementWords();$remainKeyword = str_replace($elementWords, "::", $this->keywordEntity->keyword);$remainElements = explode("::", $remainKeyword);$ret = array();foreach ($remainElements as $element) {if ($this->isSplit($element)) {$ret[] = $element;}}return $ret;}/*** 是否需要拆分** @param $element* @return bool*/public function isSplit($element){if (UTF8::isPhrase($element)) {return true;}return false;} }然后小帥帥繼續(xù)實(shí)現(xiàn)業(yè)務(wù)拆分算法,以及Scws拆分算法。小帥帥淫笑了,這點(diǎn)小事情還是可以辦到的。
class TermSplitter extends Splitter {private $dictionary = array();public function setDictionary($dictionary = array()){usort($dictionary, function ($a, $b) {return (UTF8::length($a) < UTF8::length($b)) ? 1 : -1;});$this->dictionary = $dictionary;}public function getDictionary(){return $this->dictionary;}/*** 把關(guān)鍵詞拆分成詞組或者單詞** @return KeywordScore[] $keywordScores*/public function split(){foreach ($this->dictionary as $phrase) {$remainKeyword = implode("::",$this->getRemainKeywords());$matchTimes = preg_match_all("/$phrase/", $remainKeyword, $matches);if ($matchTimes > 0) {$this->keywordEntity->addElement($phrase, $matchTimes);}}} }class ScwsSplitter extends Splitter {public function split(){if (!extension_loaded("scws")) {throw new Exception("scws extension load fail");}$remainElements = $this->getRemainKeywords();foreach ($remainElements as $element) {$so = scws_new();$so->set_charset('utf8');$so->send_text($element);while ($res = $so->get_result()) {foreach ($res as $tmp) {if ($this->isValidate($tmp)) {$this->keywordEntity->addElement($tmp['word']);}}}$so->close();}}/*** @param array $scws_words* @return bool*/public function isValidate($scws_words){if ($scws_words['len'] == 1 && ($scws_words['word'] == "\r" || $scws_words['word'] == "\n")) {return false;}return true;}}小帥帥終于把這些代碼全部搞定了,高興之余,他還順手畫(huà)了UML圖送給大家:
小帥帥的成長(zhǎng)真心夠厲害的哦,于老大看后,連稱(chēng)贊了三次。
為了測(cè)試,小帥帥寫(xiě)了測(cè)試代碼,代碼如下:
class SplitterAppTest {public static function split($keyword){$keywordEntity = new KeywordEntity($keyword);#業(yè)務(wù)分詞$termSplitter = new TermSplitter($keywordEntity);$seg = new TestSegmentation();$termSplitter->setDictionary($seg->transferDictionary());$termSplitter->split();#SCWS分詞$scwsSplitter = new ScwsSplitter($keywordEntity);$scwsSplitter->split();#后續(xù)遺留單詞或者詞組處理$elementWords = $keywordEntity->getElementWords();$remainKeyword = str_replace($elementWords, "::", $keywordEntity->keyword);$remainElements = explode("::", $remainKeyword);foreach($remainElements as $element){if(!empty($element))$keywordEntity->addElement($element);}return $keywordEntity;} }SplitterAppTest::split("連衣裙xl裙寬衣裙");小帥帥意淫著,想到總有一天把你們踩在腳下。
?
轉(zhuǎn)載于:https://www.cnblogs.com/oshine/p/3958063.html
總結(jié)
以上是生活随笔為你收集整理的手把手教你做关键词匹配项目(搜索引擎)---- 第二十一天的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Leetcode: Multiply S
- 下一篇: 关于添加图片到svg中,rails下使用