侧边栏壁纸
  • 累计撰写 197 篇文章
  • 累计收到 2 条评论

为了防爬虫采集给文章加了前端混淆,搜索收录却断崖暴跌?排查自定义字体乱码、CSS 隐藏文本被判作弊与 JS 解密快照白屏我踩过的几个坑

2026-10-15 / 0 评论 / 4 阅读
AI 摘要由 AI 生成

为防止爬虫采集,作者采用了自定义字体混淆、隐藏文本和JS解密等手段,却导致百度和谷歌搜索收录断崖式下跌。原因在于搜索引擎无法识别自定义字体和隐藏文本,误判为作弊行为。文章详细分析了问题原因和解决方案,强调正确的防御姿势。

起因:防住了无脑采集站,却把百度和谷歌蜘蛛一起送走了

上个月做内容站的朋友跑来找我诉苦。他的技术站每天被几十个无脑爬虫按分钟爬取,刚发两分钟的原创文章,转头就出现在各大垃圾采集站上,甚至有些权重高的采集站排名比他还靠前。

他实在忍无可忍,拉着前端同学连夜上了三套业内“常见”的反爬防御:
先把关键技术名词和参数用自定义 WebFont 字体做 Unicode 编码混淆(大众点评、猫眼同款骚操作);
接着在正文段落里随机插入大量带有 display:none 的隐藏垃圾干扰词,用来污染采集者的文本清洗脚本;
最后还把正文核心代码块做成 AES 密文,页面加载时通过 JS 动态解密注入 DOM。

上线当天效果立竿见影:普通 Python 脚本抓下来的全是乱码和密文,几个知名采集站直接熄火,大家还开开心心去喝了杯奶茶。

结果不到两周,悲剧发生了。

站长后台的数据如同高空坠物:百度搜索资源平台的“抓取频次”腰斩,新文章收录彻底停滞,原本排在首页的核心关键词直接掉出前 100 页。打开搜索诊断一看,抓取快照里的内容不堪入目:要么是一整片空白,要么全是框框问号乱码,甚至收到了“页面存在作弊违规(隐藏文本)”的站内信。

防采集防到了把自己整个站点送进搜索引擎黑名单,这种代价实在太惨烈。今天把这套组合拳背后逐一拆解,把这里面的技术原理、搜索引擎的工作逻辑以及正确的防御姿势讲透。

坑一:自定义 WebFont 字体混淆,搜索引擎快照直接抓成乱码天书

很多工程师以为字体反爬只是一层视觉映射。

常见的做法是写个脚本把正文里的一部分常用中文字符(比如“配置”、“服务器”、“数据库”),替换成私有 Unicode 编码区(PUA,\uE000 到 \uF8FF),同时动态生成一份把字形(Glyph)重新映射过的 .woff2 字体文件。

浏览器收到 HTML 后,通过 @font-face 加载这个自定义字体文件:

<style>
@font-face {
  font-family: 'AntiSpiderFont';
  src: url('/static/fonts/cipher-font-v2.woff2') format('woff2');
}
.secret-text {
  font-family: 'AntiSpiderFont';
}
</style>

<p>修改 Nginx 的 <span class="secret-text">&#xE3A1;&#xE3A2;</span> 文件,把超时时间调大。</p>

人类读者在屏幕上看着一切正常,“配置”两个字清清楚楚。

但搜索引擎蜘蛛(Baiduspider、Googlebot)是怎么读网页的?

爬虫提取文本根本不关心字形长什么样,爬虫只做一件事:从 HTML 字符流中提取 Unicode 码点,再送进分词器(Tokenizer)建立倒排索引(Inverted Index)。

当蜘蛛抓取上述页面时,它拿到的字符是:
修改 Nginx 的 \uE3A1\uE3A2 文件,把超时时间调大。

