站内搜索页SEO:搜不到商品那一页,一半站允许被收录

站内搜索页SEO:搜不到商品那一页,一半站允许被收录
张文保 更新 21 分钟阅读 3,345 阅读
本文目录
  1. 搜一个世界上不存在的词,站点会回什么?
  2. 怎么确定它是真的在搜,而不是随便给了我一页?
  3. 那40个站,搜不到东西的时候回了什么状态码?
  4. 搜到和搜不到,爬虫能看出区别吗?
  5. 这一页准备好被搜索引擎收录了吗?
  6. 这三样闸门,到底是谁配上去的?
  7. 一个不设防的搜索页,最后会长成什么样?
  8. 该怎么办,先动哪一样?
  9. 什么情况下反而不该屏蔽搜索页?
  10. 常见问题解答
  11. 搜索结果为空,应该返回404吗?
  12. 用robots.txt禁掉搜索路径就够了吗?
  13. 怎么知道我的站有没有这个问题?
  14. 搜索结果靠JavaScript渲染,是不是就没这个问题了?
  15. canonical指回带参数的地址自己,有多严重?
  16. 为什么有29个站被剔出了统计?
  17. 我用的是Shopify,是不是就不用管了?
  18. 权威参考资料
摘要:拿一个不存在于任何语言的乱码词去搜131个英文大站,剔掉根本没在搜的那批之后,40个站能判定。结果是40个全部返回200,一个404都没有。真正分岔的地方在后面:只有42.5%的页面带noindex,9个站的canonical直接指回这个带乱码参数的地址本身,robots.txt里禁了自己搜索路径的占41.7%。把这三样按平台一分组,答案就浮出来了——Shopify那批robots.txt中位104行、有7条指令覆盖六成以上的站,而认不出平台的那24个站中位只有26行、最高一条指令的覆盖率是12.5%。这一页允不允许被收录,多数站主根本没做过这个决定。

这件事是被一个GSC截图勾起来的。一个卖户外家具的独立站,被收录的页面数比他们的商品数多出四千多个,点进覆盖率报告一看,一长串地址长得都差不多,全是/search?q=开头。

站长的第一反应是被人刷了。但翻日志会发现更普通的解释:那些地址是站内自己的搜索建议、外链、还有以前某个促销活动的落地页留下来的,爬虫顺着链接一路爬进去,站点每次都老老实实回了一个200。

我想知道这是个案还是常态,于是编了个词去问131个站。

搜一个世界上不存在的词,站点会回什么?

用的词是zqxjkvbnmqp7,随手敲的,确认过任何一个站都不可能有这件商品。为了排除偶然,我还准备了第二个乱码词wfrhtygdlpz3,以及一个几乎必然搜得到的词black做对照。

131个站里,首页能正常访问的70个。这70个里,能找到搜索路径并拿到回答的69个。绝大多数站的搜索地址是/search?q=,只有一个用的是WordPress那套/?s=

探测这一步本身也有点信息量。我按五种常见形态挨个试过去:/search?q=拿到49次200和17次404,/?s=拿到20次200,还有几次撞在限流上。也就是说有相当一批站压根没有/search这条路径,请求要么被路由到别处,要么直接404。写这类批量脚本时不能一刀切认定路径,得先探再问,否则统计出来的全是噪声。

然后是这次最要紧的一步过滤,它决定了后面所有数字算不算数。

怎么确定它是真的在搜,而不是随便给了我一页?

判据只有一条:换一个必然搜得到的词,它的回答必须跟着变。如果搜black和搜一串乱码拿到的HTML一模一样、连标题都跟首页相同,那这个站压根没在搜,只是把某个固定页面塞给了我。

按这条判据把69个站分成三类:

  • 回落到固定页面:29个。搜什么都给同一页,标题跟首页一字不差。这批站被整体剔出分母。
  • 搜索页但结果靠JavaScript填:22个。标题会变,但HTML里搜到和搜不到几乎没差别。
  • 服务端把结果写进HTML:18个。换个词,字节跟着变。

后两类合起来40个站,是这篇里所有比例的分母。

