Python实现敏感词过滤

举报
Python爱好者 发表于 2020/12/28 23:26:01 2020/12/28
【摘要】 在我们生活中的一些场合经常会有一些不该出现的敏感词,我们通常会使用*去屏蔽它,例如:尼玛 -> **,一些骂人的敏感词和一些政治敏感词都不应该出现在一些公共场合中,这个时候我们就需要一定的手段去屏蔽这些敏感词。下面我来介绍一些简单版本的敏感词屏蔽的方法。 (我已经尽量把脏话做成图片的形式了,要不然文章发不出去) 方法一:replace过滤 replace就是最...

在我们生活中的一些场合经常会有一些不该出现的敏感词,我们通常会使用*去屏蔽它,例如:尼玛 -> **,一些骂人的敏感词和一些政治敏感词都不应该出现在一些公共场合中,这个时候我们就需要一定的手段去屏蔽这些敏感词。下面我来介绍一些简单版本的敏感词屏蔽的方法。

(我已经尽量把脏话做成图片的形式了,要不然文章发不出去)

方法一:replace过滤

replace就是最简单的字符串替换,当一串字符串中有可能会出现的敏感词时,我们直接使用相应的replace方法用*替换出敏感词即可。

缺点:

文本和敏感词少的时候还可以,多的时候效率就比较差了


   
  1. import datetime
  2. now = datetime.datetime.now()
  3. print(filter_sentence, " | ", now)

如果是多个敏感词可以用列表进行逐一替换


   
  1. for i in dirty:
  2.     speak = speak.replace(i, '*')
  3. print(speak, " | ", now)

方法二:正则表达式过滤

正则表达式算是一个不错的匹配方法了,日常的查询中,机会都会用到正则表达式,包括我们的爬虫,也都是经常会使用到正则表达式的,在这里我们主要是使用“|”来进行匹配,“|”的意思是从多个目标字符串中选择一个进行匹配。写个简单的例子:


   
  1. import re
  2. def sentence_filter(keywords, text):
  3.     return re.sub("|".join(keywords), "***", text)
  4. print(sentence_filter(dirty, speak))

方法三:DFA过滤算法

