做SEO量出74%的页面对爬虫和用户不一样,补上一组对照之后只剩2.2%

做SEO量出74%的页面对爬虫和用户不一样,补上一组对照之后只剩2.2%
张文保 30 分钟阅读 3,101 阅读
本文目录
  1. 你说的这个页面变了,有多大可能它一直在变?
  2. 为什么字节数一样,也不能说明内容一样?
  3. 三种在字节数上完全隐形的变化
  4. 抖动到底抖在哪一段字节上?
  5. 两个极端的样本
  6. 把一眼就是噪声的串剥掉之后,还剩多少?
  7. 连抓两次和连抓三次,结论会差多少?
  8. 一个安全用的随机数,能让抓取预算多花多少?
  9. 弱ETag为什么没能救场
  10. 那份爬虫版和用户版不一样的报告,有多少是这40.9%?
  11. 这组对照该怎么设计
  12. 怎么给自己的站量一条基线?
  13. 第一步:选URL,别只测首页
  14. 第二步:三次请求,而且要拉开间隔
  15. 第三步:算两枚指纹
  16. 第四步:不一致的时候,做行级差异,别看百分比
  17. 第五步:把这个数字写进你所有对比结论的前面
  18. 哪些抖动该修,哪些不必修
  19. 常见问题解答
  20. 页面抖动会直接影响排名吗?
  21. 40.9%这个数字能不能直接套到我的站上?
  22. 用无头浏览器渲染后再比对,是不是就能绕过这个问题?
  23. 为什么剥掉随机串之后还有34.3%在变?
  24. 只发两次请求真的会漏掉很多吗?
  25. 抖动和cloaking是一回事吗?
  26. ETag改成弱验证器能不能解决这个问题?
  27. 权威参考资料

摘要:我原本想量一件很简单的事:带不带Cookie,服务器给的页面会不会不一样。第一轮跑完,74.4%的站两次响应字节对不上,我差点就把这个数字写进结论。补了一组对照——同样的请求头、同样的会话、什么都不改,再发一次——才发现绝大部分差异跟Cookie毫无关系,是页面自己在抖。230个真实URL连抓三遍,40.9%三次指纹不全同;这些抖动里56.4%三次字节数分毫不差,靠Content-Length根本看不出来;剥掉nonce、请求ID、时间戳这类一眼就是噪声的串之后,仍有34.3%在变。而真正由Cookie造成的分岔,在稳定样本上只有2.2%。这篇讲怎么先把噪声量出来,再谈差异。

做技术SEO的人手里都有那么几件对比工具:抓一份Googlebot看到的HTML,再抓一份浏览器看到的,摆在一起看差多少;或者今天存一份快照,明天再存一份,看页面有没有被人偷偷改过。这类工具的输出通常很干脆——两版不一致,扣分,红灯。

问题是,这套逻辑默认了一件从来没人验证过的事:同一个地址,在什么都不变的情况下,会稳定地返回同一份内容。

这篇文章要做的事就一件:把这个默认前提拿去实测。230个真实站点URL,每个连抓三遍,同一套请求头、同一个出口、间隔不到一秒,然后看三份字节对不对得上。

你说的这个页面变了,有多大可能它一直在变?

先交代口径。样本来自124个首页加106个深层页,共230个URL,全部在三次基线请求里都返回200。请求头完全固定:同一个Chrome桌面UA、同一个Accept-Language: en-US,en;q=0.9、同一个出口IP,三次之间不做任何改动。指纹用响应体原始字节的SHA-1,不做任何清洗。

结果是这样的:

样本URL数三次指纹不全同占比
全部2309440.9%
首页1245544.4%
深层页1063936.8%

换句话说,三次完全一致的URL只有136个,占59.1%。剩下那四成,你在什么都没改的情况下,就已经拿到了两份或三份不同的HTML。

首页比深层页更爱抖,这个方向符合直觉:首页上挂的推荐位、榜单、促销条、埋点脚本最多。但36.8%这个深层页的数字仍然不低——那些通常是文章页和商品页,也就是大家平时最认真做SEO的那一批。