被剔掉的那29个站不是就没事了,恰恰相反,它们的形态更粗糙:一个能填任意字符串的地址,填什么都返回同一份HTML,连标题都跟首页一模一样。这等于把重复内容的产能直接开到最大,只是这批地址通常没什么链接指向,爬虫发现得慢一点而已。想知道自己是不是这一类,办法就是上面那条判据——拿一个必然有的词和一个必然没有的词各请求一次,比一比两次响应的字节数。

自基线跑了三条,结果比我预期的干净。同一个乱码词连着请求两次,40个站状态码全部一致,字节相对差的中位数是0.0000、最大0.002。换成第二个乱码词,状态码仍然40个全部一致。把搜索词本身抹掉之后比对标题模板,40个站的两次结果一模一样——说明这一页确实是按模板生成的,不是碰运气。

这套过滤器是从上一次做错误页那批实测里搬过来的思路:判断一个东西开着之前,得先证明这个站会说“没有”。这次的版本更严一点,因为搜索页天生就会返回200,光看状态码什么都判断不出来。

那40个站,搜不到东西的时候回了什么状态码?

全部是200。没有一个站返回404,也没有一个返回410或者其它任何非200。

这件事本身不算错。搜索结果为空跟地址不存在是两回事,语义上200是对的——这个地址确实存在,只是这次查询没匹配到东西。MDN对404的定义说得很清楚,它描述的是资源不存在,不是结果为空。

问题在于,一个稳定返回200的地址模板,配上一个可以填任意字符串的参数,等于给了外界一个无限生成可索引页面的入口。这一页能不能被索引,就成了唯一的闸门。

顺带记一下这批页面的体量:解压后中位数409KB、压缩后69.8KB。超过512KB的16个站,超过1MB的8个站,最大的两个是misen.com的5.55MB和burrow.com的4.45MB。页面上的链接数中位97条,最多的一个站挂了1694条;脚本数中位29个,最多137个。响应耗时中位1550毫秒,最慢的一个跑了8503毫秒。

这些数字跟样式表那篇算的是同一类账:一个什么内容都没产出的页面,仍然要把整套站点外壳原样端一遍——导航、页脚、几十个第三方脚本、上百条链接,一个不少。真要看单份响应有多大是不是踩了抓取上限,抓取体积那套工具比肉眼估更准。

搜到和搜不到,爬虫能看出区别吗?

这是我没想到的一格。把每个站“搜得到的词”和“搜不到的词”的HTML体积摆在一起比,字节比的中位数是1.00——两次响应几乎一样大。40个站里只有6个的空结果页比有结果页更大。

原因就是前面分类里那22个站:结果由前端脚本填进去,服务端吐的HTML是同一副骨架。这意味着不执行JavaScript的那一遍抓取,拿到的确实是同一份文件,无论参数里是什么。框架站的渲染模式会直接决定这件事,而空壳页面那篇量过同一个现象在首页上的表现。

回显情况也值得看。40个站里有24个把搜索词原样写进了正文,占60%;写进<title>的只有7个,占17.5%。写进标题的那批风险更实一点,因为标题是搜索结果里最显眼的一行。

标题模板的重合度也说明问题。把搜索词抹掉之后,40个站一共只有29种不同的标题,其中4种出现在两个以上的站上。最集中的一组有7个站,标题就是干巴巴一个单词Search——allbirds、arcteryx、article、buckmason、quince、swarovski、theordinary。另有4个站压根没输出标题:aliexpress、kotn、on.com、temu。剩下两组各两个站,分别是小写的searchSearch Results

这些公司彼此没有关系,做的品类也不挨着,标题却一字不差。原因不复杂:他们用的是同一套主题模板,而模板作者在search.liquid里写了什么,商家多半没去看过。一个只写着Search的标题如果进了索引,在搜索结果里跟别人的完全一样,谁也认不出是哪家。

再往下拆一层,这40个站按平台分是Shopify 17个、认不出平台11个、Next.js 6个、Salesforce 6个。Shopify占了将近一半,这批站的行为高度一致,也正是下一节能拆出归属的原因。

这一页准备好被搜索引擎收录了吗?

三样东西可以拦住它:页面上的noindex、canonical指向哪里、robots.txt里有没有那一行。逐个数。