在 Unicode 规范里,\uE3A1 属于私有保留区,通用字典和分词模型里根本不存在这个词。百度的分词系统看到连续两个未知编码,直接当成无意义乱码过滤掉。更糟糕的是,如果一篇文章里核心的行业术语全被替换成了 PUA 编码,搜索引擎计算 TF-IDF(词频-逆文档频率)和 BM25 权重时,整篇页面的关键词相关性得分直接趋近于零。

你可以直接在终端里用一条命令模拟蜘蛛眼中的真实内容:

curl -s -A "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" \
  https://example.com/post/nginx-timeout | \
  grep -o 'class="secret-text">[^<]*'

终端打印出来的全是空心方块或 &#xE3A1; 这种字符实体。

即使 Googlebot 具备无头浏览器(Headless Chrome)渲染能力,它也不会花昂贵的 GPU 资源去对每一个渲染切片做 OCR 文字识别。爬虫依然读取 DOM 树中的文本节点(TextNode),只要文本节点是混淆编码,索引库里存的就是垃圾数据。

坑二:CSS 隐藏文本搞垃圾注入,直接触发搜索引擎反作弊算法

采集者通常会写 XPath 或 CSS 选择器去提取文章正文。于是有人自作聪明:在正文里插入大量带混淆标记的隐藏元素,故意混入干扰词汇或者假版权信息。

比如像这样写:

<p>
  为了防止超时,我们需要在
  <span style="display: none;">禁止转载自雷灵测试采集站,违者必究非法抓取</span>
  <span style="position: absolute; left: -9999px; top: -9999px;">大量无意义垃圾词填充测试</span>
  <span style="opacity: 0; font-size: 0px; color: transparent;">www.example.com</span>
  Nginx 配置文件中调整 keepalive_timeout 参数。
</p>

采集脚本如果没有处理样式继承,抓下来的文本就会变成一段语义不通的疯话。

但这套逻辑在搜索引擎反作弊算法面前,完全是撞在枪口上。

搜索引擎对抗“黑帽 SEO”已经二十多年了。早在上世纪九十年代,就有人用白色字体写在白色背景上,或者用 display:none 塞满几千个热门关键词来骗排名。因此,各大搜索引擎内部都有一套极其严厉的隐藏文本检测过滤器(Hidden Text Detection Filter)。

百度搜索在官方规范和反作弊算法中反复强调:任何以欺骗搜索引擎或向爬虫提供与人类可见不一致的内容,均属于作弊行为。Google 搜索指南更把 Hidden Text and Stuffing 列为严重违规,轻则单页降权,重则整站施加人工处置(Manual Action)。

