站内搜索页SEO:搜不到商品那一页,一半站允许被收录
本文目录
- 搜一个世界上不存在的词,站点会回什么?
- 怎么确定它是真的在搜,而不是随便给了我一页?
- 那40个站,搜不到东西的时候回了什么状态码?
- 搜到和搜不到,爬虫能看出区别吗?
- 这一页准备好被搜索引擎收录了吗?
- 这三样闸门,到底是谁配上去的?
- 一个不设防的搜索页,最后会长成什么样?
- 该怎么办,先动哪一样?
- 什么情况下反而不该屏蔽搜索页?
- 常见问题解答
- 搜索结果为空,应该返回404吗?
- 用robots.txt禁掉搜索路径就够了吗?
- 怎么知道我的站有没有这个问题?
- 搜索结果靠JavaScript渲染,是不是就没这个问题了?
- canonical指回带参数的地址自己,有多严重?
- 为什么有29个站被剔出了统计?
- 我用的是Shopify,是不是就不用管了?
- 权威参考资料
摘要:拿一个不存在于任何语言的乱码词去搜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。剩下两组各两个站,分别是小写的search和Search Results。
这些公司彼此没有关系,做的品类也不挨着,标题却一字不差。原因不复杂:他们用的是同一套主题模板,而模板作者在search.liquid里写了什么,商家多半没去看过。一个只写着Search的标题如果进了索引,在搜索结果里跟别人的完全一样,谁也认不出是哪家。
再往下拆一层,这40个站按平台分是Shopify 17个、认不出平台11个、Next.js 6个、Salesforce 6个。Shopify占了将近一半,这批站的行为高度一致,也正是下一节能拆出归属的原因。
这一页准备好被搜索引擎收录了吗?
三样东西可以拦住它:页面上的noindex、canonical指向哪里、robots.txt里有没有那一行。逐个数。
| 闸门 | 做了的站 | 占比 |
|---|---|---|
| 页面带noindex(meta或响应头) | 17/40 | 42.5% |
| canonical指向不带词的搜索页 | 20/40 | 50.0% |
| canonical指回这个带词地址自己 | 9/40 | 22.5% |
| 完全没有canonical | 6/40 | 15.0% |
| robots.txt禁了自己这条搜索路径 | 15/36 | 41.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的比例 |
|---|---|---|---|---|
| Shopify | 13 | 104 | 7条 | 76.9% |
| Salesforce | 8 | 112 | 1条 | 50.0% |
| Next.js | 11 | 32 | 0条 | 18.2% |
| 认不出平台 | 24 | 26 | 0条 | 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