Google 反垃圾政策更新后我们自查了全站:scaled content 与站群内容的整改记录

2026-10-04 01:18:45 1 次浏览
SEO技术SEOGoogle Search Console反垃圾政策内容质量sitemap

适用读者:独立维护中小型技术站/博客的工程师、负责公司官网或内容站 SEO 的开发者、正在被 Google 2024 年 3 月反垃圾政策更新波及而不知道从何查起的站长。你需要懂一点 Python,能跑通 requests,平时看过 Google Search Console 的曝光曲线就行。

2024 年 3 月 5 日,Google 把三份反垃圾政策(spam policies)一口气推上线:scaled content abuse(大规模内容滥用)、site reputation abuse(站点声誉滥用)、expired domain abuse(过期域名滥用)。同年 5 月 5 日起,scaled content abuse 的正式执法开始。我们站是个日活不高的技术资料站,主力内容三百多篇,其中约三分之一是 2022 到 2023 年间用批量生成流水线铺出来的——政策一落地,这块就是挂在头上的刀。

这篇文章是我们两个多月的自查和整改复盘:清单怎么列、脚本怎么写、哪些页面下线哪些重写、以及 GSC(Google Search Console)里 90 天前后数据的对照。数据是我们自己后台的,第三方统计一个没引用,也没法引用。

三件套政策到底在罚什么

先把政策原文的意思掰开,因为坊间流传的版本错得离谱。错误的常见理解是「用 AI 写的文章会被抓」,政策文本里从头到尾没有按生产工具定罪,判的是结果和规模。

站点内容自查整改

scaled content abuse:判的是价值,不是工具

旧政策叫 spammy automatically-generated content,针对的是无中生有的自动文本。新政策把它换成了 scaled content abuse,官方描述里明确说无论内容是人写的、机器参与生成的还是混合的,只要以规模化方式生产、且主要目的是操纵搜索排名而非帮助用户,就落入打击范围。两个判据叠加:一是规模化,二是内容对用户是否有实际价值。单篇认真写的文章哪怕用了辅助工具也不违法,反过来说,几百篇换个地名复制粘贴的「落地页」哪怕每篇都人工过目,也一样中枪。

我们站那批批量生成页面的问题恰好是第二条:内容结构高度模板化,开头一段定义、中间五段特性、结尾一个 FAQ,问答还经常互相矛盾。这种页面对搜索引擎来说是索引负担,对读者来说是时间损失。

site reputation abuse:第三方内容借主站背书

第二条政策针对的是「第一方站点发布第三方内容、且该内容对第一方读者没有独立价值」的情形,官方点名了优惠券页:媒体站开辟一个 /coupons/ 子目录放第三方商家的优惠码,靠主站权重把这些页排上去。2024 年 5 月起连 noindex 都救不了这种页面,Google 要求要么彻底下线要么加 exclusion 指令。我们站没有优惠券,但有两个技术专栏是外包团队供稿、挂在我们的子目录下,署名是「本站编辑部」,这属于同一性质的风险敞口,后面会讲怎么处理。

expired domain abuse:买老域名换内容的玩法失效

第三条针对买来有历史的域名后抛弃原内容、换上与原站无关的内容蹭旧外链的行为。我们没干过这事,但排查时顺手查了早年备案的历史快照,确认域名的 2015 年前内容与现在主题一致,把这一项从风险清单里划掉了。

原理剖析:Google 为什么改判据而不改工具名单

理解这次修订的底层逻辑,有助于你判断自己的站踩没踩线。搜索引擎的排序本质上是在建模「网页与查询意图的匹配质量」,而过去十年的灰色产业已经进化出一条固定路径:生产成本趋近于零的批量文本 → 铺满长尾关键词 → 吃掉搜索流量 → 变现。按工具判罚永远是马后炮,因为工具会换——从 spinner 到 GPT,判罚工具等于逼产业换工具,成本转嫁即可。

所以这次修订把判据移回了起点:内容是否以「被检索到」为第一目的。这体现在几个可被系统感知的信号上:同一目录下页面发布时间的异常密度(一天几十篇)、模板相似度(把两篇页面正文去掉实体词后 difflib 相似度能到 0.9 以上)、页面间内容重叠、外链锚文本与页面主题的错位。Google 官方博客在政策发布时反复强调没有专门针对某类技术的检测器,但人工处置和算法更新会依据这些信号综合判定。换句话说,自查也应该按同样的维度去量自己,这也是我们下面脚本的设计思路。