这个40.9%意味着什么?意味着任何一份“两版不一致”的报告,都自带一个四成的假阳性地板。你的对比工具抓两份HTML发现它们不同,第一嫌疑人不是Googlebot被区别对待了,而是这个页面本来就不会给你两份一样的东西。

为什么这么基础的一个前提,很少有人真的去验证?我猜有两个原因。

一个是这件事在十几年前确实成立。那时候一个PHP页面渲染出来就是那一份HTML,同一个模板同一批数据,字节级稳定是常态,抖动才是异常。这套直觉一直沿用到今天,可今天的页面上挂着三四个埋点脚本、两套实验框架、一个推荐引擎,中间还隔着CDN——稳定从常态变成了少数派,直觉却没跟着更新。

另一个原因更朴素:验证这件事需要多发一次请求,而多发的那一次不产出任何新信息。你抓两份就能出一张对比图,抓三份只能得到一句“这个页面本身就在抖”,看起来像白干。做数据的人都懂对照组的价值,可轮到自己写巡检脚本的时候,那一步几乎总是第一个被省掉的。

为什么字节数一样,也不能说明内容一样?

很多自建的巡检脚本图省事,用Content-Length或者curl -w '%{size_download}'来判断“页面有没有变”。这个办法有一个非常大的漏洞。

在那94个抖动的URL里,53个三次的字节数完全相同,占56.4%。它们的SHA-1三次全不一样,字节数却一个数都没差。

为什么会这样?我在实验台上造了一个最小的例子。一台干净的nginx后面挂一个PHP页面,页面里输出六个列表项,唯一的处理是每次响应前把顺序打乱:

指纹字节列表顺序
3693324aec282foxtrot alpha delta bravo echo charlie
aa67125512282foxtrot bravo alpha echo delta charlie
0be277628d282charlie foxtrot delta bravo alpha echo
2feee55ee9282bravo delta alpha echo charlie foxtrot
e5f9e616c0282charlie delta alpha foxtrot bravo echo
43d62c19f4282charlie delta foxtrot bravo alpha echo
bf22c0b7da282alpha echo charlie delta bravo foxtrot
077ccbb24c282echo alpha foxtrot charlie bravo delta

八次请求,八个不同的指纹,八个一模一样的282。凡是“重排”而不是“增删”的变化,在字节数这个维度上是完全隐形的。

现实里这类变化多得很:商品列表按个性化权重重排、推荐位轮播、相关文章随机取N条、多个CDN域名轮询、A/B分桶决定组件顺序。它们改变的是“哪个在前面”,不改变“一共有多少”。

顺带说个数:这94个抖动URL里,三次字节差的中位数是0,p90是536字节,最大的一个是zapier.com首页的42604字节。中位数为0这件事本身就是结论——抖动的典型形态不是页面重写了一半,而是几个字符悄悄换了个位置。

三种在字节数上完全隐形的变化

把这类变化分个类,排查的时候能快很多。

第一种是重排。上面那个实验就是。列表顺序、推荐位顺序、CDN域名轮询、多个脚本标签的先后,全都属于这一类。它们的共同点是元素集合没变,只是走了个位置。

第二种是等长替换。一个32位的请求ID换成另一个32位的请求ID、一个UUID换成另一个UUID、一个毫秒时间戳换成下一个毫秒时间戳——长度天然固定,内容每次都新。这类是所有抖动里最普遍的。

第三种最阴,是等长的内容替换。比如价格从¥129变成¥139、库存从12件变成13件、评分从4.6变成4.7。这些是真实的内容变化,会影响结构化数据,也会影响搜索结果里展示的富媒体信息,可它们在字节数上一动不动。

第三种正是为什么“只看size就行了”这个想法特别危险:它对噪声不敏感,同时对真正该报警的那类变化也不敏感。一个既报不出假警、也报不出真警的指标,其实就是没有指标。

抖动到底抖在哪一段字节上?

知道“在抖”还不够,得知道“抖在哪”。我把那94个URL全部重抓两份完整HTML,逐行做差异比对,再给每一段差异归类。

第一个意外的发现:重抓时只有86个还在抖,另外8个这次三次一致了。也就是说抖动本身也是间歇的——一个页面今天没抖,不等于它稳定,只等于这次没赶上。