当爬虫解析你的 CSS 时,以下几类规则会立刻标记风险:

  1. display: none 或 visibility: hidden 包含与正文关键词密集相关的内容。
  2. font-size: 0、line-height: 0 或者 color: transparent。
  3. position: absolute; left: -9999px 这种绝对定位移除视口区域的做法。
  4. 前景色与背景色对比度低于阈值(例如在 #FFFFFF 背景上写 #FEFEFE 颜色的字)。

爬虫把可见文本与源码文本一比对,直接判定该页面存在针对爬虫的对抗作弊,连带着站内原本正常的权重也被一并清算。

坑三:JS 动态解密与异步加载,把百度蜘蛛逼进“快照白屏”死胡同

还有一种程序员思维的典型反爬策略:既然前端源码能被抓,那把正文加密起来,页面加载后用 JS 动态解密不就行了?

很多架构师直接把正文打包成 JSON 或 Base64 字符串塞进页面底部,甚至用 WebAssembly 做解密执行:

<div id="article-container">
  <!-- 骨架屏或 Loading -->
  <div class="skeleton-placeholder">正在加载内容...</div>
</div>

<script>
document.addEventListener("DOMContentLoaded", function() {
  const cipher = "U2FsdGVkX1+vupppZ0ndG...[超长密文]";
  const key = "my-secret-salt";
  const plainText = CryptoJS.AES.decrypt(cipher, key).toString(CryptoJS.enc.Utf8);
  document.getElementById("article-container").innerHTML = plainText;
});
</script>

浏览器打开,JS 执行耗时 20 毫秒,肉眼根本察觉不到延迟。

但这在技术 SEO 领域是一个致命陷阱。

很多人有个误区:以为现在的搜索引擎都和浏览器一样能执行 JavaScript。真相远比想象残酷:

Googlebot 确实拥有基于最新 Chromium 内核的渲染引擎(Web Rendering Service, WRS),但它处理 JS 页面是分两阶段进行的。第一阶段只抓取初始 HTML 并建立快速索引;第二阶段把包含 JS 的页面扔进渲染队列(Rendering Queue),等待空闲计算资源进行二次渲染。这个排队延迟可能是几小时,也可能是几天甚至几周。如果你的页面每天更新,这种延迟直接让时效性归零。

至于国内最大的 Baiduspider,对 JavaScript 的动态渲染支持非常有限,且极其昂贵。百度爬虫每天抓取数十亿页面,绝大多数抓取请求走的依然是高并发的轻量级 HTML 解析通道。如果返回的原始 HTML 容器里只有一句“正在加载内容...”,百度蜘蛛在第一步就会把这个页面判定为软 404(Soft 404)或者低质无价值空白页!

在服务器上用无头浏览器测试一下 Baiduspider 的抓取实况:

curl -s -L -A "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" \
  https://example.com/post/docker-pitfalls | \
  grep -A 5 '<div id="article-container">'

输出结果:

<div id="article-container">
  <div class="skeleton-placeholder">正在加载内容...</div>
</div>

在百度搜索资源平台的“抓取诊断”工具里,抓取结果显示的快照正文长度只有几百字节,文本摘要全部缺失。连续抓取几篇新文章都是这个结果,蜘蛛就会认为整站都在产出无实质内容的空壳页面,随后果断削减站点的抓取配额(Crawl Budget)。

坑四:WAF 验证码拦截一刀切,误把真爬虫当黑客封在门外

为了阻挡自动化扫描和采集工具,有些运维喜欢在 Cloudflare、阿里云 WAF 或者宝塔面板里配置严格的防 CC 和反爬规则。

最常见的配置失误包括:

  1. 开启全局五秒盾(JS Challenge)或滑块验证。
  2. 只要 User-Agent 包含特定字符串或请求频率稍微偏高,就强制返回 403 Forbidden 或 302 重定向到验证码页面。
  3. 试图通过判断请求头中的 Cookie 来区分人机。因为爬虫通常不带 Session Cookie,一律丢给挑战页面。

问题是,无论是 Googlebot 还是 Baiduspider,它们都不会去解五秒盾的 JS 谜题,更不可能去拖动滑块验证码。

当爬虫拿到 403 状态码或 302 跳转到 /captcha 时:
如果返回的是 403,搜索引擎会记录该页面不可达。连续多日 403,已有索引会被彻底剔除;
如果返回的是 302 到验证码页面,搜索引擎会把验证码页面的标题和空内容当成新快照,覆盖原有的排名称谓,造成索引彻底错乱。

在 Nginx 日志中排查真假蜘蛛时,往往能看到令人心惊肉跳的统计数据:

# 统计爬虫被 WAF 或限流规则拦截的 403 记录
grep -i "Baiduspider" /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c

如果统计结果里 403 和 302 的比例超过 5%,说明站点的抓取通路已经千疮百孔。

正确解法:既能限制采集,又绝不伤害 SEO 的工程实践

既然前端层面的文本混淆、样式隐藏和 JS 加密全都会被搜索引擎反噬,那内容站到底该怎么做防采集?

核心原则只有一条:面向搜索引擎的公开索引内容,必须保证 HTML 原生语义的纯粹与完整;防御必须前置到网关层与行为层,通过网络鉴权与分发策略去解决。

1. 网关层做基于 rDNS 的真搜索引擎蜘蛛白名单

任何爬虫都可以伪造 User-Agent 为 Baiduspider 或 Googlebot。但伪造不了的是 IP 地址的双向反查(Reverse DNS, rDNS)。

正规搜索引擎的 IP 都配置了权威 PTR 记录。我们可以在 Nginx 网关层或者自建反代集群中,对声称是搜索引擎蜘蛛的请求做正反向二次验证,把真蜘蛛放进无限制白名单通道,对假冒蜘蛛施加严格的频率控制。

在 Linux 终端手动验证一个蜘蛛 IP 的真实性:

# 第一步:反查 IP 对应的 PTR 域名
host 220.181.108.188
# 输出:188.108.181.220.in-addr.arpa domain name pointer baiduspider-220-181-108-188.crawl.baidu.com.

# 第二步:拿该域名正向解析,验证 IP 是否对得上
host baiduspider-220-181-108-188.crawl.baidu.com
# 输出:baiduspider-220-181-108-188.crawl.baidu.com has address 220.181.108.188

确认两者双向匹配且域名后缀属于 *.baidu.com 或 *.googlebot.com,这才是真正的蜘蛛。

在 Nginx 中,如果觉得每个请求都走外部 DNS 查询会拖慢响应,可以维护官方网段列表(CIDR),配合 Nginx 的 geo 模块构建白名单体系:

# /etc/nginx/conf.d/spider_whitelist.conf

geo $is_search_engine {
    default 0;
    # 百度官方常用爬虫出口网段
    180.76.0.0/16       1;
    220.181.0.0/16      1;
    123.125.64.0/18     1;
    # 谷歌爬虫官方公布网段
    66.249.64.0/19      1;
    # Bing 爬虫网段
    40.77.167.0/24      1;
}

# 结合限流模块
limit_req_zone $binary_remote_addr zone=anti_crawl_limit:20m rate=10r/s;

server {
    listen 443 ssl http2;
    server_name example.com;

    location / {
        # 如果是真蜘蛛,绕过限流
        set $limit_rate_bypass $is_search_engine;

        # 普通用户与恶意采集器执行限流
        limit_req zone=anti_crawl_limit burst=20 nodelay;

        proxy_pass http://backend_upstream;
    }
}

真蜘蛛走绿色通道,畅通无阻抓取完整干净的 HTML;恶意采集脚本只要并发稍微提高,直接被 Nginx 返回 429 Too Many Requests,根本接触不到正文。

2. 内容层防御:把反爬重点放在低价值字段与非核心接口上

如果你的网站必须上字体反爬或动态解密(例如电商价格、电话号码、评分数据、敏感联系方式):

千万不要混淆文章的正文、标题、摘要和核心技术名词。

把保护范围严格收窄到非 SEO 导向的关键字段上。比如:
文章正文全部输出明文语义化 HTML;
只有付费阅读部分、用户个人手机号、敏感资源下载链接,才通过异步接口(API)或前端组件单独加载。
这样即使该接口被混淆或加密,搜索引擎也能无障碍索引到整篇数千字的高质量正文。

3. SEO 维权:用技术手段在搜索引擎内部建立“原创先发权”

很多采集站之所以能踩着原创站排到前面,根本原因是它的权重高、爬虫抓取频次高。你的新文章发出来 6 个小时后爬虫才来抓,而采集站 5 分钟就扒走并立刻被百度收录,在搜索引擎眼里反倒成了“原创”。

解决这个问题的关键不是把内容藏起来,而是以最快速度通知搜索引擎锁定原创时间戳:

第一,配置标准 JSON-LD 结构化数据:在 HTML <head> 中输出包含明确发布时间(ISO 8601 格式)、作者、版权归属的元数据:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "为了防爬虫采集给文章加了前端混淆,搜索收录却断崖暴跌?排查自定义字体乱码、CSS 隐藏文本被判作弊与 JS 解密快照白屏我踩过的几个坑",
  "datePublished": "2026-10-15T03:30:00+08:00",
  "dateModified": "2026-10-15T03:30:00+08:00",
  "author": {
    "@type": "Person",
    "name": "雷灵"
  },
  "publisher": {
    "@type": "Organization",
    "name": "雷灵博客",
    "url": "https://www.llbbs.cn"
  },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://www.llbbs.cn/anti-scraping-obfuscation-seo-penalty-pitfalls.html"
  }
}
</script>