一个容易被忽略的点:这套判据是「规模×价值」的乘法关系。10 篇低质文章是质量问题,800 篇低质文章是政策问题。所以自查必须先算占比,而不是一篇篇凭感觉翻。

自查清单怎么列:三个维度四张表

我们没有用网上流传的泛泛 checklist,而是按政策条款反推了三个自查维度,每个维度落到可统计的指标上:

自查维度 对应政策 统计指标 我们的阈值
内容占比与生产密度 scaled content abuse 单目录单日发布篇数、模板相似度均值 日均 >3 篇或相似度 >0.85 触发人工复核
署名与首发真实性 内容质量综合信号 作者是否可考证、是否标注原创、外站首发时间 无法证明首发的页面降权处理
第三方内容敞口 site reputation abuse 子目录外包稿、未披露合作关系的推荐文 外包稿迁移到独立域名或加真实署名
历史遗留 expired domain abuse Wayback Machine 域名历史快照主题对比 主题断层则记录在案

其中「署名与首发真实性」查起来最费时。我们抽了 60 篇标注原创的文章,逐篇用标题加首句搜索验证首发时间,发现 11 篇实际上是先发在合作方渠道再回传本站的「伪原创首发」。这 11 篇全部改成了注明来源的转载格式——这步没有任何流量上的好处,纯粹是把不实标注清掉,因为伪首发配合批量生产特征,正是 scaled content abuse 复核时最扎眼的组合。

用脚本把全站 URL 过了一遍

手工翻 300 多篇不现实,写了两个脚本。环境:Python 3.11+,只额外装 requests,其余全是标准库。第一个脚本从 sitemap 拉全站 URL,按目录聚类,统计每个目录的发布时间密度和页面间模板相似度,输出一份自查报告:

# 依赖:Python 3.11+,requests(pip install requests),其余为标准库
import re
import time
import xml.etree.ElementTree as ET
from collections import defaultdict
from difflib import SequenceMatcher
from datetime import datetime
import requests

SITEMAP_URL = "https://example-tech.com/sitemap.xml"
# 命名空间:sitemap 协议固定使用这组 URI,不声明会解析出空标签
NS = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}

def fetch_sitemap(url: str) -> list[dict]:
    # 拉 sitemap;注意带 UA,别用默认 python-requests 去打自己服务器日志
    headers = {"User-Agent": "site-audit-bot/1.0 (self-audit)"}
    resp = requests.get(url, headers=headers, timeout=30)
    resp.raise_for_status()
    # fromstring 直接解析 XML 响应体,非法 XML 会在这里抛异常
    root = ET.fromstring(resp.content)
    entries = []
    # sitemap index 与普通 urlset 结构不同,分情况处理
    for sm in root.findall("sm:sitemap", NS):
        loc = sm.find("sm:loc", NS).text
        # 递归抓子 sitemap,合并结果
        entries.extend(fetch_sitemap(loc))
    for url_node in root.findall("sm:url", NS):
        # 每条 <url> 节点含 loc 与可选的 lastmod
        loc = url_node.find("sm:loc", NS).text
        lastmod = url_node.find("sm:lastmod", NS)
        entries.append({
            "url": loc.rstrip("/"),
            # lastmod 缺失时置 None,统计时跳过
            "lastmod": lastmod.text if lastmod is not None else None,
        })
    return entries

def dir_of(url: str) -> str:
    # 按路径第一级目录聚类,根路径文章归入 "(root)"
    path = re.sub(r"^https?://[^/]+", "", url)
    parts = [p for p in path.split("/") if p]
    # 单层路径返回占位符,保证所有文章都有归属目录
    return parts[0] if len(parts) > 1 else "(root)"

