Python基于DFA算法實(shí)現(xiàn)內(nèi)容敏感詞過濾
DFA 算法是通過提前構(gòu)造出一個 樹狀查找結(jié)構(gòu),之后根據(jù)輸入在該樹狀結(jié)構(gòu)中就可以進(jìn)行非常高效的查找。
設(shè)我們有一個敏感詞庫,詞酷中的詞匯為:
- 我愛你
- 我愛他
- 我愛她
- 我愛你呀
- 我愛他呀
- 我愛她呀
- 我愛她啊
那么就可以構(gòu)造出這樣的樹狀結(jié)構(gòu):
設(shè)玩家輸入的字符串為:白菊我愛你呀哈哈哈
我們遍歷玩家輸入的字符串 str,并設(shè)指針 i 指向樹狀結(jié)構(gòu)的根節(jié)點(diǎn),即最左邊的空白節(jié)點(diǎn):
- str[0] = ‘白’ 時,此時 tree[i] 沒有指向值為 ‘白’ 的節(jié)點(diǎn),所以不滿足匹配條件,繼續(xù)往下遍歷
- str[1] = ‘菊’,同樣不滿足匹配條件,繼續(xù)遍歷
- str[2] = ‘我’,此時 tree[i] 有一條路徑連接著 ‘我’ 這個節(jié)點(diǎn),滿足匹配條件,i 指向 ‘我’ 這個節(jié)點(diǎn),然后繼續(xù)遍歷
- str[3] = ‘愛’,此時 tree[i] 有一條路徑連著 ‘愛’ 這個節(jié)點(diǎn),滿足匹配條件,i 指向 ‘愛’,繼續(xù)遍歷
- str[4] = ‘你’,同樣有路徑,i 指向 ‘你’,繼續(xù)遍歷
- str[5] = ‘呀’,同樣有路徑,i 指向 ‘呀’
此時,我們的指針 i 已經(jīng)指向了樹狀結(jié)構(gòu)的末尾,即此時已經(jīng)完成了一次敏感詞判斷。我們可以用變量來記錄下這次敏感詞匹配開始時玩家輸入字符串的下標(biāo),和匹配結(jié)束時的下標(biāo),然后再遍歷一次將字符替換為 * 即可。
結(jié)束一次匹配后,我們把指針 i 重新指向樹狀結(jié)構(gòu)的根節(jié)點(diǎn)處。
此時我們玩家輸入的字符串還沒有遍歷到頭,所以繼續(xù)遍歷:
str[6] = ‘哈’,不滿足匹配條件,繼續(xù)遍歷
str[7] = ‘哈’ …
str[8] = ‘哈’ …
可以看出我們遍歷了一次玩家輸入的字符串,就找到了其中的敏感詞匯。
DFA算法python實(shí)現(xiàn)
class DFA: """DFA 算法 敏感字中“*”代表任意一個字符 """ def __init__(self, sensitive_words: list, skip_words: list): # 對于敏感詞sensitive_words及無意義的詞skip_words可以通過數(shù)據(jù)庫、文件或者其他存儲介質(zhì)進(jìn)行保存 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方法生成敏感詞的樹狀結(jié)構(gòu),(以字典保存),對于上面的例子,生成的樹狀結(jié)構(gòu)保存如下:
if __name__ == '__main__': dfa = DFA(['我愛你', '我愛他', '我愛她', '我愛你呀', '我愛他呀', '我愛她呀', '我愛她啊'], skip_words=[]) # 暫時不配置skip_words print(dfa)
結(jié)果:
{'我': {'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}}}}}
然后調(diào)用match方法,輸入內(nèi)容進(jìn)行敏感詞匹配:
if __name__ == '__main__': dfa = DFA(['我愛你', '我愛他', '我愛她', '我愛你呀', '我愛他呀', '我愛她呀', '我愛她啊'], ['\n', '\r\n', '\r']) # print(dfa) print(dfa.match('白菊我愛你呀哈哈哈'))
結(jié)果:
[{'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
驗(yàn)證一下:
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'}]
上列中如果輸入的內(nèi)容中,“大傻X安樂飛大傻B”寫成“大%傻X安樂飛大&傻B”,看看是否能識別出敏感詞呢?識別不出了!
if __name__ == '__main__': dfa = DFA(['大傻*'], []) print(dfa) print(dfa.match('大%傻X安樂飛大&傻B'))
結(jié)果:
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[
諸如“,&,!,!,@,#,$,¥,*,^,%,?,?,<,>,《,》",這些特殊符號無實(shí)際意義,但是可以在敏感詞中間插入而破壞敏感詞的結(jié)構(gòu)規(guī)避敏感詞檢查
進(jìn)行無意義詞配置,再進(jìn)行敏感詞檢查,如下,可見對于被破壞的敏感詞也能識別
if __name__ == '__main__': dfa = DFA(['大傻*'], ['%', '&']) print(dfa) print(dfa.match('大%傻X安樂飛大&傻B'))
結(jié)果:
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[{'start': 0, 'match': '大%傻X'}, {'start': 7, 'match': '大&傻B'}]
以上就是Python基于DFA算法實(shí)現(xiàn)內(nèi)容敏感詞過濾的詳細(xì)內(nèi)容,更多關(guān)于Python敏感詞過濾的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python中的測試模塊unittest和doctest的使用教程
這篇文章主要介紹了Python中的測試模塊unittest和doctest的使用教程,本文來自于IBM官方網(wǎng)站技術(shù)文檔,需要的朋友可以參考下2015-04-04python 循環(huán)while和for in簡單實(shí)例
下面小編就為大家?guī)硪黄猵ython 循環(huán)while和for in簡單實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08python3 字符串知識點(diǎn)學(xué)習(xí)筆記
字符串是 Python 中最常用的數(shù)據(jù)類型。我們可以使用引號('或")來創(chuàng)建字符串2020-02-02python生成器/yield協(xié)程/gevent寫簡單的圖片下載器功能示例
這篇文章主要介紹了python生成器/yield協(xié)程/gevent寫簡單的圖片下載器功能,結(jié)合實(shí)例形式分析了python生成器、yield協(xié)程與gevent圖片下載器相關(guān)功能定義與使用技巧,需要的朋友可以參考下2019-10-10python中re.findall函數(shù)實(shí)例用法
在本篇文章里小編給大家整理了一篇關(guān)于python中re.findall函數(shù)實(shí)例用法相關(guān)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-09-09python帶參數(shù)打包exe及調(diào)用方式
今天小編就為大家分享一篇python帶參數(shù)打包exe及調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12