DFA的算法,即Deterministic Finite Automaton算法,翻译成中文就是确定有穷自动机算法。它的基本思想是基于状态转移来检索敏感词,只需要扫描一次待检测文本,就能对所有敏感词进行检测。(实现见代码注释)


   
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. # @Time:2020/4/15 11:40
  4. # @Software:PyCharm
  5. # article_add: https://www.cnblogs.com/JentZhang/p/12718092.html
  6. __author__ = "JentZhang"
  7. import json
  8. MinMatchType = 1  # 最小匹配规则
  9. MaxMatchType = 2  # 最大匹配规则
  10. class DFAUtils(object):
  11.     """
  12.     DFA算法
  13.     """
  14.     def __init__(self, word_warehouse):
  15.         """
  16.         算法初始化
  17.         :param word_warehouse:词库
  18.         """
  19.         # 词库
  20.         self.root = dict()
  21.         # 无意义词库,在检测中需要跳过的(这种无意义的词最后有个专门的地方维护,保存到数据库或者其他存储介质中)
  22.         self.skip_root = [' ''&''!''!''@''#''$''¥''*''^''%''?''?''<''>'"《"'》']
  23.         # 初始化词库
  24.         for word in word_warehouse:
  25.             self.add_word(word)
  26.     def add_word(self, word):
  27.         """
  28.         添加词库
  29.         :param word:
  30.         :return:
  31.         """
  32.         now_node = self.root
  33.         word_count = len(word)
  34.         for i in range(word_count):
  35.             char_str = word[i]
  36.             if char_str in now_node.keys():
  37.                 # 如果存在该key,直接赋值,用于下一个循环获取
  38.                 now_node = now_node.get(word[i])
  39.                 now_node['is_end'] = False
  40.             else:
  41.                 # 不存在则构建一个dict
  42.                 new_node = dict()
  43.                 if i == word_count - 1:  # 最后一个
  44.                     new_node['is_end'] = True
  45.                 else:  # 不是最后一个
  46.                     new_node['is_end'] = False
  47.                 now_node[char_str] = new_node
  48.                 now_node = new_node
  49.     def check_match_word(self, txt, begin_index, match_type=MinMatchType):
  50.         """
  51.         检查文字中是否包含匹配的字符
  52.         :param txt:待检测的文本
  53.         :param begin_index: 调用getSensitiveWord时输入的参数,获取词语的上边界index
  54.         :param match_type:匹配规则 1:最小匹配规则,2:最大匹配规则
  55.         :return:如果存在,则返回匹配字符的长度,不存在返回0
  56.         """
  57.         flag = False
  58.         match_flag_length = 0  # 匹配字符的长度
  59.         now_map = self.root
  60.         tmp_flag = 0  # 包括特殊字符的敏感词的长度
  61.         for i in range(begin_index, len(txt)):
  62.             word = txt[i]
  63.             # 检测是否是特殊字符"
  64.             if word in self.skip_root and len(now_map) < 100:
  65.                 # len(nowMap)<100 保证已经找到这个词的开头之后出现的特殊字符
  66.                 tmp_flag += 1
  67.                 continue
  68.             # 获取指定key
  69.             now_map = now_map.get(word)
  70.             if now_map:  # 存在,则判断是否为最后一个
  71.                 # 找到相应key,匹配标识+1
  72.                 match_flag_length += 1
  73.                 tmp_flag += 1
  74.                 # 如果为最后一个匹配规则,结束循环,返回匹配标识数
  75.                 if now_map.get("is_end"):
  76.                     # 结束标志位为true
  77.                     flag = True
  78.                     # 最小规则,直接返回,最大规则还需继续查找
  79.                     if match_type == MinMatchType:
  80.                         break
  81.             else:  # 不存在,直接返回
  82.                 break
  83.         if tmp_flag < 2 or not flag:  # 长度必须大于等于1,为词
  84.             tmp_flag = 0
  85.         return tmp_flag
  86.     def get_match_word(self, txt, match_type=MinMatchType):
  87.         """
  88.         获取匹配到的词语
  89.         :param txt:待检测的文本
  90.         :param match_type:匹配规则 1:最小匹配规则,2:最大匹配规则
  91.         :return:文字中的相匹配词
  92.         """
  93.         matched_word_list = list()
  94.         for i in range(len(txt)):  # 0---11
  95.             length = self.check_match_word(txt, i, match_type)
  96.             if length > 0:
  97.                 word = txt[i:i + length]
  98.                 matched_word_list.append(word)
  99.                 # i = i + length - 1
  100.         return matched_word_list
  101.     def is_contain(self, txt, match_type=MinMatchType):
  102.         """
  103.         判断文字是否包含敏感字符
  104.         :param txt:待检测的文本
  105.         :param match_type:匹配规则 1:最小匹配规则,2:最大匹配规则
  106.         :return:若包含返回true,否则返回false
  107.         """
  108.         flag = False
  109.         for i in range(len(txt)):
  110.             match_flag = self.check_match_word(txt, i, match_type)
  111.             if match_flag > 0:
  112.                 flag = True
  113.         return flag
  114.     def replace_match_word(self, txt, replace_char='*', match_type=MinMatchType):
  115.         """
  116.         替换匹配字符
  117.         :param txt:待检测的文本
  118.         :param replace_char:用于替换的字符,匹配的敏感词以字符逐个替换,如"你是大王八",敏感词"王八",替换字符*,替换结果"你是大**"
  119.         :param match_type:匹配规则 1:最小匹配规则,2:最大匹配规则
  120.         :return:替换敏感字字符后的文本
  121.         """
  122.         tuple_set = self.get_match_word(txt, match_type)
  123.         word_set = [i for i in tuple_set]
  124.         result_txt = ""
  125.         if len(word_set) > 0:  # 如果检测出了敏感词,则返回替换后的文本
  126.             for word in word_set:
  127.                 replace_string = len(word) * replace_char
  128.                 txt = txt.replace(word, replace_string)
  129.                 result_txt = txt
  130.         else:  # 没有检测出敏感词,则返回原文本
  131.             result_txt = txt
  132.         return result_txt
  133. if __name__ == '__main__':
  134.     dfa = DFAUtils(word_warehouse=word_warehouse)
  135.     print('词库结构:', json.dumps(dfa.root, ensure_ascii=False))
  136.     # 待检测的文本
  137.     msg = msg
  138.     print('是否包含:', dfa.is_contain(msg))
  139.     print('相匹配的词:', dfa.get_match_word(msg))
  140.     print('替换包含的词:', dfa.replace_match_word(msg))

方法四:AC自动机

