Python基于DFA算法實(shí)現(xiàn)內(nèi)容敏感詞過(guò)濾
DFA 算法是通過(guò)提前構(gòu)造出一個(gè) 樹(shù)狀查找結(jié)構(gòu),之后根據(jù)輸入在該樹(shù)狀結(jié)構(gòu)中就可以進(jìn)行非常高效的查找。
設(shè)我們有一個(gè)敏感詞庫(kù),詞酷中的詞匯為:
- 我愛(ài)你
- 我愛(ài)他
- 我愛(ài)她
- 我愛(ài)你呀
- 我愛(ài)他呀
- 我愛(ài)她呀
- 我愛(ài)她啊
那么就可以構(gòu)造出這樣的樹(shù)狀結(jié)構(gòu):
設(shè)玩家輸入的字符串為:白菊我愛(ài)你呀哈哈哈
我們遍歷玩家輸入的字符串 str,并設(shè)指針 i 指向樹(shù)狀結(jié)構(gòu)的根節(jié)點(diǎn),即最左邊的空白節(jié)點(diǎn):
- str[0] = ‘白’ 時(shí),此時(shí) tree[i] 沒(méi)有指向值為 ‘白’ 的節(jié)點(diǎn),所以不滿(mǎn)足匹配條件,繼續(xù)往下遍歷
- str[1] = ‘菊’,同樣不滿(mǎn)足匹配條件,繼續(xù)遍歷
- str[2] = ‘我’,此時(shí) tree[i] 有一條路徑連接著 ‘我’ 這個(gè)節(jié)點(diǎn),滿(mǎn)足匹配條件,i 指向 ‘我’ 這個(gè)節(jié)點(diǎn),然后繼續(xù)遍歷
- str[3] = ‘愛(ài)’,此時(shí) tree[i] 有一條路徑連著 ‘愛(ài)’ 這個(gè)節(jié)點(diǎn),滿(mǎn)足匹配條件,i 指向 ‘愛(ài)’,繼續(xù)遍歷
- str[4] = ‘你’,同樣有路徑,i 指向 ‘你’,繼續(xù)遍歷
- str[5] = ‘呀’,同樣有路徑,i 指向 ‘呀’
此時(shí),我們的指針 i 已經(jīng)指向了樹(shù)狀結(jié)構(gòu)的末尾,即此時(shí)已經(jīng)完成了一次敏感詞判斷。我們可以用變量來(lái)記錄下這次敏感詞匹配開(kāi)始時(shí)玩家輸入字符串的下標(biāo),和匹配結(jié)束時(shí)的下標(biāo),然后再遍歷一次將字符替換為 * 即可。
結(jié)束一次匹配后,我們把指針 i 重新指向樹(shù)狀結(jié)構(gòu)的根節(jié)點(diǎn)處。
此時(shí)我們玩家輸入的字符串還沒(méi)有遍歷到頭,所以繼續(xù)遍歷:
str[6] = ‘哈’,不滿(mǎn)足匹配條件,繼續(xù)遍歷
str[7] = ‘哈’ …
str[8] = ‘哈’ …
可以看出我們遍歷了一次玩家輸入的字符串,就找到了其中的敏感詞匯。