第二,配合 Canonical 规范链接:即使采集站全盘克隆了你的 HTML,规范标签也会声明权威源地址:

<link rel="canonical" href="https://www.llbbs.cn/anti-scraping-obfuscation-seo-penalty-pitfalls.html" />

第三,文章发布流水线触发实时推送:利用百度搜索资源平台的 API 提交接口,以及 Bing 的 IndexNow 协议,在文章发布的毫秒级瞬间主动向搜索引擎推送新 URL。

import urllib.request
import json

def push_to_baidu_realtime(urls, token, domain="www.llbbs.cn"):
    api_url = f"http://data.zz.baidu.com/urls?site={domain}&token={token}"
    data = "\n".join(urls).encode("utf-8")
    req = urllib.request.Request(api_url, data=data, headers={"Content-Type": "text/plain"})
    with urllib.request.urlopen(req, timeout=10) as resp:
        return json.loads(resp.read().decode("utf-8"))

只要爬虫在你推过去的第一秒完成抓取入库,任何后续采集站抓到的版本都会在时间戳和内容哈希层面被自动标记为复制品。

4. 发布前的自动化自检脚本:验证蜘蛛端呈现

上线任何反爬或前端构建优化后,切忌只用普通浏览器看一眼没问题就发布。写个轻量脚本跑一遍“爬虫真实视角比对”:

#!/bin/bash
TARGET_URL="https://example.com/post/my-new-article"

echo "=== 检查爬虫视角 HTML 文本提取 ==="
SPIDER_UA="Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"

# 获取状态码与正文大小
HTTP_STATUS=$(curl -s -o /tmp/spider_page.html -w "%{http_code}" -A "$SPIDER_UA" "$TARGET_URL")

if [ "$HTTP_STATUS" != "200" ]; then
    echo "❌ 异常:爬虫访问返回状态码 $HTTP_STATUS"
    exit 1
fi

# 检查是否有私有字体映射乱码 (Unicode PUA 范围 \uE000-\uF8FF)
PUA_COUNT=$(grep -oP '&#x[EeFf][0-9a-fA-F]{3};' /tmp/spider_page.html | wc -l)
if [ "$PUA_COUNT" -gt 0 ]; then
    echo "⚠️ 警告:检测到 $PUA_COUNT 处私有区域字体编码实体,可能导致快照乱码!"
fi

# 检查是否包含危险的隐藏样式
HIDDEN_STYLES=$(grep -Ei 'display:\s*none|font-size:\s*0|left:\s*-[0-9]{4,}' /tmp/spider_page.html | wc -l)
if [ "$HIDDEN_STYLES" -gt 0 ]; then
    echo "⚠️ 警告:检测到 $HIDDEN_STYLES 处隐藏样式,注意排查是否混入正文导致作弊降权!"
fi

echo "✅ 基础检测完成,状态码: $HTTP_STATUS"

把这个检测脚本挂载到 CI/CD 发布流水线里,任何企图把爬虫当傻子的前端黑魔法,都会在发布前被拦截下来。

技术防护的终点是平衡。如果为了挡住几只小偷爬虫,把自家面向全世界的大门给反锁钉死,甚至把招牌涂花了,那才是真正得不偿失的事情。把明文交给搜索引擎,把铁壁筑在网关之上,这才是成熟系统该有的样子。

评论一下?

OωO
取消