serverless实战:3分钟实现文本敏感词过滤-金马国际
- 2020-05-10
本文字数:12146 字
阅读完需:约 40 分钟

敏感词过滤是随着互联网社区一起发展起来的一种阻止网络犯罪和网络暴力的技术手段,通过对可能存在犯罪或网络暴力的关键词进行有针对性的筛查和屏蔽,能够防患于未然,将后果严重的犯罪行为扼杀于萌芽之中。
随着各种社交论坛的日益火爆,敏感词过滤逐渐成为了非常重要的功能。那么在 serverless 架构下,利用 python 语言,敏感词过滤又有那些新的实现呢?我们能否用最简单的方法实现一个敏感词过滤的 api 呢?
了解敏感过滤的几种方法
replace 方法
敏感词过滤,其实在一定程度上是文本替换,以 python 为例,我们可以通过replace
来实现,首先准备一个敏感词库,然后通过replace
进行敏感词替换:
def worldfilter(keywords, text):
for eve in keywords:
text = text.replace(eve, "***")
return text
keywords = ("关键词1", "关键词2", "关键词3")
content = "这是一个关键词替换的例子,这里涉及到了关键词1还有关键词2,最后还会有关键词3。"
print(worldfilter(keywords, content))
这种方法虽然操作简单,但是存在一个很大的问题:在文本和敏感词汇非常庞大的情况下,会出现很严重的性能问题。
举个例子,我们先修改代码进行基本的性能测试:
import time
def worldfilter(keywords, text):
for eve in keywords:
text = text.replace(eve, "***")
return text
keywords =[ "关键词" str(i) for i in range(0,10000)]
content = "这是一个关键词替换的例子,这里涉及到了关键词1还有关键词2,最后还会有关键词3。" * 1000
starttime = time.time()
worldfilter(keywords, content)
print(time.time()-starttime)
此时的输出结果是:0.12426114082336426
,可以看到性能非常差。
正则表达方法
相较于replace
,使用正则表达re.sub
实现可能更加快速。
import time
import re
def worldfilter(keywords, text):
return re.sub("|".join(keywords), "***", text)
keywords =[ "关键词" str(i) for i in range(0,10000)]
content = "这是一个关键词替换的例子,这里涉及到了关键词1还有关键词2,最后还会有关键词3。" * 1000
starttime = time.time()
worldfilter(keywords, content)
print(time.time()-starttime)
增加性能测试之后,我们按照上面的方法进行改造测试,输出结果是0.24773502349853516
。
对比这两个例子,我们会发现当前两种方法的性能差距不是很大,但是随着文本数量的增加,正则表达的优势会逐渐凸显,性能提升明显。
dfa 过滤敏感词
相对来说,dfa 过滤敏感词的效率会更高一些,例如我们把坏人、坏孩子、坏蛋作为敏感词,那么它们的树关系可以这样表达:
而 dfa 字典是这样表示的:
{
'坏': {
'蛋': {
'\x00': 0
},
'人': {
'\x00': 0
},
'孩': {
'子': {
'\x00': 0
}
}
}
}
使用这种树表示问题最大的好处就是可以降低检索次数、提高检索效率。其基本代码实现如下:
import time
class dfafilter(object):
def __init__(self):
self.keyword_chains = {} # 关键词链表
self.delimit = '\x00' # 限定
def parse(self, path):
with open(path, encoding='utf-8') as f:
for keyword in f:
chars = str(keyword).strip().lower() # 关键词英文变为小写
if not chars: # 如果关键词为空直接返回
return
level = self.keyword_chains
for i in range(len(chars)):
if chars[i] in level:
level = level[chars[i]]
else:
if not isinstance(level, dict):
break
for j in range(i, len(chars)):
level[chars[j]] = {}
last_level, last_char = level, chars[j]
level = level[chars[j]]
last_level[last_char] = {self.delimit: 0}
break
if i == len(chars) - 1:
level[self.delimit] = 0
def filter(self, message, repl="*"):
message = message.lower()
ret = []
start = 0
while start < len(message):
level = self.keyword_chains
step_ins = 0
for char in message[start:]:
if char in level:
step_ins = 1
if self.delimit not in level[char]:
level = level[char]
else:
ret.append(repl * step_ins)
start = step_ins - 1
break
else:
ret.append(message[start])
break
else:
ret.append(message[start])
start = 1
return ''.join(ret)
gfw = dfafilter()
gfw.parse( "./sensitive_words")
content = "这是一个关键词替换的例子,这里涉及到了关键词1还有关键词2,最后还会有关键词3。" * 1000
starttime = time.time()
result = gfw.filter(content)
print(time.time()-starttime)
这里的字典库是:
with open("./sensitive_words", 'w') as f:
f.write("\n".join( [ "关键词" str(i) for i in range(0,10000)]))
执行结果:
0.06450581550598145
从中,我们可以看到性能又进一步得到了提升。
ac 自动机过滤敏感词算法
什么是 ac 自动机?简单来说,ac 自动机就是字典树 kmp 算法 失配指针,一个常见的例子就是给出 n 个单词,再给出一段包含 m 个字符的文章,让你找出有多少个单词在文章里出现过。
代码实现:
import time
class node(object):
def __init__(self):
self.next = {}
self.fail = none
self.isword = false
self.word = ""
class acautomation(object):
def __init__(self):
self.root = node()
# 查找敏感词函数
def search(self, content):
p = self.root
result = []
currentposition = 0
while currentposition < len(content):
word = content[currentposition]
while word in p.next == false and p != self.root:
p = p.fail
if word in p.next:
p = p.next[word]
else:
p = self.root
if p.isword:
result.append(p.word)
p = self.root
currentposition = 1
return result
# 加载敏感词库函数
def parse(self, path):
with open(path, encoding='utf-8') as f:
for keyword in f:
temp_root = self.root
for char in str(keyword).strip():
if char not in temp_root.next:
temp_root.next[char] = node()
temp_root = temp_root.next[char]
temp_root.isword = true
temp_root.word = str(keyword).strip()
# 敏感词替换函数
def wordsfilter(self, text):
"""
:param ah: ac自动机
:param text: 文本
:return: 过滤敏感词之后的文本
"""
result = list(set(self.search(text)))
for x in result:
m = text.replace(x, '*' * len(x))
text = m
return text
acautomation = acautomation()
acautomation.parse('./sensitive_words')
starttime = time.time()
print(acautomation.wordsfilter("这是一个关键词替换的例子,这里涉及到了关键词1还有关键词2,最后还会有关键词3。"*1000))
print(time.time()-starttime)
词库同样是:
with open("./sensitive_words", 'w') as f:
f.write("\n".join( [ "关键词" str(i) for i in range(0,10000)]))
使用上面的方法,测试结果为0.017391204833984375
。
敏感词过滤方法小结
根据上文的测试对比,我们可以发现在所有算法中,dfa 过滤敏感词性能最高,但是在实际应用中,dfa 过滤和 ac 自动机过滤各自有自己的适用场景,可以根据具体业务来选择。
实现敏感词过滤 api
想要实现敏感词过滤 api,就需要将代码部署到 serverless 架构上,选择 api 网关与函数计算进行结合。以 ac 自动机过滤敏感词算法为例:我们只需要增加是几行代码就好:
# -*- coding:utf-8 -*-
import json, uuid
class node(object):
def __init__(self):
self.next = {}
self.fail = none
self.isword = false
self.word = ""
class acautomation(object):
def __init__(self):
self.root = node()
# 查找敏感词函数
def search(self, content):
p = self.root
result = []
currentposition = 0
while currentposition < len(content):
word = content[currentposition]
while word in p.next == false and p != self.root:
p = p.fail
if word in p.next:
p = p.next[word]
else:
p = self.root
if p.isword:
result.append(p.word)
p = self.root
currentposition = 1
return result
# 加载敏感词库函数
def parse(self, path):
with open(path, encoding='utf-8') as f:
for keyword in f:
temp_root = self.root
for char in str(keyword).strip():
if char not in temp_root.next:
temp_root.next[char] = node()
temp_root = temp_root.next[char]
temp_root.isword = true
temp_root.word = str(keyword).strip()
# 敏感词替换函数
def wordsfilter(self, text):
"""
:param ah: ac自动机
:param text: 文本
:return: 过滤敏感词之后的文本
"""
result = list(set(self.search(text)))
for x in result:
m = text.replace(x, '*' * len(x))
text = m
return text
def response(msg, error=false):
return_data = {
"uuid": str(uuid.uuid1()),
"error": error,
"message": msg
}
print(return_data)
return return_data
acautomation = acautomation()
path = './sensitive_words'
acautomation.parse(path)
def main_handler(event, context):
try:
sourcecontent = json.loads(event["body"])["content"]
return response({
"sourcecontent": sourcecontent,
"filtedcontent": acautomation.wordsfilter(sourcecontent)
})
except exception as e:
return response(str(e), true)
最后,为了方便本地测试,我们可以再增加以下代码:
def test():
event = {
"requestcontext": {
"serviceid": "service-f94sy04v",
"path": "/test/{path}",
"httpmethod": "post",
"requestid": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
"identity": {
"secretid": "abdcdxxxxxxxsdfs"
},
"sourceip": "14.17.22.34",
"stage": "release"
},
"headers": {
"accept-language": "en-us,en,cn",
"accept": "text/html,application/xml,application/json",
"host": "service-3ei3tii4-251000691.ap-guangzhou.apigateway.myqloud.com",
"user-agent": "user agent string"
},
"body": "{\"content\":\"这是一个测试的文本,我也就呵呵了\"}",
"pathparameters": {
"path": "value"
},
"querystringparameters": {
"foo": "bar"
},
"headerparameters": {
"refer": "10.0.2.14"
},
"stagevariables": {
"stage": "release"
},
"path": "/test/value",
"querystring": {
"foo": "bar",
"bob": "alice"
},
"httpmethod": "post"
}
print(main_handler(event, none))
if __name__ == "__main__":
test()
完成之后,就可以进行测试运行,例如我的字典是:
呵呵
测试
执行之后结果:
{'uuid': '9961ae2a-5cfc-11ea-a7c2-acde48001122', 'error': false, 'message': {'sourcecontent': '这是一个测试的文本,我也就呵呵了', 'filtedcontent': '这是一个**的文本,我也就**了'}}
接下来,我们将代码部署到云端,新建serverless.yaml
:
sensitive_word_filtering:
component: "@serverless/tencent-scf"
inputs:
name: sensitive_word_filtering
codeuri: ./
exclude:
- .gitignore
- .git/**
- .serverless
- .env
handler: index.main_handler
runtime: python3.6
region: ap-beijing
description: 敏感词过滤
memorysize: 64
timeout: 2
events:
- apigw:
name: serverless
parameters:
environment: release
endpoints:
- path: /sensitive_word_filtering
description: 敏感词过滤
method: post
enablecors: true
param:
- name: content
position: body
required: 'false'
type: string
desc: 待过滤的句子
然后通过sls --debug
进行部署,部署结果:
最后,通过 postman 进行测试:
总结
敏感词过滤是当前企业的普遍需求,通过敏感词过滤,我们可以在一定程度上遏制恶言恶语和违规言论的出现。在具体实现过程中,有两个方面需要额外主要:
敏感词库的获得问题:github 上有很多敏感词库,其中包含了各种场景中的敏感词,大家可以自行搜索下载使用;
api 使用场景的问题:我们可以将这个 api 放置在社区跟帖系统、留言评论系统或者是博客发布系统中,这样可以防止出现敏感词汇,减少不必要的麻烦。
作者介绍:
刘宇,腾讯 serverless 团队后台研发工程师。毕业于浙江大学,硕士研究生学历,曾在滴滴出行、腾讯科技做产品经理,本科开始有自主创业经历,是 anycodes 在线编程的负责人(该软件累计下载量超 100 万次)。目前投身于 serverless 架构研发,著书《serverless 架构:从原理、设计到项目实战》,参与开发和维护多个 serverless 组件,是活跃的 serverless framework 的贡献者,也曾多次公开演讲和分享 serverless 相关技术与经验,致力于 serverless 的落地与项目上云。
更多内容推荐
已知 alist = [1, 2, 3],bset = {1, 2, 3} (1)从alist和bset中查找4,最坏时间复杂度哪个大?(2)从alist和bset中插入4,最坏时间复杂度哪个大?
2022-08-30
11 月 3 日,在 2022 云栖大会“云计算加速开源创新”主题论坛上,阿里巴巴开源委员会举行了“2022 年度开源人物”颁奖仪式,龙蜥社区理事长、阿里云研究员马涛荣获该奖项。
2022-11-15
2023 ccf中国开源大会(ccf chinaosc)于2023年10月21日至22日在湖南省长沙市北辰国际会议中心召开。大会由开放原子开源基金会与中国计算机学会(ccf)主办,ccf开源发展委员会、湖南先进技术研究院承办,中国电子信息产业集团有限公司协办,csdn社区特别支持
2023-10-13
2022-12-29
摘要:错字率是ocr任务中的重要指标,文本纠错需要机器具备人类水平相当的语言理解能力。随着人工智能应用的成熟,越来越多的纠错方法被提出。
2022-08-24
本文介绍的是关键词即特定场景语料,在序列到序列任务中通过构建状态转移自动机的方法改善最终效果的方案。
编写 kubernetes 部署脚本将 httpserver 部署到 kubernetes 集群
2022-02-28
注:del是一个关键词,而不是一个函数所以不是使用小括号:del(names[2]),而是使用空格:del names[2]
2023-05-14
2022-09-08
兆骑科创高层次人才引进服务平台,创业大赛,云路演
2022-08-24
2022-09-08
文本已收录至我的github仓库,欢迎star:https://github.com/bin392328206/six-finger
2022-09-08
本月 10 本新书,本本都是精选。
2022-08-27
从这节课开始,我们正式进入爬虫项目的实战环节。
2022-11-22
这节课我们就来看看jsx是如何用在web ui开发中的。即使你不使用react,这样的模版模式也有很大的借鉴意义。
2022-12-17
这节课重点学习web中常见的漏洞和攻击。
2022-11-29
给你一个仅包含小写英文字母和 '?' 字符的字符串 s,请你将所有的 '?' 转换为若干小写字母,使最终的字符串不包含任何 连续重复 的字符。
2022-01-06
注:del是一个关键词,而不是一个函数所以不是使用小括号:del(names[2]),而是使用空格:del names[2]
2023-10-10
linux 提供了这样的机制,这种机制被称为模块(module)。模块具有这样的特点。
2022-07-21
近期,关于 chatgpt 的访问量有所下降的消息引发激烈讨论,不过这并不意味着开发者对于 aigc 的热情有所减弱,例如素有【2023 最潮大语言模型 web 开发框架】之称的大网红 langchain 的热度就只增不减。
2023-07-14
推荐阅读
2023-12-07
2023-12-05
2023-10-29
2023-02-15
2023-09-26
2023-05-31
2023-12-08
电子书

业内首个分布式云行业实践方法论、工具箱。
大厂实战ppt下载
姚立 | 字节跳动 架构前端平台架构 团队负责人
吴忠远 | ebay ai 平台架构师
贾立 | 哈啰出行 软件研发中心/基础算法负责人
评论