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解析树及树的遍历
Feb 03 Python
Python打包可执行文件的方法详解
Sep 19 Python
python 读取txt中每行数据,并且保存到excel中的实例
Apr 29 Python
python执行精确的小数计算方法
Jan 21 Python
详解Python3注释知识点
Feb 19 Python
python 自动批量打开网页的示例
Feb 21 Python
详解Django 时间与时区设置问题
Jul 23 Python
利用setuptools打包python程序的方法步骤
Jan 18 Python
在Python中使用K-Means聚类和PCA主成分分析进行图像压缩
Apr 10 Python
详解tensorflow2.x版本无法调用gpu的一种解决方法
May 25 Python
Python numpy大矩阵运算内存不足如何解决
Nov 19 Python
利用Python实时获取steam特惠游戏数据
Jun 25 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
牡丹941资料
2021/03/01 无线电
十天学会php之第二天
2006/10/09 PHP
用PHP读取flv文件的播放时间长度
2009/09/03 PHP
怎么在Windows系统中搭建php环境
2013/08/31 PHP
ThinkPHP查询中的魔术方法简述
2014/06/25 PHP
浅谈PHP接收POST数据方式
2015/06/05 PHP
WordPress中访客登陆实现邮件提醒的PHP脚本实例分享
2015/12/14 PHP
Yii框架弹出窗口组件CJuiDialog用法分析
2017/01/07 PHP
php批量删除操作代码分享
2017/02/26 PHP
laravel框架模型中非静态方法也能静态调用的原理分析
2019/11/23 PHP
JavaScript中对DOM节点的访问、创建、修改、删除
2015/11/16 Javascript
JavaScript解八皇后问题的方法总结
2016/06/12 Javascript
JS实现仿百度文库评分功能
2017/01/12 Javascript
vue-cli+webpack记事本项目创建
2017/04/01 Javascript
Vue.js@2.6.10更新内置错误处机制Fundebug同步支持相应错误监控
2019/05/13 Javascript
vue 使用v-for进行循环的实例代码详解
2020/02/19 Javascript
vue.js中使用微信扫一扫解决invalid signature问题(完美解决)
2020/04/11 Javascript
简单讲解Python中的数字类型及基本的数学计算
2016/03/11 Python
Python环境下搭建属于自己的pip源的教程
2016/05/05 Python
Python中使用Counter进行字典创建以及key数量统计的方法
2018/07/06 Python
Python3 读、写Excel文件的操作方法
2018/10/20 Python
对Python3 pyc 文件的使用详解
2019/02/16 Python
flask框架路由常用定义方式总结
2019/07/23 Python
Python 二叉树的层序建立与三种遍历实现详解
2019/07/29 Python
Python实现隐马尔可夫模型的前向后向算法的示例代码
2019/12/31 Python
基于Python的身份证验证识别和数据处理详解
2020/11/14 Python
利用python爬取有道词典的方法
2020/12/08 Python
详解python 条件语句和while循环的实例代码
2020/12/28 Python
NFL墨西哥官方商店:Tienda NFL
2017/11/28 全球购物
毕业生文员求职信
2013/11/03 职场文书
电厂职工自我鉴定
2014/02/20 职场文书
自我推荐信范文
2014/05/09 职场文书
小学生竞选班干部演讲稿(5篇)
2014/09/12 职场文书
总结一下关于在Java8中使用stream流踩过的一些坑
2021/06/24 Java/Android
SpringBoot 整合mongoDB并自定义连接池的示例代码
2022/02/28 MongoDB
Java 超详细讲解hashCode方法
2022/04/07 Java/Android