闸门做了的站占比
页面带noindex(meta或响应头)17/4042.5%
canonical指向不带词的搜索页20/4050.0%
canonical指回这个带词地址自己9/4022.5%
完全没有canonical6/4015.0%
robots.txt禁了自己这条搜索路径15/3641.7%

只有四成出头的页面明确说了别收我。noindex这17个里,14个写在meta标签上,7个走的是响应头X-Robots-Tag,有几个两样都写了。这两种写法的适用场景在机器可读端点那篇里分过,对HTML页面来说两者等效,写哪个都行。

把noindex这一栏也按平台拆开,顺序跟后面robots.txt那张表几乎是反的:Salesforce是5/6,比例最高;Shopify 8/17;Next.js 2/6;认不出平台的只有2/11。Salesforce那批robots.txt写得最长、却最少去禁搜索路径,反倒在页面上把noindex补齐了;Shopify正好掉个个儿。两条路都能走通,但各自漏掉的场景不一样:robots禁抓挡不住已收录页面的清理,页面上的noindex挡不住抓取预算被消耗。这笔预算怎么算,跟条件请求那篇算的是同一本账。

canonical那一栏更值得琢磨。有9个站的canonical直接指回/search?q=zqxjkvbnmqp7这个地址本身——canonical的语义是“这是这组重复内容里的首选版本”,指回自己等于主动告诉搜索引擎:这个带着乱码参数的地址,就是它自己那组内容的规范版本。这9个站是allbirds、avocadogreenmattress、awaytravel、canyon、cybex-online、misen、monos、suitsupply、theordinary。

把三样叠在一起看,全都没做的只有theordinary.com一个站:200、canonical自指带词、没有noindex、robots.txt也没禁。这个数字比我预想的小,说明大多数站至少挡住了一层。但“至少挡住一层”和“想清楚了该怎么挡”,差得远。

这三样闸门,到底是谁配上去的?

这是我做这次统计真正想问的。判据很简单:同一个平台里,一条规则的覆盖率越高,它越可能来自出厂模板而不是某个人的决定。

59个站有robots.txt,全文没有任何两份是逐字相同的。但按平台分组数指令覆盖率,形态就出来了:

平台站数robots.txt行数中位覆盖六成以上站点的指令条数禁了/search的比例
Shopify131047条76.9%
Salesforce81121条50.0%
Next.js11320条18.2%
认不出平台24260条20.8%

