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

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

面向对象升华:面向对象的认识----新生的初识面向对象的番外----思想的梦游篇(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‘] == "\r" || $scws_words[‘word‘] == "\n")) {
            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‘] == "\r" || $scws_words[‘word‘] == "\n")) {
            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‘] == "\r" || $scws_words[‘word‘] == "\n")) {
            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裙宽衣裙");

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

时间: 2024-11-05 12:30:28

手把手教你做关键词匹配项目(搜索引擎)---- 第二十一天的相关文章

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

第三天 小王(运营总监)看到小丁丁整天都在淘宝.百度.魔方.拍拍上面淘关键词,每天花费的时间好长,工作效率又低,拿着这个借口来找到我. 说到:小帅帅,你看小丁丁每天都在淘宝.百度.魔方.拍拍上面淘关键词花费的时间好长,你能不能帮帮忙,看看能不能让系统自己做啦,这样可以节省好多人力,带来的效益多高.(0 其实就是为了掩饰他们懒惰 0) 小帅帅一听到可以带来的效益好高,王总还求着我呢 ,马上 两眼冒着星光,是该好好体现, 解决这个问题就可以体现出我的价值. 小帅帅拍着胸膛保证到:王总,这个小KS啦,

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

第六天 小帅帅周五休息后,精神估计太旺盛了,周末两天就狂欢去了,酒喝高了,把一件重要的事儿给忘记了. 周一重新整装 刺骨上战场. 一来公司,小帅帅终于记得他要做的事情,就迫不及待的整理会议报告(工作总结). 1.上周工作任务: 1) 页面提交关键词到关键词词库 2) 文件导入到关键词词库 3) 自动抓取关键此到关键词词库 2.能力的提升 1) 学会了如何读csv文件 2)  学会了curl 3)  学会了Html Dom parse 3.下周工作任务: 1) 了解下关键词词库的应用 刚写到这儿,

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

第七天 小帅帅拿回去仔细研究了一个晚上. 发现代码其实都是自己写的,就多了一些类,于老大还不是抄的我的代码,心里又鄙视了于老大一番. 其实每个人都有通病,写过程的总是会鄙视写面向对象的,因为他们没体会到面向对象是啥玩意,要让他们写好可得花上好几年的工夫. 小帅帅学编程的时候,明明知道有函数这一概念,知道函数的写法,但是实际上就算一个函数里面几百行代码,也不知道去提前多个函数出来,美其名约:你看我多厉害,几百行代码耶. 小帅帅心里虽然鄙视于老大,但是看到于老大的代码怎么感觉很清爽,一切都那么自然.

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

第一天 收到需求,需求如下: 1. 收集关键词,构建关键词词库. 收到这个任务,第一想法,这还不简单吗? 马上动手创建一个关键词录入界面,保存到数据库. 第一步完成了,哈哈大笑了一天,没想到事情原来如此的简单. $keywords = $_POST["keywords"]; foreach($keywords as $keyword) { #save $keyword to database .............. } 手把手教你做关键词匹配项目(搜索引擎)---- 第一天

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

第九天 回顾: 8. 手把手教你做关键词匹配项目(搜索引擎)---- 第八天 7. 手把手教你做关键词匹配项目(搜索引擎)---- 第七天 6. 手把手教你做关键词匹配项目(搜索引擎)---- 第六天 5. 手把手教你做关键词匹配项目(搜索引擎)---- 第五天 4. 手把手教你做关键词匹配项目(搜索引擎)---- 第四天 3. 手把手教你做关键词匹配项目(搜索引擎)---- 第三天 2. 手把手教你做关键词匹配项目(搜索引擎)---- 第二天 1. 手把手教你做关键词匹配项目(搜索引擎)---

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

第八天 话说小帅帅自从走进了淘宝开放平台这个迷雾森林,感觉这迷雾森林好大,正当他无所适从的时候. 一位悦耳动听的声音响起来了,甜甜的声音说道:亲,想通过这片森林吗,我将指引你前进. 小帅帅一听,那种感觉,身体不由自主的跟随这声音而去,突然一道强光闪过,啊..... 小帅帅惊醒了.小帅帅一看时间,我滴个天,这么晚了.就这样小帅帅从业一来第一次迟到. 其实小帅帅在平台里面琢磨了一个晚上,整个晚上其实也没琢磨个啥出来. 正当要到公司的时候,手机的铃声响起来了,一看是于老大的电话,接通电话. 于老大问候

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

第十五天 小帅帅很乐意做简单的事情,复杂的事情他搞不懂怎么做,但是听了于老大的讲解,他觉得好多事情怎么这么简单. 他随手把一些代码写了出来,然后去找于老大,去请教以后怎么做...... <?php class OldAgeCharListHandle extends CharListHandle{ public function exec(){ $this->charlist->addCore("老年"); $this->charlist->addBlac

关键词匹配项目深入研究(二)- 分表思想的引入

(二)分表思想的引入 近期的文章: 1)高并发数据采集的架构应用(Redis的应用)                     2)高可用数据采集平台(如何玩转3门语言php+.net+aauto) 手把手教你做关键词匹配项目这块基本已经完成,深入研究是对系统的性能作为分析,在一些环境的刺激下所必需要做的一些改变. 手把手教你做关键词匹配项目: 手把手教你做关键词匹配项目(搜索引擎)---- 第一天~手把手教你做关键词匹配项目(搜索引擎)---- 第二十二天 (共22篇) 深入研究:上节讲到 关键

关键词匹配项目深入研究-过滤器的引入

关键词匹配项目深入研究(一) - 过滤器的引入 在开始看本篇时,请先了解 手把手教你做关键词匹配项目(搜索引擎)---- 第一天 ~ 手把手教你做关键词匹配项目(搜索引擎)---- 第二十二天 前言 接上回说到,小帅帅把完成后的代码,以及测试结果给出来后,于老大也没多说什么,就被要求上线了,原因很简单:大家都想有点贡献,福利也是少不了的,后期遇到的事情后期再来升级,那餐饭,小帅帅很happy,很满足,当然同行的有小丁丁,于老大,王总监. 问题的前因 随着项目的运行,采集关键词的应用日益广泛,突然