AC自动机需要有前置知识:Trie树(简单介绍:又称前缀树,字典树,是用于快速处理字符串的问题,能做到快速查找到一些字符串上的信息。)

详细参考:

https://www.luogu.com.cn/blog/juruohyfhaha/trie-xue-xi-zong-jie

ac自动机,就是在tire树的基础上,增加一个fail指针,如果当前点匹配失败,则将指针转移到fail指针指向的地方,这样就不用回溯,而可以路匹配下去了。

详细匹配机制我在这里不过多赘述,关于AC自动机可以参考一下这篇文章:

https://blog.csdn.net/bestsort/article/details/82947639

python可以利用ahocorasick模块快速实现:


   
  1. # python3 -m pip install pyahocorasick
  2. import ahocorasick
  3. def build_actree(wordlist):
  4.     actree = ahocorasick.Automaton()
  5.     for index, word in enumerate(wordlist):
  6.         actree.add_word(word, (index, word))
  7.     actree.make_automaton()
  8.     return actree
  9. if __name__ == '__main__':
  10.     actree = build_actree(wordlist=wordlist)
  11.     sent_cp = sent
  12.     for i in actree.iter(sent):
  13.         sent_cp = sent_cp.replace(i[1][1], "**")
  14.         print("屏蔽词:",i[1][1])
  15.     print("屏蔽结果:",sent_cp)

当然,我们也可以手写一份AC自动机,具体参考:


   
  1. class TrieNode(object):
  2.     __slots__ = ['value''next''fail''emit']
  3.     def __init__(self, value):
  4.         self.value = value
  5.         self.next = dict()
  6.         self.fail = None
  7.         self.emit = None
  8. class AhoCorasic(object):
  9.     __slots__ = ['_root']
  10.     def __init__(self, words):
  11.         self._root = AhoCorasic._build_trie(words)
  12.     @staticmethod
  13.     def _build_trie(words):
  14.         assert isinstance(words, list) and words
  15.         root = TrieNode('root')
  16.         for word in words:
  17.             node = root
  18.             for c in word:
  19.                 if c not in node.next:
  20.                     node.next[c] = TrieNode(c)
  21.                 node = node.next[c]
  22.             if not node.emit:
  23.                 node.emit = {word}
  24.             else:
  25.                 node.emit.add(word)
  26.         queue = []
  27.         queue.insert(0, (root, None))
  28.         while len(queue) > 0:
  29.             node_parent = queue.pop()
  30.             curr, parent = node_parent[0], node_parent[1]
  31.             for sub in curr.next.itervalues():
  32.                 queue.insert(0, (sub, curr))
  33.             if parent is None:
  34.                 continue
  35.             elif parent is root:
  36.                 curr.fail = root
  37.             else:
  38.                 fail = parent.fail
  39.                 while fail and curr.value not in fail.next:
  40.                     fail = fail.fail
  41.                 if fail:
  42.                     curr.fail = fail.next[curr.value]
  43.                 else:
  44.                     curr.fail = root
  45.         return root
  46.     def search(self, s):
  47.         seq_list = []
  48.         node = self._root
  49.         for i, c in enumerate(s):
  50.             matched = True
  51.             while c not in node.next:
  52.                 if not node.fail:
  53.                     matched = False
  54.                     node = self._root
  55.                     break
  56.                 node = node.fail
  57.             if not matched:
  58.                 continue
  59.             node = node.next[c]
  60.             if node.emit:
  61.                 for _ in node.emit:
  62.                     from_index = i + 1 - len(_)
  63.                     match_info = (from_index, _)
  64.                     seq_list.append(match_info)
  65.                 node = self._root
  66.         return seq_list
  67. if __name__ == '__main__':
  68.     aho = AhoCorasic(['foo''bar'])
  69.     print aho.search('barfoothefoobarman')

以上便是使用Python实现敏感词过滤的四种方法,前面两种方法比较简单,后面两种偏向算法,需要先了解算法具体实现的原理,之后代码就好懂了。(DFA作为比较常用的过滤手段,建议大家掌握一下~)

最后附上敏感词词库:

https://github.com/qloog/sensitive_words


   
  1. 以上,便是今天的内容,希望大家喜欢,欢迎「转发」或者点击「在看」支持,谢谢各位。
  2. “扫一扫,关注我吧”

文章来源: blog.csdn.net,作者:敲代码的灰太狼,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/tongtongjing1765/article/details/105963611

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。