- 目录
- 1.前言
- 方案1:PHP函数
- 方案2:正则匹配
- 方案3:全文搜索OR分词
- 方案4:API
- 2.方案实战
- 2.1创建敏感词存储数据库
- 2.2方案1:PHP函数
- 2.3方案2:正则匹配
- 2.4方案3:全文搜索OR分词
- 2.4.1案例一:使用PHPAnalysis提取中文分词进行匹配
- 2.4.2案例二:使用Elasticsearch全文搜索检测敏感词
- 2.5方案4:API
- 3.总结
1.前言
最近给公司开发商品评论系统,需求要对评论进行脱敏处理,因为之前没深入接触过,有些想当然的怎么简单怎么来,刚开始想用foreach循环匹配,但是组长一直说再想一想,想不到这里的水真深。先说说思想,我们一般人能够是通过数据库去匹配文本内容、如果敏感词库数据量大,就会出现典型的数据多匹配数据少,这样即增加开销、浪费资源,又可能增加了页面的响应时间;而符合生产环境的做法是将文本内容分词然后匹配数据库敏感词,这样少量的循环就能实现我们需要的效果,如下图所示。
可以看到我们传统思路是由数据库匹配文本,典型的多匹配少;所以思路逆转,少匹配多,采用文本分词手段,将需要的词匹配数据库,总的来说,当前方案一共讨论出四种。
方案1:PHP函数
优点:开发快
缺点:数据大效率低
方案2:正则匹配
优点:开发快,匹配度高
缺点:数据大效率低
方案3:全文搜索OR分词
优点:效率高
缺点:开发较复杂
方案4:API
优点:无需开发,直接调用
缺点:可能需要付费、无法自定义敏感词库
本章代码分享:https://github.com/mtdgclub/sensitiveWord
2.方案实战
2.1创建敏感词存储数据库
CREATE TABLE `t_sensitive_word` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `word` varchar(20) NOT NULL COMMENT '敏感字', `reason` varchar(20) DEFAULT NULL COMMENT '过滤原因', `is_del` tinyint(1) DEFAULT '0' COMMENT '是否删除 0不删除 1删除', `create_time` int(11) DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='敏感词库';
2.2方案1:PHP函数
代码实现如下:
/** * PHP函数,处理敏感词接口 * 作用:将敏感词变成*号,并返回处理后的文本 */ public function dealByFuncAction() { $word = $this->$_POST['appraise_msg']; if (!empty($word)) { //将敏感库初始静态化 if (empty(self::$save_words_array)) { //从数据库获得敏感词库 $m_sensitive_word = Helper::load('sensitive_word'); $sensitiveWordArray = $m_sensitive_word->Field('word')->Select(); //处理数组,组装 $dealArray = []; foreach ($sensitiveWordArray as $k => $v) { $dealArray[] = $v['word']; } self::$save_words_array = $dealArray; } //给敏感词打码 $badWords = array_combine(self::$save_words_array, array_fill(0, count(self::$save_words_array), '*')); $res = strtr($word, $badWords); $data = ['code' => 1, 'msg' => '处理成功', 'data' => $res]; } else { $data = ['code' => 0, 'msg' => '参数丢失']; } Helper::response($data); }
2.3方案2:正则匹配
/** * 正则匹配,处理敏感词接口 * 作用:检测提示敏感词 */ public function checkByRegularAction() { $word = $this->$_POST['appraise_msg']; if (!empty($word)) { //去掉文本多余字符 $flag_arr = array('•', '?', '!', '¥', '(', ')', ':', '‘', '’', '“', '”', '《', '》', ',', '…', '。', '、', 'nbsp', '】', '【', '~'); $content_filter = preg_replace('/\s/', '', preg_replace("/[[:punct:]]/", '', strip_tags(html_entity_decode(str_replace($flag_arr, '', $word), ENT_QUOTES, 'UTF-8')))); //将敏感库初始静态化 if (empty(self::$save_words_array)) { //从数据库获得敏感词库 $m_sensitive_word = Helper::load('sensitive_word'); $sensitiveWordArray = $m_sensitive_word->Field('word')->Select(); //处理数组,组装 $dealArray = []; foreach ($sensitiveWordArray as $k => $v) { $dealArray[] = $v['word']; } self::$save_words_array = $dealArray; } //正则匹配 $blacklist = "/" . implode("|", self::$save_words_array) . "/i"; self::$save_words_string = $blacklist; //判断 if (preg_match(self::$save_words_string, $content_filter, $matches)) { $data = ['code' => 0, 'msg' => '有敏感词', 'data' => $matches[0]]; } else { $data = ['code' => 1, 'msg' => '没有敏感词']; } } else { $data = ['code' => 0, 'msg' => '参数丢失']; } Helper::response($data); }
另外还有正则匹配将敏感词变成*号并返回处理文本的函数dealByRegular(),详见Sensitive.php文件
2.4方案3:全文搜索OR分词
关于中文分词引擎,现在市面上提供多种方案选择,比如:SCWS、Sphinx、PHPAnalysis等等;更有结合 scws(分词引擎) + xapian(搜索引擎) 构建的开源全文搜索引擎 xunsearch、高伸缩的开源全文搜索和分析引擎Elasticsearch 等成熟的全文搜索引擎插件。更深入的有检测算法->DFA算法(全名:Deterministic Finite Automaton)即有穷自动机。其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。
下面举两个案例:
2.4.1案例一:使用PHPAnalysis提取中文分词进行匹配
下载地址:https://github.com/mtdgclub/PHPAnalysis
核心代码如下:
/** * Notes:返回文本的所有分词结果(一维数组) * @param string $content * @return array|string */ public static function getAllParticiple($content = ""){ if (empty ( $content )) { return ''; } require_once 'phpanalysis.class.php'; $pa = new \PhpAnalysis ( 'utf-8', 'utf-8', false ); $pa->SetSource($content); $pa->resultType=2; $pa->differMax=true; $pa->StartAnalysis(); $arr=$pa->GetFinallyIndex(); return $arr; }
控制器中引用如下:
//1.引入PHPAnalysis分词引擎,提取分词数组 $Analysis = new WordAnalysis(); $result = $Analysis->getAllParticiple($word);
2.4.2案例二:使用Elasticsearch全文搜索检测敏感词
使用composer拉取Elasticsearch
{ "require": { "elasticsearch/elasticsearch" : "~5.0" }, "repositories": { "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com" } } }
暂时放放,功力不足,看的吃力,可参考如下文档:
https://www.cnblogs.com/mzhaox/p/11210025.html
https://www.cnblogs.com/subendong/p/7308647.html
2.5方案4:API
这里以阿里云提供的敏感词API为例(点击处理跳转到API)
/** * 阿里云api * @param $content * @return array|mixed|string */ private function aliyunApi($content) { $host = "http://monitoring.market.alicloudapi.com"; $path = "/neirongjiance"; $method = "POST"; $appcode = "150b1369d73e4aa3b3f2b22ed6cceeb4"; $headers = array(); array_push($headers, "Authorization:APPCODE " . $appcode); //根据API的要求,定义相对应的Content-Type array_push($headers, "Content-Type" . ":" . "application/x-www-form-urlencoded; charset=UTF-8"); $querys = ""; $bodys = "in={$content}"; $url = $host . $path; $curl = curl_init();//初始化curl curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($curl, CURLOPT_URL, $url);//抓取指定网页 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_FAILONERROR, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);//要求结果为字符串且输出到屏幕上 curl_setopt($curl, CURLOPT_HEADER, true); if (1 == strpos("$" . $host, "https://")) { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); } curl_setopt($curl, CURLOPT_POSTFIELDS, $bodys); $result = curl_exec($curl); $code = curl_getinfo($curl,CURLINFO_HTTP_CODE);//取得响应码 $data = ''; if($code == 200){ $data = strstr($result, '{'); $data = json_decode($data); } curl_close($curl); $data = ['code' => 1, 'msg' => '敏感词API测试', 'data' => $data]; return $data; } }
3.总结
通过开发敏感词校测功能,我明白不要小看每个功能,看似简单的东西,其实一点也不简单,每个小功能都能够延伸一定的高度,拿到需求先多想、多思考,把能够想到实现方案列出来,然后让拍板人去选择哪种方案,但是呢,空闲时间也要对其他不采用的方案进行了解和学习,要做到以点盖全,而不是点就是点,加油~