在这86个里:

  • 变化行数中位数4行,p90是20行,最多98行;
  • 变化行占全文行数的比例,中位数0.743%,p90是25.2%;
  • 变化块数中位2块
  • 第一处差异落在</head>之前的,占55.8%。

最后那条最要命。超过一半的抖动,第一处就发生在head里——而head正是title、meta description、canonical、hreflang、结构化数据的地盘。你的差异报告有一半概率会把手指头点在SEO最敏感的那一段上。

把差异段落做关键词归类之后,成因分布是这样:

抖动来源命中URL数占比
广告与追踪脚本5159.3%
实验与A/B分桶4653.5%
推荐与排序4350.0%
时间戳与日期3743.0%
库存、价格与计数3136.0%
构建ID与资源版本号2529.1%
会话ID1922.1%
CSRF令牌1922.1%
请求ID与链路追踪1922.1%
CSP的nonce1315.1%
一类都没命中1315.1%

一个URL可以同时命中好几类,所以合计超过100%。这张表里最值得盯的是最后一行:有15.1%的抖动,用这十条规则一条都识别不出来。那些是页面自己业务逻辑里的东西——库存数字、评论条数、“3人正在看”这类实时片段,没有任何通用特征可以把它们摘出去。

几条真实的差异行,能让人对这件事有个具体的感觉:

