Python使用DFA算法过滤内容敏感词


Posted in Python onApril 22, 2022

DFA 算法是通过提前构造出一个 树状查找结构,之后根据输入在该树状结构中就可以进行非常高效的查找。

设我们有一个敏感词库,词酷中的词汇为:

  • 我爱你
  • 我爱他
  • 我爱她
  • 我爱你呀
  • 我爱他呀
  • 我爱她呀
  • 我爱她啊

那么就可以构造出这样的树状结构:

设玩家输入的字符串为:白菊我爱你呀哈哈哈

我们遍历玩家输入的字符串 str,并设指针 i 指向树状结构的根节点,即最左边的空白节点:

  • str[0] = ‘白’ 时,此时 tree[i] 没有指向值为 ‘白’ 的节点,所以不满足匹配条件,继续往下遍历
  • str[1] = ‘菊’,同样不满足匹配条件,继续遍历
  • str[2] = ‘我’,此时 tree[i] 有一条路径连接着 ‘我’ 这个节点,满足匹配条件,i 指向 ‘我’ 这个节点,然后继续遍历
  • str[3] = ‘爱’,此时 tree[i] 有一条路径连着 ‘爱’ 这个节点,满足匹配条件,i 指向 ‘爱’,继续遍历
  • str[4] = ‘你’,同样有路径,i 指向 ‘你’,继续遍历
  • str[5] = ‘呀’,同样有路径,i 指向 ‘呀’

此时,我们的指针 i 已经指向了树状结构的末尾,即此时已经完成了一次敏感词判断。我们可以用变量来记录下这次敏感词匹配开始时玩家输入字符串的下标,和匹配结束时的下标,然后再遍历一次将字符替换为 * 即可。

结束一次匹配后,我们把指针 i 重新指向树状结构的根节点处。

此时我们玩家输入的字符串还没有遍历到头,所以继续遍历:

str[6] = ‘哈’,不满足匹配条件,继续遍历

str[7] = ‘哈’ …

str[8] = ‘哈’ …

可以看出我们遍历了一次玩家输入的字符串,就找到了其中的敏感词汇。

Python使用DFA算法过滤内容敏感词

DFA算法python实现

class DFA:
    """DFA 算法
       敏感字中“*”代表任意一个字符
    """

    def __init__(self, sensitive_words: list, skip_words: list):  # 对于敏感词sensitive_words及无意义的词skip_words可以通过数据库、文件或者其他存储介质进行保存
        self.state_event_dict = self._generate_state_event(sensitive_words)
        self.skip_words = skip_words

    def __repr__(self):
        return '{}'.format(self.state_event_dict)

    @staticmethod
    def _generate_state_event(sensitive_words) -> dict:
        state_event_dict = {}
        for word in sensitive_words:
            tmp_dict = state_event_dict
            length = len(word)
            for index, char in enumerate(word):
                if char not in tmp_dict:
                    next_dict = {'is_end': False}
                    tmp_dict[char] = next_dict
                    tmp_dict = next_dict
                else:
                    next_dict = tmp_dict[char]
                    tmp_dict = next_dict
                if index == length - 1:
                    tmp_dict['is_end'] = True
        return state_event_dict

    def match(self, content: str):
        match_list = []
        state_list = []
        temp_match_list = []

        for char_pos, char in enumerate(content):
            if char in self.skip_words:
                continue
            if char in self.state_event_dict:
                state_list.append(self.state_event_dict)
                temp_match_list.append({
                    "start": char_pos,
                    "match": ""
                })
            for index, state in enumerate(state_list):
                is_match = False
                state_char = None
                if '*' in state: # 对于一些敏感词,比如大傻X,可能是大傻B,大傻×,大傻...,采用通配符*,一个*代表一个字符
                    state_list[index] = state['*']
                    state_char = state['*']
                    is_match = True
                if char in state:
                    state_list[index] = state[char]
                    state_char = state[char]
                    is_match = True
                if is_match:
                    if state_char["is_end"]:
                        stop = char_pos + 1
                        temp_match_list[index]['match'] = content[
                                                          temp_match_list[index]['start']:stop]
                        match_list.append(copy.deepcopy(temp_match_list[index]))
                        if len(state_char.keys()) == 1:
                            state_list.pop(index)
                            temp_match_list.pop(index)
                else:
                    state_list.pop(index)
                    temp_match_list.pop(index)
        for index, match_words in enumerate(match_list):
            print(match_words['start'])
        return match_list

_generate_state_event方法生成敏感词的树状结构,(以字典保存),对于上面的例子,生成的树状结构保存如下:

if __name__ == '__main__':
    dfa = DFA(['我爱你', '我爱他', '我爱她', '我爱你呀', '我爱他呀', '我爱她呀', '我爱她啊'], skip_words=[])  # 暂时不配置skip_words
    print(dfa)

结果:

{'我': {'is_end': False, '爱': {'is_end': False, '你': {'is_end': True, '呀': {'is_end': True}}, '他': {'is_end': True, '呀': {'is_end': True}}, '她': {'is_end': True, '呀': {'is_end': True}, '啊': {'is_end': True}}}}}

然后调用match方法,输入内容进行敏感词匹配:

if __name__ == '__main__':
    dfa = DFA(['我爱你', '我爱他', '我爱她', '我爱你呀', '我爱他呀', '我爱她呀', '我爱她啊'], ['\n', '\r\n', '\r'])
    # print(dfa)
    print(dfa.match('白菊我爱你呀哈哈哈'))

结果:

[{'start': 2, 'match': '我爱你'}, {'start': 2, 'match': '我爱你呀'}]

而对于一些敏感词,比如大傻X,可能是大傻B,大傻×,大傻...,那是不是可以通过一个通配符*来解决?

