本文目录导读:

- 方案一:基于关键词与正则表达式的本地过滤(免费、适合基础敏感词屏蔽)
- 方案二:调用第三方云内容审核API(推荐、成本可控、高准确率)
- 方案三:实现完整的人工审核工作流(后台管理)
- 方案四:针对图片/视频内容的审核
- 总结:如何选择?
在 PHP 项目中实现内容审核功能,通常有三种主流路径:基于关键词的本地过滤、调用第三方云服务 API(如阿里云、百度AI、腾讯云)以及构建本地 AI 模型(难度最大,适合高级场景)。
下面是一个从简单到复杂、从低成本到高精度的完整实现方案。
基于关键词与正则表达式的本地过滤(免费、适合基础敏感词屏蔽)
这是最基础、成本最低的方案,适合屏蔽明确的脏话、广告词或违禁词。
数据结构设计(MySQL)
CREATE TABLE `sensitive_words` ( `id` int(11) NOT NULL AUTO_INCREMENT, `word` varchar(100) NOT NULL COMMENT '敏感词', `word_type` tinyint(1) DEFAULT '1' COMMENT '1=政治, 2=色情, 3=广告', `status` tinyint(1) DEFAULT '1' COMMENT '1启用 0禁用', `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `word` (`word`) ) ENGINE=InnoDB;
PHP 敏感词过滤类
class SensitiveFilter {
private $dict = [];
private $config = [];
public function __construct() {
// 加载敏感词库(建议使用Redis缓存,避免每次查询数据库)
$this->loadDictFromCache();
}
/**
* 检测文本是否包含敏感词
* @param string $text
* @return array ['hit' => true/false, 'words' => [...], 'text' => '过滤后的文本']
*/
public function check($text) {
$hitWords = [];
$filteredText = $text;
// 遍历敏感词进行匹配(优化:使用Trie树实现DFA算法性能更好)
foreach ($this->dict as $word) {
if (mb_stripos($text, $word) !== false) {
$hitWords[] = $word;
// 替换为星号(在实际审核中可能不替换,而是标记待审)
$filteredText = str_ireplace($word, '**', $filteredText);
}
}
return [
'hit' => !empty($hitWords),
'words' => array_unique($hitWords),
'text' => $filteredText
];
}
private function loadDictFromCache() {
// 从Redis或文件读取敏感词列表
$this->dict = ['傻逼', '操你妈', '赌博', '毒品']; // 示例
}
}
在业务逻辑中使用
$content = $_POST['content'];
$filter = new SensitiveFilter();
$result = $filter->check($content);
if ($result['hit']) {
// 方案A:直接拦截并提示用户(适合论坛发帖)
throw new \Exception('含有违禁词:' . implode(',', $result['words']));
// 方案B:自动将其状态设为待审核(更适合CMS、新闻系统)
$contentModel->saveWithStatus($content, 'pending_review');
}
局限性:无法识别拼音变体、谐音(如“草泥马”)、图片中的文字、上下文语义(“我买了一个毒品”vs“吸毒有害健康”)。
调用第三方云内容审核API(推荐、成本可控、高准确率)
这是目前大多数商业项目采用的最佳实践,只需调用HTTP接口,云服务商会自动识别涉政、色情、辱骂、广告等。
主流服务商对比
| 服务商 | 免费额度( | 文档地址 | 特点 |
|---|---|---|---|
| 阿里云 | 每月1000次免费 | 文档 | 支持文本、图片、视频、语音 |
| 百度AI | 每天5万次免费 | 文档 | 有专门的文本审核接口 |
| 腾讯云 | 每月1万次免费 | 文档 | 支持多语言、表情包 |
| 网易易盾 | 1万次/天 | 文档 | 游戏行业常用 |
示例:阿里云文本审核(PHP实现)
安装SDK
composer require alibabacloud/sdk
审核类(建议封装成Service)
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
class ContentModerationService {
private $accessKeyId;
private $accessKeySecret;
public function __construct() {
$this->accessKeyId = 'your-access-key-id';
$this->accessKeySecret = 'your-access-key-secret';
AlibabaCloud::accessKeyClient($this->accessKeyId, $this->accessKeySecret)
->regionId('cn-shanghai')
->asDefaultClient();
}
/**
* 审核文本内容
* @param string $text
* @param string $userId 用户ID(用于关联审核记录)
* @return array ['pass'=>true/false, 'label'=>'', 'suggestion'=>'', 'riskLevel'=>'']
*/
public function checkText($text, $userId = null) {
try {
$result = AlibabaCloud::green()
->v20220302()
->textModeration()
->withService('nickname_detection') // 使用昵称检测服务,还可选 'comment_detection'
->withServiceParameters(json_encode([
'content' => $text,
'userId' => $userId
]))
->request();
$data = $result->toArray();
// 解析返回结果
if (isset($data['Data']['Results'])) {
$ext = $this->parseResult($data['Data']['Results']);
return $ext;
}
// 默认通过
return ['pass' => true, 'label' => 'normal', 'suggestion' => 'pass', 'riskLevel' => 'none'];
} catch (ClientException $e) {
// Log error
return ['pass' => false, 'error' => $e->getErrorMessage()];
} catch (ServerException $e) {
return ['pass' => false, 'error' => $e->getErrorMessage()];
}
}
private function parseResult($results) {
foreach ($results as $result) {
$label = $result['Label'] ?? '';
$suggestion = $result['Suggestion'] ?? 'pass';
$riskLevel = $result['RiskLevel'] ?? 'low';
if ($suggestion === 'block') {
// 高风险,需拦截
return ['pass' => false, 'label' => $label, 'suggestion' => 'block', 'riskLevel' => $riskLevel];
} elseif ($suggestion === 'review') {
// 疑似违规,需人工审核
return ['pass' => 'review', 'label' => $label, 'suggestion' => 'review', 'riskLevel' => $riskLevel];
}
}
// 全部通过
return ['pass' => true, 'label' => 'normal', 'suggestion' => 'pass', 'riskLevel' => 'low'];
}
}
业务调用与异步审核策略
public function storeComment(Request $request) {
$commentText = $request->input('content');
$moderation = new ContentModerationService();
$result = $moderation->checkText($commentText);
if ($result['pass'] === false) {
// 明确违规:直接拒绝并提示
return response()->json(['code' => 403, 'msg' => '内容包含违规信息', 'detail' => $result['label']]);
} elseif ($result['pass'] === 'review') {
// 疑似违规:保存为草稿,进入人工审核队列
$comment = Comment::create([
'content' => $commentText,
'status' => 'pending_review', // 待审状态
'risk_label' => $result['label']
]);
// 发送通知给管理员
return response()->json(['code' => 200, 'msg' => '提交成功,等待审核']);
} else {
// 通过
$comment = Comment::create([
'content' => $commentText,
'status' => 'active'
]);
return response()->json(['code' => 200, 'msg' => '发布成功']);
}
}
实现完整的人工审核工作流(后台管理)
使用API只能完成机器审核,大多数合规系统需要多级审核流程。
数据库设计(审核记录表)
CREATE TABLE `audit_records` ( `id` int(11) NOT NULL AUTO_INCREMENT, `resource_type` varchar(50) NOT NULL COMMENT '资源类型: article/comment/image/user', `resource_id` int(11) NOT NULL COMMENT '原始资源ID', `content_snapshot` text COMMENT '内容快照(防止原始内容被修改)', `machine_label` varchar(50) DEFAULT NULL COMMENT '机器审核标签', `machine_risk_level` varchar(20) DEFAULT NULL COMMENT '机器风险等级: high/middle/low', `human_reviewer` int(11) DEFAULT NULL COMMENT '人工审核员ID', `human_status` tinyint(1) DEFAULT '0' COMMENT '0未审 1通过 2驳回 3忽略', `audit_remark` varchar(500) DEFAULT NULL COMMENT '审核备注', `audited_at` datetime DEFAULT NULL COMMENT '审核时间', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_resource` (`resource_type`,`resource_id`), KEY `idx_status` (`human_status`), KEY `idx_machine_status` (`machine_risk_level`) ) ENGINE=InnoDB;
管理员审核后台(Laravel示例)
// 审核列表页
$pendingItems = AuditRecord::where('human_status', 0)
->orderBy('created_at', 'asc')
->paginate(20);
// 审核通过
public function approve($id) {
$record = AuditRecord::findOrFail($id);
$record->human_status = 1; // 通过
$record->human_reviewer = auth()->id();
$record->audited_at = now();
$record->save();
// 更新原始资源状态
$resourceClass = $this->getResourceClass($record->resource_type);
$resource = $resourceClass::find($record->resource_id);
$resource->status = 'active';
$resource->save();
// 记录操作日志
return redirect()->back()->with('success', '审核通过');
}
// 审核驳回
public function reject(Request $request, $id) {
$request->validate(['reason' => 'required|string|max:500']);
$record = AuditRecord::findOrFail($id);
$record->human_status = 2; // 驳回
$record->human_reviewer = auth()->id();
$record->audit_remark = $request->input('reason');
$record->audited_at = now();
$record->save();
// 通知内容发布者
$this->notifyUser($record, 'rejected', $request->input('reason'));
return redirect()->back()->with('success', '已驳回');
}
自动处理策略(定时任务/队列)
// 每天凌晨清理超过7天的待审核数据
$schedule->call(function () {
AuditRecord::where('human_status', 0)
->where('created_at', '<', now()->subDays(7))
->update(['human_status' => 3]); // 自动忽略
})->daily();
针对图片/视频内容的审核
审核不止是文本,以下以图片审核为例(调用阿里云):
public function checkImage($imagePath) {
try {
$result = AlibabaCloud::green()
->v20220302()
->imageModeration()
->withService('baselineCheck') // 基线检测
->withServiceParameters(json_encode([
'imageUrl' => $imagePath,
'dataId' => uniqid()
]))
->request();
$data = $result->toArray();
// 解析结果... 类似文本审核的流程
} catch (\Exception $e) {
Log::error('图片审核失败: ' . $e->getMessage());
return ['pass' => true]; // 审核失败时,默认通过(或拦截)
}
}
如何选择?
| 场景 | 推荐方案 | 成本 |
|---|---|---|
| 个人博客、小型社区,流量<1000/天 | 方案一(关键词过滤) | 0元,需维护词库 |
| 中型社区、电商评论、UGC内容平台 | 方案二(云API)+ 人工审核 | 每月几百~几千元 |
| 短视频/直播平台(视频+音频审核) | 方案二(腾讯/阿里云) | 按量计费,较高 |
| 金融/政府/医疗行业(需极高准确率) | 云API + 本地AI模型 + 多级人工审核 | 高,需专业团队 |
最佳实践建议:
- 前端配合:用户输入时就调用本地过滤(防抖)快速提示,节省API调用成本。
- 用户分级:高信用用户可直接通过,低信用用户触发机器+人工双重审核。
- 缓存结果:同一段内容如果多人发布(如引用新闻),可缓存审核结果。
- 日志留存:所有审核记录保留至少6个月(法规要求)。
如果你需要具体的某个服务商的SDK封装代码(例如阿里云、腾讯云),我可以继续帮你写完整的封装类。