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性能优化技巧
Mar 09 Python
PyQt5 pyqt多线程操作入门
May 05 Python
python利用smtplib实现QQ邮箱发送邮件
May 20 Python
使用Anaconda3建立虚拟独立的python2.7环境方法
Jun 11 Python
浅谈dataframe中更改列属性的方法
Jul 10 Python
CentOS 7下安装Python3.6 及遇到的问题小结
Nov 08 Python
对python csv模块配置分隔符和引用符详解
Dec 12 Python
python使用selenium实现批量文件下载
Mar 11 Python
Python-copy()与deepcopy()区别详解
Jul 12 Python
Python实现Singleton模式的方式详解
Aug 08 Python
python 爬取疫情数据的源码
Feb 09 Python
详解django使用include无法跳转的解决方法
Mar 19 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扩展函数
2006/10/09 PHP
聊天室php&amp;mysql(三)
2006/10/09 PHP
destoon实现调用热门关键字的方法
2014/07/15 PHP
从ThinkPHP3.2.3过渡到ThinkPHP5.0学习笔记图文详解
2019/04/03 PHP
JQuery 返回布尔值Is()条件判断方法代码
2012/05/14 Javascript
两种常用的javascript数组去重方法思路及代码
2013/03/26 Javascript
javascript中常用编程知识
2013/04/08 Javascript
js实现仿网易点击弹出提示同时背景变暗效果
2015/08/13 Javascript
JS实现的页面自定义滚动条效果
2015/10/26 Javascript
浏览器检测JS代码(兼容目前各大主流浏览器)
2016/02/21 Javascript
JavaScript通过使用onerror设置默认图像显示代替alt
2016/03/01 Javascript
jQuery查找节点并获取节点属性的方法
2016/09/09 Javascript
js多个物体运动功能实例分析
2016/12/20 Javascript
Vue.js实战之Vuex的入门教程
2017/04/01 Javascript
Angular.js中数组操作的方法教程
2017/07/31 Javascript
jQuery实现菜单栏导航效果
2017/08/15 jQuery
JS实现的缓冲运动效果示例
2018/04/30 Javascript
Vue实现table上下移动功能示例
2019/02/21 Javascript
CKEditor扩展插件:自动排版功能autoformat插件实现方法详解
2020/02/06 Javascript
Vue如何基于es6导入外部js文件
2020/05/15 Javascript
用Python解析XML的几种常见方法的介绍
2015/04/09 Python
python 字典修改键(key)的几种方法
2018/08/10 Python
python使用Matplotlib画条形图
2020/03/25 Python
在django中,关于session的通用设置方法
2019/08/06 Python
如何在Django配置文件里配置session链接
2019/08/06 Python
Python 如何在字符串中插入变量
2020/08/01 Python
瑞典的玛丽小姐:Miss Mary of Sweden
2019/02/13 全球购物
美国最大婚纱连锁店运营商:David’s Bridal
2019/03/12 全球购物
幼儿园教学随笔感言
2014/02/23 职场文书
五四青年节优秀演讲稿范文
2014/05/28 职场文书
开学典礼策划方案
2014/05/28 职场文书
有关九一八事变的演讲稿
2014/09/14 职场文书
2015年小学总务工作总结
2015/07/21 职场文书
2016年秋季趣味运动会开幕词
2016/03/04 职场文书
Java中CyclicBarrier和CountDownLatch的用法与区别
2021/08/23 Java/Android
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
2022/05/04 Python