Shopify那13个站里,Disallow: /account出现在84.6%的站上,/admin/orders/checkout/cart各76.9%,/carts/search各69.2%。还有一批更冷门的,比如/collections/*%2b*——那是把加号URL编码之后的写法,用来挡分面筛选生成的组合地址。

没有一个店主会自己想出要拦截百分号2b。这一整套是Shopify的robots.txt.liquid模板出厂就带的,商家不动它,它就一直在那儿。

反过来看认不出平台的那24个站:中位26行,最高一条指令的覆盖率只有12.5%,也就是24个站里有3个写了同一条。这批站的robots.txt是各写各的,没有共同来源。同样地,Next.js那11个站中位32行、零条高覆盖指令。

Salesforce那8个站有意思:行数中位112行,跟Shopify差不多长,但只有Disallow: /这一条覆盖过六成。它们高频出现的是/*prefn2*/*srule*/*pmax*这类分面参数的通配符,覆盖率在25%到37.5%之间——像是从同一份官方建议里各抄了一部分,抄的程度不一样。

把这张表倒过来读就是本文的答案:你的搜索页被不被robots.txt拦住,跟你用的平台高度相关,跟你自己关系不大。Shopify商家里有76.9%拦住了,不是因为他们比别人更懂SEO,是因为模板里本来就写着;自建站那批只有20.8%拦住,也不是因为他们更粗心,是因为没有任何人在他们的robots.txt里预先写好那一行。这跟robots指令有没有人读那篇的发现是一枚硬币的两面:那边是写了没人读,这边是压根没人写。

顺带说一句,Shopify的搜索模板本身在主题模板文档里是可以改的,主题作者完全可以在空结果时输出noindex。数据里Shopify有8/17带了noindex,说明确实有一部分主题这么做了,另一部分没有。同一个平台上的商家,命运取决于当初买了哪套主题。

一个不设防的搜索页,最后会长成什么样?

三条路径,按发生概率排。

第一条最常见:自己人喂出来的。站内搜索的热搜词模块、导航里的快捷入口、邮件营销里带搜索参数的落地页,都会生成真实的内链,爬虫顺着就进去了。开头那个户外家具站四千多个多余页面,绝大部分是这么来的。

第二条是外部链接。别人在论坛、社媒里贴了一个带搜索参数的地址,这个地址就进了发现队列。你删不掉别人的链接,只能在自己这一端处理。

第三条是被人用来生成内容。搜索词回显进页面,尤其是回显进标题,理论上就可以被外部构造出成千上万个“有内容”的页面。本次样本里有7个站把词写进了标题。这不是漏洞,也不会直接导致处罚,但它把这批页面的数量上限从有限变成了无限。

三条路径最后落到同一个后果上:一批内容高度雷同、对用户没有独立价值的页面进了抓取队列。薄内容那篇讲过判定逻辑,电商重复内容那篇里这属于八类成因中的参数变体一类。抓取预算这本账则要对着日志算,日志分析那篇里的做法可以直接照搬——先数一数爬虫每天在/search上敲多少下。

保哥手上有个做宠物用品的客户,当初查这件事花了半天,办法很笨但有效:把三个月的日志按路径聚合一遍,/search?q=这一段占掉了爬虫总请求的11%,而这些地址带来的自然点击是零。腾出来的额度,两周内就体现在新品的收录速度上了。要注意这跟分面筛选产生的地址爆炸不是一回事:那边是组合爆炸、量级更大,这边是单参数、但取值无限。两者常常同时存在,得分开数。

该怎么办,先动哪一样?

按见效速度和风险排,顺序是这样的。

先做noindex,它比robots.txt更该优先。这两件事经常被当成同一件事,其实不是:robots.txt管的是“别抓”,noindex管的是“别收”。Google对noindex的说明里有一条关键的注意事项——如果你用robots.txt禁止抓取,爬虫就读不到页面上的noindex,这个地址反而可能因为外链而以无标题的形式留在索引里。想清除已收录的,必须先让它能抓、读到noindex,收干净之后再考虑要不要禁抓。这个先后关系在noindex与canonical那篇里按九种场景拆过。

第二步是把canonical改成指向不带参数的搜索入口,或者干脆去掉。指回自己是最坏的一种,等于替这个地址背书。要注意canonical只是建议不是指令,Google最终选哪个有它自己的一套决策逻辑,别指望这一条就能解决问题。

第三步才轮到robots.txt。写法上RFC 9309规定的匹配规则跟很多人的直觉不一样,通配符和路径前缀的判定容易写错,动手前值得对着规则生成器那篇核一遍。方案层面的四种选择——Disallow、noindex、canonical、静态化——站内搜索URL该不该屏蔽那篇有一张完整的选型矩阵,本文只是给它补了一组横向实测数据。

第四步,把这件事挪到监控里。GSC的覆盖率报告里按路径筛/search,看有多少地址处于已收录或已抓取未收录的状态。这个数字应该逐月下降,不降就说明前三步有一步没生效。索引覆盖状态那篇解释了每种状态各代表什么。

还有一种情况这套顺序完全不适用:站上根本没有robots.txt。首页正常的70个站里有11个是这样,包括allbirds、misen、monos这几个不算小的牌子。没有这个文件本身不是错误,默认就是全部允许;但它意味着连“我这里有一份没人管过的默认配置”这个认知都不存在。这类站要挡搜索页,只能靠页面上的noindex,没有第二条路。

另外提一句本次样本的分布:59份robots.txt里,最长的431行,最短的只有1行,中位数53行。长短之间差了四百多行,而这个差距的绝大部分不是谁比谁更用心,是平台模板的长度差。

还有一件顺手能做的事:去数一数自己robots.txt有多少行,再数一数其中有几行是自己加的。如果你用的是Shopify,那份文件大概率一百行出头,而你写的可能是零行。这不是问题,模板写得挺好;问题是你不知道它写了什么,于是也不知道它漏了什么。

什么情况下反而不该屏蔽搜索页?

有两种,值得单独说,因为一刀切屏蔽也是错的。

第一种是搜索页本身就是有价值的落地页。有些站把高频搜索词做成了静态化的结果页,配了独立的标题、描述和编辑推荐语,这类页面跟普通分类页没有区别,屏蔽掉纯属自断一臂。判据是:这个页面有没有人工编辑过的内容,有没有稳定的地址,会不会因为库存变化而变成空页。三条都满足才留。

第二种是站内搜索数据本身的价值。就算页面屏蔽了,搜索词的日志也该留着——那是用户用自己的话告诉你他们想要什么,是关键词研究里最便宜也最准的一手来源。屏蔽的是页面进索引,不是关掉这个功能。

最后提醒一句风险。给搜索页加noindex之前,先确认你的搜索路径没有被别的东西复用。有些主题把分类页、标签页也走同一套模板,改模板的时候容易一起波及。改之前把/search下面所有正在被收录的地址导出来看一眼,别把该留的一起关掉。这一步跟改版前的地址盘点是同一个动作。

常见问题解答

搜索结果为空,应该返回404吗?

不应该。404描述的是这个地址不存在,而搜索页是存在的,只是这次查询没命中。返回200在语义上是对的,本次40个站也全部返回200。真正需要处理的不是状态码,是这一页要不要进索引,那是noindex和canonical该管的事。少数站会在空结果时跳转到一个提示页,那样做会额外制造一次跳转,不如原地返回200加noindex干净。

用robots.txt禁掉搜索路径就够了吗?

不够,而且顺序容易搞反。robots.txt禁的是抓取,被禁之后爬虫读不到页面上的noindex,已经收录的地址可能反而清不掉、以无标题的形式留在索引里。正确顺序是先允许抓取、让它读到noindex,等索引清干净之后再决定要不要禁抓。如果站上完全没有已收录的搜索页,直接禁抓也可以。

怎么知道我的站有没有这个问题?

两步。第一步用curl请一个必然搜不到的乱码词,看返回的状态码、meta robots和canonical;第二步去GSC的页面报告里按路径筛搜索路径,看被收录了多少个。第二步的数字是决定要不要动手的依据——如果本来就是零,前面那些配置怎么写都不着急。

搜索结果靠JavaScript渲染,是不是就没这个问题了?

不是,反而多一层麻烦。本次22个站属于这种情况,它们搜到和搜不到返回的HTML体积几乎一样,字节比中位数1.00。对不执行JavaScript的那一遍抓取来说,这些地址返回的是同一份文件,等于凭空制造了一批完全重复的页面。索引指令该配还是得配。

canonical指回带参数的地址自己,有多严重?

不算漏洞,但它取消了canonical本该起的合并作用。canonical的意思是“这组重复内容里选我”,指回自己等于宣布这个带乱码参数的地址是一个独立的规范页面。本次有9个站是这样。稳妥的做法是指向不带查询参数的搜索入口,或者在这一页上干脆不输出canonical,让noindex去起作用。

为什么有29个站被剔出了统计?

因为它们没有在搜。判据是换一个必然搜得到的词,回答完全不变、标题跟首页一字不差——这说明请求被路由到了某个固定页面,不是搜索结果。这类站占了69个里的29个,比例不低。如果不做这一步过滤,它们会被当成“回了200的搜索页”一起统计,把结论污染掉。

我用的是Shopify,是不是就不用管了?

不是。数据里Shopify商家禁了搜索路径的比例是76.9%,确实比自建站的20.8%高得多,但那意味着还有将近四分之一没禁。而且noindex这一项,17个Shopify站里只有8个带了,取决于当初买的主题怎么写。花两分钟curl一下自己的搜索页,比猜测靠谱。

权威参考资料

分享到
标签
版权声明

本文标题:《站内搜索页SEO:搜不到商品那一页,一半站允许被收录》

本文链接:https://zhangwenbao.com/site-search-empty-result-indexability-audit.html

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

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