Python基于DFA算法实现内容敏感词过滤
作者:fdzwdt 发布时间:2023-07-25 20:14:11
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] = ‘哈’ …
可以看出我们遍历了一次玩家输入的字符串,就找到了其中的敏感词汇。
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: # 对于一些敏感词,比如大 * ,可能是大 * ,大傻×,大傻...,采用通配符*,一个*代表一个字符
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': '我爱你呀'}]
而对于一些敏感词,比如大 * ,可能是大 * ,大傻×,大傻...,那是不是可以通过一个通配符*来解决?
见代码:48 ~51行
if '*' in state: # 对于一些敏感词,比如大 * ,可能是大 * ,大傻×,大傻...,采用通配符*,一个*代表一个字符
state_list[index] = state['*']
state_char = state['*']
is_match = True
验证一下:
if __name__ == '__main__':
dfa = DFA(['大傻*'], [])
print(dfa)
print(dfa.match('大 * 安乐飞大 * '))
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[{'start': 0, 'match': '大 * '}, {'start': 6, 'match': '大 * '}]
上列中如果输入的内容中,“大 * 安乐飞大 * ”写成“大% * 安乐飞大& * ”,看看是否能识别出敏感词呢?识别不出了!
if __name__ == '__main__':
dfa = DFA(['大傻*'], [])
print(dfa)
print(dfa.match('大% * 安乐飞大& * '))
结果:
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[
诸如“,&,!,!,@,#,$,¥,*,^,%,?,?,<,>,《,》",这些特殊符号无实际意义,但是可以在敏感词中间插入而破坏敏感词的结构规避敏感词检查
进行无意义词配置,再进行敏感词检查,如下,可见对于被破坏的敏感词也能识别
if __name__ == '__main__':
dfa = DFA(['大傻*'], ['%', '&'])
print(dfa)
print(dfa.match('大% * 安乐飞大& * '))
结果:
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[{'start': 0, 'match': '大% * '}, {'start': 7, 'match': '大& * '}]
来源:https://www.cnblogs.com/fdzwdt/p/16174752.html


猜你喜欢
- 本篇文章主要涉及mysql5.7.14用以往的安装方法安装存在的密码登录不上,密码失效等问题的解决办法,希望可以帮到有同样困扰的朋友。具体过
- golang常用库之-pkg/errors包背景golang自带了错误信息包error只提供了简单的用法, 如errors.New(),和e
- 这次让我们一个用 Python 做一个小工具:将动态 GIF 图片倒序播放!GIF(Graphics Interchange Format)
- 字典的添加与修改# coding:utf-8if __name__ == '__main__':
- 1. 引言在Python相关代码中,我们经常会遇到如下代码段:# stuffif __name__ == "__main__&qu
- 本文将讲述vue-cli+vux-scroller实现移动端的上拉加载功能:纠错声明:网上查阅资料看到很多人都将vux和vuex弄混,在这里
- 如下所示:a = [1,1,1,2,3,45,1,2,1]a.remove(1) result: [1,1,2,3,45,1,2,1]whi
- 前言提示:这里可以添加本文要记录的大概内容:将一个EXCEL等份拆成多个EXCEL将多个小EXCEL合并成一个大EXCEL并标记来源提示:以
- 欢迎来到 Python Httpx 教程。在本教程中,我们将深入探讨 Httpx 库,并学习如何使用它来构建高性能的异步网络应用程序。什么是
- 1.字符串大小写转换string.title() #将字符串中所有单词的首字母以大写形式显示string.upper() #将字符串中所有字
- 业务需求:需要测试手机滑动解锁失败时事件的次数及等待的时间,本来想利用Python+Appium实现,但是Appium运行时自动给我解锁了.
- pynput这个库让你可以控制和监控输入设备。对于每一种输入设备,它包含一个子包来控制和监控该种输入设备:pynput.mouse:包含控制
- Lightbox里面的一个函数,能把页面实际的高宽与浏览器可视面积的高宽保存在一个数组中..非常好用.什么是Lightbox?下载light
- 如果要在应用程序中周期性地进行某项操作,比如周期性地检测主机的CPU值,则需要用到QTimer定时器,QTimer类提供了重复的和单次的定时
- 前言摘要之前写了一篇 grpool goroutine池详解 | 协程管理 收到了大家积极的反馈,今天这篇来做一下grpool的性能测试分析
- 前言一个Excel电子表格文档称为一个工作簿一个工作簿保存在一个扩展名为.xlsx的文件中一个工作簿可以包含多个表用户当前查看的
- 假设三节点MGR某个节点异常,需要重新把这个节点加入到MGR集群中,具体操作过程如下:贡献者端执行(192.168.1.11)DROP US
- 在SQL中系统已为我们提供了很非常丰富的函数:例:聚会函数avg, sum,count,max,min 日期函数:Day,Mon
- 在html中引入外部js文件,并调用js文件中的带参函数1 项目结构2 编写a.js、test.html//a.jsfunction abc
- 一个几百行代码做出http/https代理服务器的脚本,启动即可做http https透明代理使用python proxy.py 8992使