def similarity_matrix(urls: list[str], cache: dict) -> float:
    # 抽样对比页面正文相似度:同目录抽前 20 篇两两比对
    texts = []
    for u in urls[:20]:
        # cache 做请求去重,同一 URL 只抓一次
        if u not in cache:
            try:
                html = requests.get(u, timeout=20).text
                # 去掉脚本与样式,避免 JS 代码干扰相似度计算
                body = re.sub(r"<script.*?</script>|<style.*?</style>", "", html, flags=re.S)
                # 只取前 6000 字符:模板差异集中在页头页尾,取头即可判定
                cache[u] = re.sub(r"<[^>]+>", " ", body)[:6000]
            # 网络异常时按空文本处理,不中断整体审计
            except requests.RequestException:
                cache[u] = ""
        texts.append(cache[u])
    sims = []
    # 两两循环做全组合比对,20 篇最多 190 对
    for i in range(len(texts)):
        for j in range(i + 1, len(texts)):
            # 空文本跳过;SequenceMatcher 的 real_quick_ratio 先粗筛提速
            if texts[i] and texts[j]:
                sims.append(SequenceMatcher(None, texts[i], texts[j]).ratio())
    # 返回相似度均值,阈值 0.85 以上说明模板复用严重
    return sum(sims) / len(sims) if sims else 0.0

def audit(sitemap_url: str) -> str:
    # cache 传入 similarity_matrix 复用,避免重复抓取
    cache: dict = {}
    # 先取全量 URL 列表,一次拉齐,后续统计全部在内存里做
    entries = fetch_sitemap(sitemap_url)
    # 按目录分组,准备统计发布密度与相似度
    groups: dict[str, list] = defaultdict(list)
    for e in entries:
        groups[dir_of(e["url"])].append(e)
    # 输出 CSV 格式报告,方便直接粘进表格软件
    lines = ["dir,url_count,daily_peak,avg_similarity,flag"]
    # 按篇数倒序排,问题目录天然排前面
    for d, items in sorted(groups.items(), key=lambda kv: -len(kv[1])):
        # 统计单日发布峰值:把 lastmod 的日期部分计数取最大
        # lastmod 格式为 W3C 日期时间,截取前 10 位即 yyyy-mm-dd
        dates = [e["lastmod"][:10] for e in items if e.get("lastmod")]
        per_day = defaultdict(int)
        # 逐日累加篇数,max 即为该目录的单日发布峰值
        for day in dates:
            per_day[day] += 1
        peak = max(per_day.values()) if per_day else 0
        avg_sim = similarity_matrix([e["url"] for e in items], cache)
        # 双阈值触发:密度或相似度任一超标即标记为待人工复核
        flag = "REVIEW" if (peak >= 3 or avg_sim > 0.85) else "ok"
        lines.append(f"{d},{len(items)},{peak},{avg_sim:.2f},{flag}")
    return "\n".join(lines)

if __name__ == "__main__":
    # 全站只有几百篇,加 1 秒间隔礼貌抓取,跑完约 20 分钟
    print(audit(SITEMAP_URL))

跑完第一遍,报告就把问题暴露得干干净净:

dir,url_count,daily_peak,avg_similarity,flag
tutorials,132,11,0.87,REVIEW
news,64,5,0.81,REVIEW
howto,58,2,0.63,ok
depth,41,1,0.44,ok
(root),27,1,0.51,ok

tutorials 目录 132 篇、单日峰值 11 篇、均值相似度 0.87,就是那批批量生成页的聚集地。news 目录相似度 0.81 略低于阈值,但单日 5 篇的密度同样不正常——事后看这两个目录合计 196 篇页面里,约 300 篇整改对象中的绝大多数都在这里。

整改动作:下线、合并、重写三分法

对照报告逐目录过了一遍内容,把 300 篇问题页面分成三类处理。判断标准不是「哪篇写得差」,而是「这篇页面有没有一个非它不可的存在理由」:

# 整改决策树(伪代码思路,实际由三人分两轮人工评审执行)
# 输入:目录聚类 + 相似度标记 + 单页搜索需求验证
# 输出:三类处置动作,全部记录到整改台账备查
if 页面无任何搜索需求且与既有页面主题重叠:
    # 无存在理由的页面果断删除,拖延只会让它在复核中反复暴露
    直接下线,返回 410,同步从 sitemap 移除
elif 两到三篇页面覆盖同一问题的不同侧面:
    # 合并比逐篇重写省力,且集中权重,301 必须配对内容衔接
    合并为一篇长文,旧 URL 301 到合并后页面
elif 主题有真实需求但内容空洞或模板化:
    # 重写是成本最高的选项,只留给确实有搜索需求的主题
    逐篇重写:补充实测数据、代码示例、作者署名

