船说 CMS 采集重复内容怎么过滤

chinaz
chinaz 正式会员超兽战士
发布于 2026-10-07 15:05 ·1 浏览 ·0 回复

学完这篇,你能给船说CMS的采集流程加上一套「三层查重」——标题重复、正文重复、改写重复全部在上架前拦掉,并且把库里已经堆着的历史重复数据清出来。

第一步:先分清你遇到的是哪种重复

这一步要做的,是先判断重复的类型,因为三种重复要用的过滤手段完全不同。

  • 同源重复:同一个源站的文章被采了两遍。特征是 source_url(原文地址)完全一样。这种最好治,一个唯一索引就够。
  • 跨源重复:A 站和 B 站采了同一篇稿子,标题差一两个字,正文一模一样。特征是真标题/正文高度相似。
  • 改写重复:对方用工具洗过稿,加了「小编」「以上就是」这类废话。标题正文都不完全一样,只能靠内容指纹判断。

注意:不要一上来就开「正文完全相同才过滤」,那只能拦住第一种,很多站长开完发现重复照旧,就是因为没区分类型。

先跑一条 SQL 看看你库里到底哪种多。表前缀在后台「系统 → 数据库管理」或 config/database.php 里能查到,下面统一用 cs_ 举例:

SELECT source_url, COUNT(*) c FROM cs_article
GROUP BY source_url HAVING c > 1 ORDER BY c DESC LIMIT 20;

如果结果的 c 值很大,说明是同源重复为主;如果这条查不出东西但你觉得重复很多,那就是后两种。

第二步:打开采集规则里的查重开关

这一步要做的,是在采集规则里开启自带的重复检测,让重复文章在「入库前」就被丢掉,而不是入库后再删。

进入后台 **采集管理 → 采集规则 → 找到你的规则 → 点「编辑」→ 切到「高级设置」或「其他设置」标签页**(不同小版本标签名可能差一两个字,认准「重复」「查重」「去重」这几个关键词)。里面通常有三个勾选项:

  1. 标题重复检测 —— 勾上,选「跳过」而不是「覆盖」。选覆盖会把老文章更新掉,采集量一大很容易误伤。
  2. 内容重复检测 —— 勾上,比对范围选「全文」。
  3. 源地址(URL)重复检测 —— 必勾,这是成本最低、最不容易误判的一项。

保存后先手动跑一条测试规则,采 10 篇已知重复的文章,看日志里是不是都显示「已存在,跳过」。

注意:这些开关只管「之后采的」,不会清理已经入库的重复数据。清历史数据看第四步。

第三步:给数据库加唯一索引兜底

这一步要做的,是在数据库层加一道硬约束,防止程序逻辑漏判。先加一个存放指纹的字段:

ALTER TABLE cs_article ADD COLUMN title_hash CHAR(32) DEFAULT NULL;
ALTER TABLE cs_article ADD COLUMN content_hash CHAR(32) DEFAULT NULL;
CREATE INDEX idx_title_hash ON cs_article(title_hash);
CREATE INDEX idx_content_hash ON cs_article(content_hash);

指纹建议用「去掉标点和空白后再哈希」,这样「【重磅】苹果发布新机!」和「苹果发布新机」会撞在一起:

$t = preg_replace('/[\s\p{P}\p{S}]+/u', '', $title);
$title_hash = md5(mb_substr($t, 0, 40, 'UTF-8'));
$c = preg_replace('/[\s\p{P}\p{S}]+/u', '', strip_tags($content));
$content_hash = md5(mb_substr($c, 0, 500, 'UTF-8'));

在采集入库的那段代码里,插入前先 SELECT id FROM cs_article WHERE content_hash = ?,查到就跳过。

注意:别直接建 UNIQUE 唯一索引。一旦有历史重复数据,ALTER 会直接失败;而且采集站经常需要保留同一标题的不同期号文章,唯一索引会误杀。

第四步:用 SQL 找出并清理历史重复

这一步要做的,是把已经存在的重复数据找出来删掉,只保留 ID 最小的那条:

-- 先备份
CREATE TABLE cs_article_bak_20250101 AS SELECT * FROM cs_article;

-- 看看有多少重复
SELECT content_hash, COUNT(*) c FROM cs_article
WHERE content_hash IS NOT NULL
GROUP BY content_hash HAVING c > 1;

-- 删掉重复,保留最小 id
DELETE t1 FROM cs_article t1
INNER JOIN cs_article t2
  ON t1.content_hash = t2.content_hash AND t1.id > t2.id;

跑完把第三步的字段回填:

UPDATE cs_article SET title_hash = MD5(REPLACE(REPLACE(title,' ',''),' ',''))
WHERE title_hash IS NULL;

注意:删除前一定先建备份表。删除操作不可回滚,几百篇文章删错就白采了。

第五步:对付改写稿,用相似度而不是完全匹配

这一步要做的,是拦住经过洗稿的重复内容。完全匹配的哈希拦不住它,需要做相似度判断。

一个成本很低的土办法:把正文去掉标点后取前 300 字,再按 3 字一组切分,比对两组切片的交集比例,超过 70% 就判定重复。用 PHP 写大概十几行:

function shingles($text, $n = 3) {
    $text = preg_replace('/[\s\p{P}\p{S}]+/u', '', $text);
    $len = mb_strlen($text, 'UTF-8');
    $set = [];
    for ($i = 0; $i + $n <= $len; $i++) {
        $set[mb_substr($text, $i, $n, 'UTF-8')] = 1;
    }
    return $set;
}
// 交集 / 并集 > 0.7 视为重复

采集量大的话,这一步放在入库前会明显拖慢速度,建议改成「先入库、标记为待审,再用定时任务批量比对」,在后台 系统 → 计划任务 里挂一个每小时执行的脚本。

第六步:把查重挂进发布流程,而不是只挂在采集上

这一步要做的,是防止有人从后台「手动添加文章」绕过查重。在发布/保存文章的控制器里,插入前统一调一次指纹检查函数,这样不管数据从采集来还是手动填,都走同一道闸。


小结

  1. 先分类:同源重复靠 source_url,跨源重复靠正文哈希,改写重复靠相似度,三种手段不能混用。
  2. 采集规则里的查重开关只能管以后,救不了历史数据。
  3. 数据库层加普通索引 + 程序判重,不要直接上唯一索引。
  4. 清理历史重复前必须建备份表,DELETE 不可回滚。
  5. 查重逻辑要写在发布入口,不能只写在采集入口,否则后台手动添加会绕过去。
版权声明:本文来自 GJ站长论坛《船说 CMS 采集重复内容怎么过滤》
原文链接:https://www.gj0.com/thread-357.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。

全部回复 0

还没有回复,来抢沙发~