PHP 敏感词过滤扩展 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
gaozihang
V2EX    程序员

PHP 敏感词过滤扩展

  •  
  •   gaozihang 2018-06-12 09:33:35 +08:00 5903 次点击
    这是一个创建于 2748 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Build Status libdatrie PHP xfilter License

    关键词过滤扩展,用于检查一段文本中是否出现敏感词,基于 Double-Array Trie 树实现。

    依赖环境

    • PHP 7 +
    • libdatrie (Version >= 0.2.4)

    安装

    因为本项目依赖于 libdatrie, 所以需要先安装 libdatrie, 再安装本扩展。

    $ wget https://github.com/tlwg/libdatrie/releases/download/v0.2.11/libdatrie-0.2.11.tar.xz $ tar zxvf libdatrie-0.2.11.tar.xz $ cd libdatrie-0.2.11 $ ./configure && make && make install 

    PS: libdatrie 依赖于 libiconv, 如果编译的时候报 undefined reference to libiconv, 你需要先安装 libiconv。如果安装完还有错误可以执行这个命令 ./configure LDFLAGS=-L/usr/local/lib LIBS=-liconv

    $ git clone https://github.com/cdoco/xfilter.git && cd xfilter $ phpize && ./configure && make && make install 

    你可以在 php.ini 中设置字典文件的路径, 如果你不想在 php.ini 中设置你也可以在 setFileName 方法中设置。

    [xfilter] extension=xfilter.so // 你可以在这里设置加载的字典文件路径 xfilter.filename=/path/to/xfilter/blackword.dic 

    示例

    <?php use Cdoco\Filter; // 这个方法里可以传入词典的路径或者在 php.ini 中设置 Filter::setFileName(__DIR__ . '/blackword.dic'); // 保存敏感词到字典文件中 Filter::save(['敏感词', '高子航', 'xfilter']); // 搜索字符串中的敏感词 会返回敏感词的起始位置和长度 $rs = Filter::search('这是一个敏感词测试语句, 由高子航创造, xfilter, By ZiHang Gao。'); // Array // ( // [0] => Array // ( // [0] => 12 // [1] => 9 // ) // // [1] => Array // ( // [0] => 38 // [1] => 9 // ) // // [2] => Array // ( // [0] => 55 // [1] => 7 // ) // // ) print_r($rs); 

    方法

    setFileName

    该方法可以传入一个敏感词的字典文件路径。

    Filter::setFileName(string $filename); // 示例 Filter::setFileName(__DIR__ . '/blackword.dic'); 

    save

    保存一个敏感词数组到字典文件中, 如果在 setFileName 方法中设置了路径, 会优先使用 setFileName 方法中的路径, 如果没有会使用 php.ini 中设置的 xfilter.filename

    boolean Filter::save(array $blackword [, boolean $append = false]); // 示例 Filter::save(['敏感词', '高子航', 'xfilter'], true); 

    save 方法有两个参数, 第一个参数 $blackword 是一个敏感词的数组, 第二个参数 $append 用来表示是否是追加写入。

    <?php use Cdoco\Filter; Filter::setFileName(__DIR__ . '/blackword.dic'); Filter::save(['xfilter']); $rs = Filter::search('xfilter, By ZiHang Gao。'); // Array // ( // [0] => Array // ( // [0] => 0 // [1] => 7 // ) // // ) print_r($rs); // 不设置 $append 参数 save 方法会重新建立一个文件 Filter::save(['cdoco']); $rs = Filter::search('xfilter, By ZiHang Gao。'); // Array // ( // ) print_r($rs); // 如果设置了 $append 为 true, save 方法会在之前字典的基础上追加敏感词 Filter::save(['xfilter'], true); $rs = Filter::search('cdoco, By ZiHang Gao。'); // Array // ( // [0] => Array // ( // [0] => 0 // [1] => 5 // ) // // ) print_r($rs); 

    search

    搜索一个字符串中是否包含敏感词。

    array Filter::search(string $text); // 示例 Filter::search('这是一个敏感词测试语句, 由高子航创造, xfilter, By ZiHang Gao。'); 

    search 方法会返回一个二维数组, 包含敏感词出现的位置和长度, 你可以用 substr 方法截取出敏感词。

    Array ( [0] => Array ( [0] => 0 //敏感词出现的位置 [1] => 5 //敏感词的长度 ) ) <?php use Cdoco\Filter; // 截取字符串 $cOntent= '这是一个敏感词测试语句, 由高子航创造, xfilter, By ZiHang Gao。'; $rs = Filter::search($content); foreach ($rs as $v) { echo substr($content, $v[0], $v[1]) . "\n"; } // 敏感词 // 高子航 // xfilter 

    delete

    删除字典文件中的敏感词。

    boolean Filter::delete(string $keyword); // 示例 Filter::delete('高子航'); 

    感谢

    @wulijun 的 trie-filter 扩展已不维护更新, 目前使用起来有点繁琐。这个项目是根据自己的想法, 在 trie-filter 的基础上修改而来, 感谢 @wulijun。

    License

    PHP License. See the LICENSE file.

    16 条回复    2018-06-13 10:22:05 +08:00
    tanszhe
        1
    tanszhe  
       2018-06-12 09:45:49 +08:00
    感觉意义不大,作为学习练习还是可以。
    如果加上检查一个词语在不在字符串中就 strpos 就好了。

    一个词语过滤器 应当具有词语相识度识别的功能。换句话说就是这个词语没有在你的词库中 你也应该能识别出来。在实际场景中用户如果发现一个词语被限制了他会换一个词语 相近的词语。如果只是靠枚举 肯定是不全面的 而且新词语产生的非常快。词库的维护需要耗费很多精力。所有过滤器应当有自我进化的功能。
    hubqin
        2
    hubqin  
       2018-06-12 09:48:25 +08:00 via Android
    我们一般的做法是建一个敏感词表或 php 文件(return 一个数组的形式),将敏感词读取出来(数组),循环判断文本中是否含有
    surfire91
        3
    surfire91  
       2018-06-12 10:59:08 +08:00
    @tanszhe 你说的这个挺的,现在有哪家已经实现的吗?
    phperstar
        4
    phperstar  
       2018-06-12 11:12:25 +08:00
    @hubqin 你这个效率得多低呀....
    yankebupt
        5
    yankebupt  
       2018-06-12 11:43:31 +08:00
    不知道 double array trie 哪方面性能好一点...
    是大文本量好一点还是大量关键词好一点...
    看了一眼数据结构介绍,发现没见过.
    tanszhe
        6
    tanszhe  
       2018-06-12 13:52:44 +08:00
    @surfire91 没有见过,以前做过类似的,觉得这是最基本的功能。 这个功能如果都没有还不如直接用 @hubqin 的方法了,效率也不低。
    R18
        7
    R18  
       2018-06-12 13:56:59 +08:00
    直接用的接口,有问题也好甩锅
    changwei
        8
    changwei  
       2018-06-12 15:04:16 +08:00
    提个小建议哈,如果是强调性能的项目,最好带上 benchmark 测试结果的!
    Z1076
        9
    Z1076  
       2018-06-12 17:21:46 +08:00
    我是把敏感词直接扔数据库里面, 一个 where like 完事. 决定什么是敏感词都是运营那边的大佬负责添加.
    Z1076
        10
    Z1076  
       2018-06-12 17:23:41 +08:00
    @Z1076 噢 搞错了...
    nullen
        11
    nullen  
       2018-06-12 18:07:03 +08:00
    实现的蛮好。
    我们之前也是用 PHP 实现的 Trie,但是敏感词越来越多,词库越来越大,效率降低;
    而且 PHP 请求是运行完一次就全部销毁,逐渐成为一个瓶颈;
    后来我们把敏感词单独用 Go 实现成一个服务,感觉良好。
    owenliang
        12
    owenliang  
       2018-06-12 18:28:30 +08:00
    double array trie 吧?
    GoPHP
        13
    GoPHP  
       2018-06-12 18:33:26 +08:00
    @nullen 难道不能放缓存里面?这个敏感词应该也不会经常变吧,单独写成一个服务也是
    lowe
        14
    lowe  
       2018-06-12 19:05:40 +08:00
    @nullen 很巧,我之前有个项目就是从 github 上找的 golang 版本的 Trie 树敏感词过滤,也是做成服务
    nullen
        15
    nullen  
       2018-06-13 10:20:21 +08:00
    @GoPHP 你说的放缓存是指类似 Redis 缓存吗?这种效率还是不高。

    敏感词库缓存 -> TCP -> PHP。
    敏感词库文件 -> PHP。

    TCP 开销其实蛮大的。
    nullen
        16
    nullen  
       2018-06-13 10:22:05 +08:00
    @lowe :)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     907 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 23:25 PVG 07:25 LAX 15:25 JFK 18:25
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86