zoukankan      html  css  js  c++  java
  • 手把手教你做关键词匹配项目(搜索引擎)---- 第二十一天

    客串:屌丝的坑人表单神器数据库那点事儿

    面向对象升华:面向对象的认识----新生的初识面向对象的番外----思想的梦游篇(1)面向对象的认识---如何找出类

    负载均衡:负载均衡----概念认识篇负载均衡----实现配置篇(Nginx)

    吐槽:现在欠的文章有面向对象的认识----类的转化、面向对象的番外---思想的梦游篇(2)、负载均衡 ---- 文件服务策略、手把手教你做关键词匹配项目(搜索引擎)。真心太多了,能不能让我休息一会儿。

    第二十一天

    起点:手把手教你做关键词匹配项目(搜索引擎)---- 第一天

    回顾:手把手教你做关键词匹配项目(搜索引擎)---- 第二十天

    今天有个理论知识要理解的,叫做测试驱动编程,之前我提到过概念,在:手把手教你做关键词匹配项目(搜索引擎)---- 第十一天 

    今天小帅帅秀逗了一回,使用了这个思想。

    好了,以下正文开始。

    话说小帅帅把自己写的业务拆词方法给了于老大看,于老大很高兴。

    但是业务拆词的词组都是有限的,还有就是当业务拆词的数据量越来越大的时候,就会造成运算时间增加。

    于老大就提到,是否可以用其它分词扩展来弥补拆词的不足。

    毕竟人家专业人士做的,比较靠谱点。

    于老大很有经验,就推荐小帅帅去了解SCWS的用法.

    SCWS 是 Simple Chinese Word Segmentation 的首字母缩写(即:简易中文分词系统)。
    官方网址:http://www.xunsearch.com/scws/index.php

    小帅帅听了当然很开心罗,因为又有新的知识点了。

    小帅帅照着SCWS的安装文档安装了SCWS。

    并把php扩展装好了,并尝试写了个测试代码:

    <?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'] == "
    " || $scws_words['word'] == "
    ")) {
                return false;
            }
            return true;
        }
    
    }
    
    
    var_dump(TestSCWS::split("连衣裙xxl裙连衣裙"));

    测试通过,跟理想中的一摸一样,小帅帅很高兴,就去问于老大:于老大我会用SCWS了,下一步该怎么办?

    于老大也不慌,就对小帅帅说: 你先写个ScwsSplitter来拆分关键词吧。

    小帅帅非常高兴,因为他学到了新的知识,就对于老大说到好的。

    小帅帅说到做到,代码如下:

    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'] == "
    " || $scws_words['word'] == "
    ")) {
                return false;
            }
            return true;
        }
        
    }

    小帅帅又跑去找于老大,说到:我把Scws的分词代码写好了。

    于老大也佩服小帅帅的高效率。

    又说到:如果我两个同时用了,我先用业务分词,遗留下来的词用Scws分词,小帅帅有好的方案吗?

    小帅帅就问到: 为啥要这样,这不是多此一举。

    于老大就说到:业务有些专有名词,SCWS分不出来丫,那怎么办好?

    小帅帅又说到:我看文档的时候看到有词库和规则文件的设置,我们用它好不好?

    于老大又说到:这个是可以,但是我们如何保证让运营人员维护,我们要学会把这些事情交出去丫。

    小帅帅: …….

    小帅帅沉默了片刻,觉得现在两个类都写了,一起用是最快的方案,就答应到:好吧,我回去改改….

    首先小帅帅根据测试驱动编程的思想写了入口代码:

    class SplitterApp {
    
        public static function split($keyword,$cid){
    
            $keywordEntity = new KeywordEntity($keyword);
    
            #业务分词
            $termSplitter = new TermSplitter($keywordEntity);
            $seg = new DBSegmentation();
            $seg->cid = $cid;
            $termSplitter->setDictionary($seg->transferDictionary());
            $termSplitter->split();
    
            #SCWS分词
            $scwsSplitter = new ScwsSplitter($keywordEntity);
            $scwsSplitter->split();
    
            #后续遗留单词或者词组处理
            $elementWords = $keywordEntity->getElementWords();
            $remainKeyword = str_replace($elementWords, "::", $keywordEntity->keyword);
            $remainElements = explode("::", $remainKeyword);
            foreach($remainElements as $element){
                if(!empty($element))
                    $keywordEntity->addElement($element);
            }
         return $keywordEntity; } }

    小帅帅嘿了一声,有了测试入口,还怕其他的搞不定。

    首先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 计算UTF8字符串权重
         * @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;
        }
    }

    其次就是分词了,首先先抽出公用类先,Splitter变成了公用类,有哪些方法呢?

      1. 抽象split方法

          2. 获取关键词待拆分的词组

          3. 是否需要拆分

    按照这写,小帅帅写出了以下代码:

    abstract class Splitter {
    
        /**
         * @var KeywordEntity $keywordEntity
         */
        public $keywordEntity;
    
        public function __construct($keywordEntity){
            $this->keywordEntity = $keywordEntity;
        }
    
        public abstract function split();
    
    
        /**
         * 获取未分割的字符串,过滤单词
         *
         * @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;
        }
    }

    然后小帅帅继续实现业务拆分算法,以及Scws拆分算法。小帅帅淫笑了,这点小事情还是可以办到的。

    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;
        }
    
        /**
         * 把关键词拆分成词组或者单词
         *
         * @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'] == "
    " || $scws_words['word'] == "
    ")) {
                return false;
            }
            return true;
        }
    
    }

    小帅帅终于把这些代码全部搞定了,高兴之余,他还顺手画了UML图送给大家:

    小帅帅的成长真心够厉害的哦,于老大看后,连称赞了三次。

    为了测试,小帅帅写了测试代码,代码如下:

    class SplitterAppTest {
    
        public static function split($keyword){
    
            $keywordEntity = new KeywordEntity($keyword);
    
            #业务分词
            $termSplitter = new TermSplitter($keywordEntity);
            $seg = new TestSegmentation();
            $termSplitter->setDictionary($seg->transferDictionary());
            $termSplitter->split();
    
            #SCWS分词
            $scwsSplitter = new ScwsSplitter($keywordEntity);
            $scwsSplitter->split();
    
            #后续遗留单词或者词组处理
            $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裙宽衣裙");

    小帅帅意淫着,想到总有一天把你们踩在脚下。

  • 相关阅读:
    socket
    RBAC
    CMOS和BIOS
    canvas和SVG
    兼容IE,chrome,ff的设为首页、加入收藏及保存到桌面
    HTML标签marquee实现滚动效果
    百度判断手机终端并自动跳转uaredirect.js代码及使用实例
    JavaScript中常用的事件
    解决windows server 2008 r2 登录进入桌面只显示一片蓝色背景
    ng2自定义管道
  • 原文地址:https://www.cnblogs.com/oshine/p/3958063.html
Copyright © 2011-2022 走看看