见代码:48 ~51行

if '*' in state: # 对于一些敏感词,比如大傻X,可能是大傻B,大傻×,大傻...,采用通配符*,一个*代表一个字符
 state_list[index] = state['*']
 state_char = state['*']
 is_match = True

验证一下:

if __name__ == '__main__':
    dfa = DFA(['大傻*'], [])
    print(dfa)
    print(dfa.match('大傻X安乐飞大傻B'))

{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[{'start': 0, 'match': '大傻X'}, {'start': 6, 'match': '大傻B'}]

上列中如果输入的内容中,“大傻X安乐飞大傻B”写成“大%傻X安乐飞大&傻B”,看看是否能识别出敏感词呢?识别不出了!

if __name__ == '__main__':
    dfa = DFA(['大傻*'], [])
    print(dfa)
    print(dfa.match('大%傻X安乐飞大&傻B'))

结果:

{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[

诸如“,&,!,!,@,#,$,¥,*,^,%,?,?,<,>,《,》",这些特殊符号无实际意义,但是可以在敏感词中间插入而破坏敏感词的结构规避敏感词检查

进行无意义词配置,再进行敏感词检查,如下,可见对于被破坏的敏感词也能识别

if __name__ == '__main__':
    dfa = DFA(['大傻*'], ['%', '&'])
    print(dfa)
    print(dfa.match('大%傻X安乐飞大&傻B'))

结果: 

{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[{'start': 0, 'match': '大%傻X'}, {'start': 7, 'match': '大&傻B'}]

以上就是Python基于DFA算法实现内容敏感词过滤的详细内容!


Tags in this post...

Python 相关文章推荐
python删除文件示例分享
Jan 28 Python
Python设置默认编码为utf8的方法
Jul 01 Python
django admin 后台实现三级联动的示例代码
Jun 22 Python
Sanic框架流式传输操作示例
Jul 18 Python
详解将Pandas中的DataFrame类型转换成Numpy中array类型的三种方法
Jul 06 Python
OpenCV 边缘检测
Jul 10 Python
python requests库爬取豆瓣电视剧数据并保存到本地详解
Aug 10 Python
python读取word 中指定位置的表格及表格数据
Oct 23 Python
Django后端发送小程序微信模板消息示例(服务通知)
Dec 17 Python
详解用python -m http.server搭一个简易的本地局域网
Sep 24 Python
如何在windows下安装配置python工具Ulipad
Oct 27 Python
Django REST Framework 分页(Pagination)详解
Nov 30 Python
python游戏开发之pygame实现接球小游戏
Apr 22 #Python
python游戏开发Pygame框架
Apr 22 #Python
python中的random模块和相关函数详解
Apr 22 #Python
Python写情书? 10行代码展示如何把情书写在她的照片里
Apr 21 #Python
微信小程序调用python模型
Apr 21 #Python
使用python绘制分组对比柱状图
使用python将HTML转换为PDF pdfkit包(wkhtmltopdf) 的使用方法
Apr 21 #Python
You might like
PHP的宝库目录--PEAR
2006/10/09 PHP
PHP字符串 ==比较运算符的副作用
2009/10/21 PHP
PHP无法访问远程mysql的问题分析及解决
2013/05/16 PHP
php判断GIF图片是否为动画的方法
2020/09/04 PHP
thinkphp3.x中变量的获取和过滤方法详解
2016/05/20 PHP
PHP设计模式之适配器模式定义与用法详解
2018/04/03 PHP
PHP检查URL包含特定字符串实例方法
2019/02/11 PHP
getElementById在任意一款浏览器中都可以用吗的疑问回复
2007/05/13 Javascript
Javascript的构造函数和constructor属性
2010/01/09 Javascript
Dom 学习总结以及实例的使用介绍
2013/04/24 Javascript
JavaScript中“过于”犀利地for/in循环使用示例
2013/10/22 Javascript
javascript 数字格式化输出的实现代码
2013/12/10 Javascript
javascript中expression的用法整理
2014/05/13 Javascript
Javascript基础教程之switch语句
2015/01/18 Javascript
jQuery选择器querySelector的使用指南
2015/01/23 Javascript
JavaScript使用Prototype实现面向对象的方法
2015/04/14 Javascript
实例讲解jQuery EasyUI tree中state属性慎用
2016/04/01 Javascript
@ResponseBody 和 @RequestBody 注解的区别
2017/03/08 Javascript
JavaScript 保护变量不被随意修改的实现代码
2017/09/27 Javascript
详解微信小程序scroll-view横向滚动的实践踩坑及隐藏其滚动条的实现
2019/03/14 Javascript
微信小程序前端自定义分享的实现方法
2019/06/13 Javascript
vue-列表下详情的展开与折叠案例
2020/07/28 Javascript
Python批量修改文件后缀的方法
2014/01/26 Python
pygame播放音乐的方法
2015/05/19 Python
Django uwsgi Nginx 的生产环境部署详解
2019/02/02 Python
总结Pyinstaller的坑及终极解决方法(小结)
2020/09/21 Python
2021年值得向Python开发者推荐的VS Code扩展插件
2021/01/25 Python
前端H5 Video常见使用场景简介
2020/08/21 HTML / CSS
世嘉游戏英国官方商店:SEGA Shop UK
2019/09/20 全球购物
党课学习思想汇报
2014/01/02 职场文书
《最大的麦穗》教学反思
2014/04/17 职场文书
防沙治沙典型材料
2014/05/07 职场文书
学校总务处领导干部个人对照检查材料思想汇报
2014/10/06 职场文书
学生犯错保证书
2015/05/09 职场文书
客户答谢会致辞
2015/07/30 职场文书
 python中的元类metaclass详情
2022/05/30 Python