zfc.push(['initialize', '1785433803088', (new Date).getTime(), ...(zappos.com,两次请求相差不到一秒,这个毫秒级时间戳就换了一个数)

'__TGT_DATA__': { configurable: false, enumerable: true, value: deepFreeze(JSON.parse("{ \"formFactor\": \"desktop\",\"assetPrefix...(target.com,整个页面状态被序列化进一个巨大的JSON,里面任何一个字段变了,这一行就变了)

最干净的几个也值得记一下:salesforce.com和mailchimp.com每次只变2行、占比0.019%,moz.com只变2行且十条规则一条都不命中。它们不是不抖,只是抖得极小——小到任何一个按“相似度百分比”打分的工具都会判它们通过。

两个极端的样本

抖动幅度最大的是zapier.com首页,三次分别是577728、535124、535124字节,头一次比后两次多出42604字节。这个量级不是埋点脚本能解释的,更像是首屏内容整块换了一版——可能是实验分桶,也可能是缓存里恰好躺着一份不同的副本。

另一头是paypal.com的深层页,变化行数92行,占全文的27.9%,同时命中了nonce、CSRF令牌、请求ID、会话ID、时间戳五类。金融类站点在安全上下的功夫,全都以抖动的形式写进了HTML。这没什么可指摘的,只是意味着对这类站做内容对比,噪声地板高得离谱。

把这两个极端和中位数的0字节差放在一起看,能得出一个对排查有用的判断:抖动的分布是长尾的,绝大多数是几行的小变动,极少数是整块内容的替换。所以看“变化行数”这个绝对量,比看“相似度百分比”这个相对量更有信息量——后者在一个几千行的页面上,永远是99.9%。

把一眼就是噪声的串剥掉之后,还剩多少?

到这里很自然会想到一个办法:既然大部分抖动是nonce、请求ID、时间戳这种明显的随机串,那就在算指纹之前先把它们统一替换掉,剩下的差异才算数。

这个思路是对的,我也这么做了。做法是在算第二枚指纹之前,用一组正则把UUID、32到64位的十六进制串、10到13位的纯数字、nonce="..."、各类token赋值、ISO 8601时间串,全部替换成固定的占位符,然后对处理后的文本再算一次SHA-1。于是每个响应有两枚指纹:原始指纹归一化指纹

结果比我预期的糟:

口径三次不全同占比
原始字节指纹9440.9%
归一化指纹(剥掉高熵串)7934.3%

也就是说,40.9个百分点的抖动里,只有6.5个百分点是纯token噪声,剩下34.3个百分点在剥掉所有随机串之后依然在变。

这个结果推翻了我动手前的假设。我原本以为抖动主要是安全和链路追踪那套东西造成的,清洗一遍就该干净了。实际上不是——大头是内容本身在变:推荐位换了一批、榜单重排了、库存数字动了、A/B分桶给了另一个变体。这些东西你没法用正则摘掉,因为它们长得就跟正常内容一样。

保哥的看法是,这条其实是好消息里裹着坏消息。好消息是“清洗一下就能对比”这个念头可以早点放弃,省得在正则上耗一周;坏消息是只要页面上有一块内容是活的,这个页面就没有一个稳定的“正确版本”可以拿来当基准。

连抓两次和连抓三次,结论会差多少?

几乎所有对比脚本都是抓两份。我这次特意抓了三份,中间还留了一点间隔,就是想看看第三份能多告诉我什么。

第一个数字:头两次指纹一样、第三次才变的URL有5个,占2.2%。比例不高,但方向很明确——抓两次判“稳定”,会有一小撮漏网。

第二个数字更值得注意,就是上一节说的8.5%重抓时不抖了。合起来看,这两个数字说的是同一件事:“这个页面稳不稳定”本身就是一个概率问题,不是一个是非题。你测一次得到的是一个样本,不是一个结论。

第三个数字来自实验台,是我做这批实验时最喜欢的一个小机关。我造了一个只注入时间戳的页面,然后连着请求:

时机指纹是否与第一次相同
第一次6d181df64ebb
紧接着第二次6d181df64ebb相同
隔2秒第三次75295c2096bc不同

因为这个页面注入的是秒级时间戳,在同一秒里连发两次,它看起来完美稳定。而现实里43.0%的抖动跟时间戳有关,这意味着你的采样间隔本身就是一个变量:抓得越快,页面看起来越稳。

这大概是整件事里最容易让人栽跟头的地方。为了“控制变量”,大家往往把两次请求发得越紧越好,结果恰恰是把一整类抖动屏蔽掉了。想抓时间戳类抖动,你得故意等一会儿。

一个安全用的随机数,能让抓取预算多花多少?

前面讲的都是“你怎么被误导”,这一节讲抖动实打实的代价,而且这笔代价直接记在SEO账上。

Google的爬虫支持条件请求。它会把上次抓取拿到的ETag放进If-None-Match发过来,服务端如果发现内容没变,回一个304、零字节的响应体就完事了。这套机制能省下的是真金白银的抓取预算,具体机制我在条件请求与304怎么省抓取预算里拆过一遍。

现在问题来了:如果页面每次响应的字节都不一样,应用层算出来的强ETag也就每次都不一样,条件请求会发生什么?

我在实验台上做了个标准的框架式实现——对最终HTML算一次MD5当ETag,收到If-None-Match就比对,一致返304。然后拿同一个页面的几个变体各跑100次条件请求:

页面里注入的东西100次条件请求的304命中累计回传字节
什么都不注入100 / 1000
一个CSP用的nonce0 / 10026200
列表顺序随机0 / 10021200

一个12字节的随机数,把条件请求的收益从100%清零到0%。不是打了折,是整个机制彻底失效。

让这件事变得棘手的是:那个nonce并不是谁写错了,它是按规范做对了的结果。MDN在讲CSP的script-src时写得很直白:

"It is important to note, this nonce value needs to be dynamically generated as it has to be unique for each HTTP request"

规范要求每次请求都换一个新的nonce,否则这道防线就没意义了。于是你面对的是一个真实的取舍:要么这个页面的内联脚本有CSP保护,要么这个页面能享受条件请求的抓取预算折扣,两个不能同时要——除非你把ETag的算法改成只对“内容部分”取哈希,而不是对整份HTML。

现网的情况怎么样?在230个URL里:

  • ETag响应头的81个,占35.2%;带Last-Modified的66个,占28.7%;
  • 带ETag的那批里,弱ETag(W/开头)占46.9%
  • 三次拿到的ETag值本身就各不相同的有15个,占带ETag的18.5%——这些页面上的条件请求,必然一次都命中不了。

把这三个数连起来读:三分之一的页面根本没给爬虫任何验证器;给了的里面又有接近五分之一,给的是一个每次都变的值。后者比前者更浪费——不发ETag只是没省下,发一个每次都变的ETag,是让爬虫每次多发一个请求头、还是全量重传。

弱ETag为什么没能救场

看到46.9%是弱ETag的时候,我一度以为这是个好消息。弱验证器(W/前缀)在规范里表达的是“语义等价”而不是“字节完全相同”,理论上它就是为这种场景设计的:埋点时间戳变了,但这个页面对读者来说还是同一个页面,那就该返回同一个弱ETag。

实际上没有。这批带弱ETag的页面里,仍有相当比例三次拿到的值各不相同。原因不难猜——绝大多数框架和中间件的“弱ETag”实现,只是在原来的哈希前面加了两个字符W/,哈希本身照旧对整份响应体算。前缀改了,语义没改。

要真正用好弱验证器,应用得自己回答一个问题:“这次响应和上次比,算不算实质变化?”这个判断没法通用化,只能由业务来做:把内容主体单独取哈希、把埋点和令牌排除在外,或者干脆用内容表里的modified字段生成验证器。后一条路最省事,也顺带解决了更新时间戳该怎么诚实地维护那个老问题。

那份爬虫版和用户版不一样的报告,有多少是这40.9%?

现在回到开头那件事。我一开始想量的是Cookie:站点在首访就下发Cookie,爬虫从不回传,那么“带Cookie”和“不带Cookie”拿到的页面差多少?

第一轮我用最直接的办法测:建一个会话,请求一次,把服务器下发的Cookie收下,隔一秒用同一个会话再请求一次,比对两次的字节。40个站里39个可用,29个两次响应不同,占74.4%。

这个数字很漂亮,也很有故事性——“四分之三的站给爬虫和用户的不是同一个页面”。我当时确实准备把它写进结论。

补对照组花了不到二十分钟:同样的URL,再开两个全新的、不带任何Cookie的会话各请求一次。如果这两次之间也不同,那第一轮那个74.4%就跟Cookie没关系。

对照组的结果是68.2%

换句话说,我原以为测到的是Cookie效应,实际上测到的绝大部分是页面自己在抖。等到用230个URL重做一遍、并且只在三次基线完全一致的136个稳定URL上计算之后,真实的数字是这样:

改变的东西稳定样本数响应不同占比
第二次请求带上服务器下发的Cookie13632.2%
Accept-Language换成zh-CN1361511.0%
Accept-Language换成de-DE1361410.3%
完全不发Accept-Language13675.1%
换成Googlebot的User-Agent1241411.3%

74.4%和2.2%,差了33倍。中间隔的就是一组花二十分钟补上的对照。

这组对照该怎么设计

把这次的教训抽象一下,对照组的设计其实只有一条规则:让对照组和实验组之间,只差你想测的那一个变量。听起来像废话,可具体到HTTP请求上,“只差一个变量”比想象中难做到。

我第一轮的实验组是“同一个会话的第二次请求”,对照组本该是“另一个新会话的第一次请求”。但这两者之间差的不只是Cookie——还差了时间(第二次晚了一秒)、差了连接复用状态、差了服务端可能已经把这个页面预热进缓存。任何一个都足以造成字节差异。

所以正确的做法是把对照组也做成一对:两个全新会话、都不带Cookie、间隔和实验组一样。这一对之间的差异率就是噪声地板,实验组的差异率减去它,才是Cookie的净效应。这跟SEO实验设计里的单因素隔离是同一套东西,只不过大家做A/B测试时都记得,做技术巡检时都忘了。

还有一个更省事的版本,也是我最后采用的:先把所有基线不稳定的URL整个剔除,只在稳定样本上做实验。这样噪声地板直接归零,代价是样本量从230掉到136。样本够的时候这个办法最干净。

这件事对市面上的对比类工具意味着什么,我觉得得说清楚,因为它不是“工具不行”这么简单。渲染对比器这类工具做的事——抓Googlebot版和浏览器版,逐维度比对,按差异扣分——方法本身没有问题,它查出来的cloaking也确实是真的。问题出在它默认每一维只需要各取一个样本。在一个40.9%的页面会自己跟自己对不上的世界里,单样本对比的结论,误差比信号大。

同样的道理也适用于任何一个“快照告警”系统:SEO监控告警体系里最常见的一条规则就是“页面内容变化超过X%就报警”,而这个X如果是拍脑袋定的,你要么天天收到广告脚本换了个时间戳的告警,要么把阈值调到高得连真事故都漏掉。这跟慢查询日志里783MB只有32条是真问题是同一类毛病:不先量噪声,就定不出阈值。

顺便说一句,这个坑不只在HTTP层。AI可见度排名同一个问题查多次名次都不一样关键词排名天天上下跳,讲的都是同一件事的不同层:先确认你要测的量有没有一个稳定的值,再谈它变了多少。

怎么给自己的站量一条基线?

方法不复杂,成本也低。保哥把它压成了五步,跑一次十分钟,结论能用很久。

第一步:选URL,别只测首页

至少三类各取几个:首页、一个典型的列表或分类页、一个典型的详情页(文章或商品)。我这批数据里首页44.4%、深层页36.8%,两者差了将近8个百分点,只测首页会高估自己的问题。

第二步:三次请求,而且要拉开间隔

同一套请求头连发两次,再隔至少3到5秒发第三次。前两次抓的是“每次都变”的那类(nonce、请求ID),第三次抓的是“每秒都变”的那类(时间戳、缓存刷新)。只发两次、且发得很紧,会同时漏掉后一类。

第三步:算两枚指纹

一枚是原始字节的SHA-1,一枚是清洗后的。清洗的正则不用写得多讲究,覆盖这几类就够了:UUID格式、连续32位以上的十六进制、10到13位纯数字、nonce=后面的引号串、token/csrf/requestId这类赋值、ISO 8601时间串。哈希指纹的算法选择在这里不重要,SHA-1够用,你只是在比对,不是在防篡改。

第四步:不一致的时候,做行级差异,别看百分比

两枚指纹只告诉你“变没变”,逐行逐词的差异比对才告诉你“变在哪”。重点看第一处差异落在</head>之前还是之后——落在之前,说明抖动碰到了SEO标签所在的区域,值得深挖;落在之后而且只有两三行,大概率是个埋点脚本。

第五步:把这个数字写进你所有对比结论的前面

这一步没有技术含量,但它是全部五步里最有价值的一步。以后任何一份差异报告,抬头先写一句“本站基线抖动率X%,本次差异Y%”。Y要显著大于X,这份报告才配叫报告。

我拿自己的站跑了一遍这套流程,结果是这样:

URL三次指纹是否一致ETagSet-CookieVary
首页b9711033b7b9 ×3一致Accept-Encoding
术语表页71a2f1a07840 ×3一致Accept-Encoding
工具站页ac92ffddc219 ×3一致Accept-Encoding

三个页面各三次全部一致,换Accept-Language为zh-CN、de-DE,以及换成Googlebot的UA,拿到的仍然是同一枚指纹。这不是我优化出来的,是这个站根本没有广告脚本、没有推荐位、没有A/B分桶的自然结果。一个不做个性化的站在这件事上是天然干净的,代价是它也没有那些东西带来的收益——这笔账每个站自己算。

顺带一提,这三个页面都没有ETag。按上一节的账,这是个可以捡的便宜:内容稳定却不发验证器,等于把条件请求这项白送的优惠券扔了。这条已经进了保哥的待办。

哪些抖动该修,哪些不必修

把抖动全部消灭既不可能也不划算。真正要判断的是:这段抖动有没有落在会被搜索引擎当成“内容”的字节上。按这个标准分三档:

档位典型来源处理建议
该修head里的title、description、canonical、hreflang、结构化数据随请求变化必须查。这类抖动会让规范网址判定和富媒体结果不稳定,属于真事故
该修正文主体内容按分桶给出不同版本,且没有任何标注爬虫与用户看到的页面对比那套口径自查,接近cloaking的红线
值得改ETag按整份HTML算,被nonce、请求ID带得每次都变改成只对内容部分取哈希,或者干脆改用Last-Modified,把条件请求的收益捡回来
值得改推荐位、相关文章每次刷新都换一批加个短TTL让它在几分钟内稳定,对用户体验没损失,对缓存和验证器友好得多
不必修CSP的nonce规范要求它每次都变。别为了指纹稳定去牺牲安全,去改ETag的算法
不必修埋点脚本里的毫秒时间戳、请求ID它们不进索引,也不影响内容判定,记住它们的存在、在对比时剥掉就行
不必修库存数字、评论条数、“N人正在看”这就是内容本身在变,是对的。只要别让它出现在title里

最后回到这篇文章的起点。做技术SEO这些年,保哥见过太多的排查是从一份“两版不一致”的截图开始的,然后一路查到CDN、查到WAF、查到UA判别,最后发现两版差的是一个埋点脚本里的时间戳。同一个页面有11种URL写法都返回200那篇里我数过一个页面有几个地址,这篇数的是同一个地址下有几份内容。两个问题的答案都是同一句话:你以为的“一个”,实际上从来都是“一批”。

差别只在于,地址那件事你至少能一条条列出来,内容这件事你连“正确版本”是哪一份都指不出来。所以顺序只能是先量噪声、再谈差异——反过来做,你得到的每一个结论都建立在一次运气上。

常见问题解答

页面抖动会直接影响排名吗?

抖动本身不是排名因素,Google没有任何一条规则是针对“响应字节不稳定”的。它的影响是间接的,走两条路:一是让强ETag每次都变,条件请求全部落空,白白多花抓取预算;二是如果抖动落在head里,title、canonical、结构化数据在不同抓取批次拿到不同的值,规范网址判定和富媒体结果就会不稳定。正文里几个数字或者推荐位换一批,基本没有影响。

40.9%这个数字能不能直接套到我的站上?

不能,这是一个参考量级不是一个基准值。样本是230个电商、SaaS和开发者站的URL,这类站上广告脚本、A/B分桶、个性化推荐特别密集。一个纯内容站的抖动率通常低得多——我自己这个站三类页面各测三次全部一致。这个数字的用法是提醒你自己去测一遍,而不是拿来当自己的数。

用无头浏览器渲染后再比对,是不是就能绕过这个问题?

不能,反而更糟。渲染会把JavaScript执行的结果也算进来,广告位、推荐接口、实验分桶的返回全都进入DOM,抖动幅度只会更大。渲染后对比适合查“内容有没有被JS填进来”,不适合查“两版一不一样”。要查一致性,用原始HTML加两级指纹更可控。

为什么剥掉随机串之后还有34.3%在变?

因为大头不是随机串,是内容本身。推荐位换了一批商品、榜单重新排了序、库存数字动了、A/B实验给了另一个变体,这些东西长得跟正常内容一模一样,任何正则都摘不出来。这也是为什么“清洗一遍再对比”这条路走不通——真正在变的那部分,恰恰是你想拿来对比的那部分。

只发两次请求真的会漏掉很多吗?

漏掉的比例不算大但方向很明确。头两次一样、第三次才不同的占2.2%;另有8.5%的URL重抓时干脆不抖了。更关键的是间隔:秒级时间戳类的抖动在同一秒内连发两次完全看不出来,而这类抖动占了43.0%。所以真正的建议不是“多发一次”,是“第三次要故意等几秒”。

抖动和cloaking是一回事吗?

不是,而且必须分清楚。cloaking是有意针对爬虫身份返回不同内容,是违反Google垃圾内容政策的作弊行为。抖动是页面对所有人都不稳定,跟你是谁没关系。判别方法很简单:先测三次基线,如果同一个身份连测三次就已经不一致,那么“爬虫版和用户版不同”这个观察不构成任何证据;只有在基线稳定的页面上,身份造成的差异才是真的。

ETag改成弱验证器能不能解决这个问题?

能解决一部分。弱ETag(W/开头)表达的是“语义等价”而不是“字节相同”,理论上允许你在只有埋点时间戳变了的情况下仍然返回同一个值。但这要求应用自己有能力判断“这次的变化算不算实质变化”,多数框架的实现只是简单加个W/前缀、哈希照旧算全文,那就没有任何区别。实测230个URL里带ETag的有46.9%是弱ETag,但其中仍有相当比例三次值各不相同。

权威参考资料

分享到
标签
版权声明

本文标题:《做SEO量出74%的页面对爬虫和用户不一样,补上一组对照之后只剩2.2%》

本文链接:https://zhangwenbao.com/page-content-jitter-baseline-seo-diff-false-positive.html

版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交