DFA算法python實(shí)現(xiàn)
class DFA:
"""DFA 算法
敏感字中“*”代表任意一個(gè)字符
"""
def __init__(self, sensitive_words: list, skip_words: list): # 對(duì)于敏感詞sensitive_words及無(wú)意義的詞skip_words可以通過(guò)數(shù)據(jù)庫(kù)、文件或者其他存儲(chǔ)介質(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: # 對(duì)于一些敏感詞,比如大傻X,可能是大傻B,大傻×,大傻...,采用通配符*,一個(gè)*代表一個(gè)字符
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方法生成敏感詞的樹(shù)狀結(jié)構(gòu),(以字典保存),對(duì)于上面的例子,生成的樹(shù)狀結(jié)構(gòu)保存如下:
if __name__ == '__main__':
dfa = DFA(['我愛(ài)你', '我愛(ài)他', '我愛(ài)她', '我愛(ài)你呀', '我愛(ài)他呀', '我愛(ài)她呀', '我愛(ài)她啊'], skip_words=[]) # 暫時(shí)不配置skip_words
print(dfa)結(jié)果:
{'我': {'is_end': False, '愛(ài)': {'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(['我愛(ài)你', '我愛(ài)他', '我愛(ài)她', '我愛(ài)你呀', '我愛(ài)他呀', '我愛(ài)她呀', '我愛(ài)她啊'], ['\n', '\r\n', '\r'])
# print(dfa)
print(dfa.match('白菊我愛(ài)你呀哈哈哈'))結(jié)果:
[{'start': 2, 'match': '我愛(ài)你'}, {'start': 2, 'match': '我愛(ài)你呀'}]
而對(duì)于一些敏感詞,比如大傻X,可能是大傻B,大傻×,大傻...,那是不是可以通過(guò)一個(gè)通配符*來(lái)解決?
見(jiàn)代碼:48 ~51行
if '*' in state: # 對(duì)于一些敏感詞,比如大傻X,可能是大傻B,大傻×,大傻...,采用通配符*,一個(gè)*代表一個(gè)字符 state_list[index] = state['*'] state_char = state['*'] is_match = True
驗(yàn)證一下:
if __name__ == '__main__':
dfa = DFA(['大傻*'], [])
print(dfa)
print(dfa.match('大傻X安樂(lè)飛大傻B')){'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[{'start': 0, 'match': '大傻X'}, {'start': 6, 'match': '大傻B'}]
上列中如果輸入的內(nèi)容中,“大傻X安樂(lè)飛大傻B”寫(xiě)成“大%傻X安樂(lè)飛大&傻B”,看看是否能識(shí)別出敏感詞呢?識(shí)別不出了!
if __name__ == '__main__':
dfa = DFA(['大傻*'], [])
print(dfa)
print(dfa.match('大%傻X安樂(lè)飛大&傻B'))
結(jié)果:
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[
諸如“,&,!,!,@,#,$,¥,*,^,%,?,?,<,>,《,》",這些特殊符號(hào)無(wú)實(shí)際意義,但是可以在敏感詞中間插入而破壞敏感詞的結(jié)構(gòu)規(guī)避敏感詞檢查
進(jìn)行無(wú)意義詞配置,再進(jìn)行敏感詞檢查,如下,可見(jiàn)對(duì)于被破壞的敏感詞也能識(shí)別
if __name__ == '__main__':
dfa = DFA(['大傻*'], ['%', '&'])
print(dfa)
print(dfa.match('大%傻X安樂(lè)飛大&傻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)容敏感詞過(guò)濾的詳細(xì)內(nèi)容,更多關(guān)于Python敏感詞過(guò)濾的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于Python中flask-httpauth庫(kù)用法詳解
這篇文章主要介紹了關(guān)于Python中flask-httpauth庫(kù)用法詳解,Flask-HTTPAuth是一個(gè)?Flask?擴(kuò)展,它簡(jiǎn)化了?HTTP?身份驗(yàn)證與?Flask?路由的使用,需要的朋友可以參考下2023-04-04
使用Keras畫(huà)神經(jīng)網(wǎng)絡(luò)準(zhǔn)確性圖教程
這篇文章主要介紹了使用Keras畫(huà)神經(jīng)網(wǎng)絡(luò)準(zhǔn)確性圖教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06
Python實(shí)現(xiàn)雙人五子棋對(duì)局
這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)雙人五子棋對(duì)局,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
python 實(shí)現(xiàn)二維字典的鍵值合并等函數(shù)
今天小編就為大家分享一篇python 實(shí)現(xiàn)二維字典的鍵值合并等函數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12
Python使用Webargs實(shí)現(xiàn)簡(jiǎn)化Web應(yīng)用程序的參數(shù)處理
在開(kāi)發(fā)Web應(yīng)用程序時(shí),參數(shù)處理是一個(gè)常見(jiàn)的任務(wù),Python的Webargs模塊為我們提供了一種簡(jiǎn)單而強(qiáng)大的方式來(lái)處理這些參數(shù),下面我們就來(lái)學(xué)習(xí)一下具體操作吧2024-02-02
python通過(guò)urllib2爬網(wǎng)頁(yè)上種子下載示例
這篇文章主要介紹了通過(guò)urllib2、re模塊抓種子下載的示例,需要的朋友可以參考下2014-02-02
Numpy中矩陣matrix讀取一列的方法及數(shù)組和矩陣的相互轉(zhuǎn)換實(shí)例
今天小編就為大家分享一篇Numpy中矩陣matrix讀取一列的方法及數(shù)組和矩陣的相互轉(zhuǎn)換實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07

