山间明月文化传媒作品集 既然选择了远方,便只顾风雨兼程

PHP实现敏感词过滤系统

安装说明

  1. 安装PHP扩展 trie_filter,安装教程 http://blog.41ms.com/post/39.html

  2. 安装PHP扩展 swoole,安装教程 http://www.swoole.com/


代码说明

1、敏感词库维护更新脚本:

reload_dict.php,提供自动更新字典库到trie-tree文件的过程

<?php

// 设置内存
ini_set('memory_limit', '128M');

// 读取敏感词字典库
$handle = fopen('dict.txt', 'r');

// 生成空的trie-tree-filter
$resTrie = trie_filter_new();

while(! feof($handle)) {
    $item = trim(fgets($handle));

    if (empty($item)) {
        continue;
    }

    // 把敏感词逐个加入trie-tree
    trie_filter_store($resTrie, $item);
}

// 生成trie-tree文件
$blackword_tree = 'blackword.tree';

trie_filter_save($resTrie, $blackword_tree);


2、trie树对象获取工具类

FilterHelper.php,提供获取trie-tree对象,避免重复生成trie-tree对象和保证tree文件与敏感词库的同步更新

<?php
/**
 * 过滤器助手
 *
 * getResTrie 提供trie-tree对象;
 * getFilterWords 提取过滤出的字符串
 *
 * @author W.Y.P (wangyupeng@jiayuan.com)
 */


class FilterHelper
{

    // trie-tree对象
    private static $_resTrie = null;
    // 字典树的更新时间
    private static $_mtime = null;


    /**
     * 防止初始化
     */
    private function __construct() {}


    /**
     * 防止克隆对象
     */
    private function __clone() {}


    /**
     * 提供trie-tree对象
     *
     * @param $tree_file 字典树文件路径
     * @param $new_mtime 当前调用时字典树的更新时间
     * @return null
     */
    static public function getResTrie($tree_file, $new_mtime) {

        if (is_null(self::$_mtime)) {
            self::$_mtime = $new_mtime;
        }

        if (($new_mtime != self::$_mtime) || is_null(self::$_resTrie)) {
            self::$_resTrie = trie_filter_load($tree_file);
            self::$_mtime = $new_mtime;

            // 输出字典文件重载时间
            echo date('Y-m-d H:i:s') . "\tdictionary reload success!\n";
        }

        return self::$_resTrie;
    }


    /**
     * 从原字符串中提取过滤出的敏感词
     *
     * @param $str 原字符串
     * @param $res 1-3 表示 从位置1开始,3个字符长度
     * @return array
     */
    static public function getFilterWords($str, $res)
    {
        $result = array();
        foreach ($res as $k => $v) {
            $word = substr($str, $v[0], $v[1]);

            if (!in_array($word, $result)) {
                $result[] = $word;
            }
        }

        return $result;
    }
}


3、对外提供过滤HTTP访问接口

filter.php,使用swool,对外提交过滤接口访问

<?php

// 设置脚本最大运行内存,根据字典大小调整
ini_set('memory_limit', '512M');

// 设置时区
date_default_timezone_set('Asia/Shanghai');

// 加载助手文件
require_once('FilterHelper.php');

// http服务绑定的ip及端口
$serv = new swoole_http_server("182.92.177.16", 9502);


/**
 * 处理请求
 */
$serv->on('Request', function($request, $response) {

    // 接收get请求参数
    $content = isset($request->get['content']) ? $request->get['content']: '';

    $result = '';

    if (!empty($content)) {

        // 字典树文件路径,默认当时目录下
        $tree_file = 'blackword.tree';

        // 清除文件状态缓存
        clearstatcache();

        // 获取请求时,字典树文件的修改时间
        $new_mtime = filemtime($tree_file);

        // 获取最新trie-tree对象
        $resTrie = FilterHelper::getResTrie($tree_file, $new_mtime);

        // 执行过滤
        $arrRet = trie_filter_search_all($resTrie, $content);

        // 提取过滤出的敏感词
        $a_data = FilterHelper::getFilterWords($content, $arrRet);

        $result = json_encode($a_data);
    }

    // 定义http服务信息及响应处理结果
    $response->cookie("User", "W.Y.P");
    $response->header("X-Server", "W.Y.P WebServer(Unix) (Red-Hat/Linux)");
    $response->header('Content-Type', 'Content-Type: text/html; charset=utf-8');
    $response->end($result);
});

