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] = ‘哈’ …

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

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: # 对于一些敏感词,比如大 * ,可能是大 * ,大傻×,大傻...,采用通配符*,一个*代表一个字符
                   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}}}}
[

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

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

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

标签:Python,DFA,敏感词,过滤
0
投稿

猜你喜欢

  • python得到qq句柄,并显示在前台的方法

    2021-10-08 12:44:30
  • PyTorch 普通卷积和空洞卷积实例

    2021-01-06 16:42:57
  • Oracle数据库及应用程序优化开发者网络Oracle

    2010-07-18 13:02:00
  • Mysql的慢SQL优化思路和规范详解

    2024-01-22 22:01:15
  • 解决Pytorch中的神坑:关于model.eval的问题

    2021-09-17 17:16:55
  • mysql5.7版本root密码登录问题的解决方法

    2024-01-21 00:47:43
  • python utc datetime转换为时间戳的方法

    2021-11-18 07:11:44
  • Python动态配置管理Dynaconf的实现示例详解

    2021-01-24 19:35:06
  • Python 用__new__方法实现单例的操作

    2023-05-22 08:22:41
  • javascript 通用滑动门tab类

    2023-08-05 09:42:25
  • pandas实现数据读取&清洗&分析的项目实践

    2022-01-31 09:55:05
  • 一款Python工具制作的动态条形图(强烈推荐!)

    2021-07-21 17:38:18
  • MySQL 密码设置

    2024-01-28 11:53:59
  • python 实现Flask中返回图片流给前端展示

    2023-12-01 06:58:23
  • 详解numpy.ndarray.reshape()函数的参数问题

    2022-02-06 20:22:57
  • js取得html iframe中的元素和变量值

    2024-06-07 15:26:17
  • GO语言不固定参数函数与匿名函数的使用

    2024-02-17 14:42:17
  • python excel转换csv代码实例

    2023-10-30 15:19:53
  • mysql 8.0.22 安装配置方法图文教程

    2024-01-24 20:30:05
  • vue 监听键盘回车事件详解 @keyup.enter || @keyup.enter.native

    2023-07-02 17:01:35
  • asp之家 网络编程 m.aspxhome.com