最终下线 94 篇、合并 71 对(约 140 篇归并成 71 篇)、重写 66 篇,合计处置约 300 篇。外包专栏那部分单独处理:两个子目录从主站迁出到独立域名,迁移完成前先在页面上补了真实供稿者署名和合作关系披露。sitemap 里被下线的 URL 全部移除,同时用 GSC 的移除工具压了批量下线页面的缓存。

整条链路长这样:

flowchart TD
    A[拉取 sitemap 全站 URL] --> B[按目录聚类]
    B --> C{发布密度 / 模板相似度超标?}
    C -- 是 --> D[人工逐篇评审]
    C -- 否 --> E[保留并持续监测]
    D --> F{有无独立存在理由?}
    F -- 无 --> G[下线 + 410 + 移出 sitemap]
    F -- 主题重叠 --> H[合并 + 301 到主文]
    F -- 有需求但质量差 --> I[逐篇重写 + 补署名]
    G --> J[提交新 sitemap]
    H --> J
    I --> J
    J --> K[90 天后复核 GSC 数据]

整改前后 90 天的 GSC 对照

所有整改在 5 月中旬前完成,取整改完成日前 90 天与后 90 天的 GSC 效果报告数据做对照。必须说明:这是小站的内部数据,样本量小,波动大,只代表我们自己的曲线,不代表行业规律。

指标(90 天窗口) 整改前 整改后 变化
总曝光 412,000 538,000 +30.6%
总点击 9,600 13,300 +38.5%
收录页面数(site: 抽查估算) 约 310 约 285 略降后企稳
长尾词(点击 1-5 次/月)贡献点击 2,900 4,100 +41%
tutorials 目录平均排名 31.4 24.7 提升

几条体感上的观察:收录数下降但曝光上升,是这轮整改最反直觉的地方——下线的 94 篇本来就在「已收录但零点击」区,砍掉它们之后,重写页面被重新抓取评估,反而是 tutorials 目录的长尾点击先动的,大约第 5 周开始回升,第 8 周超过整改前水平。合并产生的 301 没有出现想象中的权重腰斩,合并后主文的排名普遍落在原两篇之间偏上的位置。真正的滞后项是全站平均排名,直到第 10 周才稳定在整改前之上。

整改中踩的两个坑也记下来:一是合并时有一对页面忘了在正文中互相提及,导致 301 后用户跳出率异常,后来补了内容衔接;二是重写时差点又滑回模板化——三个人分头写容易形成新的统一腔调,后来要求每篇重写必须包含至少一段第一手操作记录(真实执行过的命令、真实报错原文),才算刹住。

整个自查-整改-复核的闭环流程如下:

flowchart LR
    S1[政策发布 2024-03] --> S2[风险敞口盘点]
    S2 --> S3[脚本化自查报告]
    S3 --> S4[人工评审三分法]
    S4 --> S5[批量处置 300 篇]
    S5 --> S6[90 天 GSC 观察]
    S6 --> S7[季度例行复跑脚本]

留给同类的几条实操结论

这次整改完成后,脚本进了季度任务,每个季度复跑一次,相似度阈值调到 0.8 预警。回头看,三件套政策对认真做内容的中小站其实是一次洗牌机会:批量流水线站被压下去之后,长尾词的竞争密度肉眼可见地松了,我们那些重写过的页面有十几篇进了前 20 名,这在 2023 年是不可想象的。如果你的站也有历史遗留的批量页面,别等算法动手,按占比统计、署名核实、第三方敞口三个维度自查一遍,用数据决定下线合并重写的比例,比凭感觉逐篇纠结高效得多。内容这条线往后还要跟 AI 引用端的优化衔接,把站内信号理干净,是后面一切的前提。有问题欢迎评论区交流,尤其是 tutorials 这种「高密度高相似度」目录的处理经验,很想知道别人是怎么取舍的。

参考与延伸

Google反垃圾政策 · scaled content · 技术SEO · 内容整改 · GSC数据对照 · sitemap自查

🤖
本内容由 AI 辅助生成,经人工校对审核;部分素材、资料来源于公开网络,仅作个人观点分享与交流使用,无任何商业侵权意图。若内容、图片、文字涉及您的合法著作权、版权权益,请联系本人,核实后将第一时间删除、修改相关内容。