$serv->start();


测试效果

词库内容:

QQ截图20151017152352.png

接口响应过滤结果:

QQ截图20151017152201.png

尝试更新敏感词库,接口程序已自动加载最新敏感词库,保证过滤效果

QQ截图20151017152517.png

ab测试结果

词库:200W敏感词

服务器配置(CPU:1核内存:1024 MB;带宽:1Mbps

QQ截图20151025095426.png

标签: php

作者:松鼠先生 分类:技术分享 浏览:25778 评论:128
留言列表
访客
访客 词库多大?  回复
访客
访客 问一下,生成的词库有其他保存方式吗,查了很久,发现都是只有文件保存这一种方式,能直接保存到redis这种缓存系统里面吗  回复
访客
访客 1  回复
aixgl
aixgl 你的这个提交点完没提示啊;bug 有点明显哈  回复
aixgl
aixgl 狂赞狂赞 trie_filter 让我认识了这个扩展  回复
小丁
小丁 博主你好 php的版本是多少 php7可以吗  回复
松鼠先生
松鼠先生 没有自动分词的功能  回复
刘sir
刘sir 怒赞!  回复
晨雨零稀
晨雨零稀 自已分词了?比如“性感美女”是敏感词,“性感”也当作第敏感词了,这就不好了吧。  回复
刘华
刘华 swoole这个安装了扩展 还需安装说明别的么?  回复
Hoheart
Hoheart 我想要你的200w敏感词库,能给我吗?谢啦谢啦  回复
松鼠先生
松鼠先生 那个并不是敏感词库,是公司内部业务关键字词库,对你并没什么用  回复
游客
游客 楼主测试过,利用这种方式和redis这种内存读取效率哪个高没有?  回复
松鼠先生
松鼠先生 不知道你所说的redis在这个系统里可以做什么?我写的这种方式已经是,一旦服务启动,词库就加载到内存中了。  回复
游客
游客 是文件缓存,硬盘吧,和内存还是有区别的。  回复
松鼠先生
松鼠先生 请认真阅读代码,熟悉下用到的扩展,你就明白了。用的就是内存,硬盘文件缓存是达不到这个效率的。  回复
youke
youke static public function getFilterWords($str, $res)
{
$result = array();
foreach ($res as $k =˃ $v) {
$word = substr($str, $v[0], $v[1]);
if (!in_array($word, $result)) {
$result[] = $word;
}
}
return $result;
}
用上面这个方法,还要trie树干嘛?  回复
松鼠先生
松鼠先生 试下200W条词库,看你这个方法效率如何  回复
游客
游客 trie树只是存储词库和读取词库方法。
和怎么查找并没有关系是吧?
就是当词库大的时候,效率会高是吧,
当只有几千条的时候,不如直接存。  回复
dpp
dpp 我在公司也搞了个类似的 已经运行4、5个月了 也是swoole+一个关键字扩展 没想到有人跟我一样。。。  回复
JerryYang
JerryYang 基于AC状态机的关键词过滤 https://github.com/hy0kl/algorithm/tree/master/AC  回复
松鼠先生
松鼠先生 嗯,已经浏览过,不知道效率如何?  回复
JerryYang
JerryYang 已被上测试报告.  回复
过过过过儿
过过过过儿 性能测试如何呢?  回复
松鼠先生
松鼠先生 性能测试已经补充至文章里  回复
y11201
y11201 good+1  回复
phperstar
phperstar 收藏  回复
发表评论
来宾的头像