搜索引擎处理一个 URL,通常不是「打开浏览器看一眼」这么简单,而是分成两步:先拉取服务器返回的原始 HTML,从里面提取链接、标题和正文;只有当页面被判断为需要执行脚本时,才进入渲染队列。渲染要消耗额外的资源,排队时间往往比抓取本身长很多。因此,如果一条链接只存在于脚本执行之后的 DOM 里,它在第一步就是缺席的,被发现的时间会被明显拉长。
链接在原始 HTML 里缺席的几种常见写法
- 用 div 或 span 绑定点击事件:视觉上是按钮或列表项,但没有 a 标签和 href,抓取阶段读不到目标地址。
- 地址由脚本拼接:点击后执行跳转,URL 藏在 JS 变量或接口返回的数据里,HTML 源码中找不到。
- 列表靠接口异步填充:初始 HTML 只有一个空容器,几十条内容链接全部来自异步请求的返回值。
- 滚动触发加载:懒加载只处理了图片,列表项本身也依赖滚动事件才插入,首屏之外的内容默认不可见。
- hash 路由:地址栏变成带 #/detail/123 这类形式,真实路径不在初始 HTML 中出现,链接也不容易被当作独立入口。
抓取与渲染是两个阶段,代价不同
抓取阶段相对便宜,一次请求加一次解析就能拿到链接;渲染阶段昂贵得多,要么在无头浏览器里跑完整套脚本,要么依赖预渲染服务。所以搜索引擎会把渲染名额分给一部分页面,而不是每个 URL 都渲染。分配逻辑通常与页面重要性、更新频率、链接权重有关,外部很难知道具体阈值,但有一条稳定的规律:能直接从 HTML 读到链接的页面,被发现和再抓取的机会,远大于必须渲染才暴露链接的页面。
排查:先看源码,再看 DOM
- 用命令行请求一次页面,把返回的 HTML 存下来;或在浏览器中选择「查看网页源代码」,注意不是开发者工具里的元素面板。
- 在源码中搜索目标链接的特征片段,例如路径关键词、详情页 ID 前缀。
- 如果源码里搜不到,再在渲染后的 DOM 中搜一次,确认差异确实来自脚本注入。
- 对照服务器日志,看这些 URL 是否长期没有抓取记录,把「没被抓」和「没被发现」区分开——两者的处理方式完全不同。
可落地的改进方向
关键入口做服务端渲染或预渲染
首页、频道页、列表页、详情页的上下篇导航,这些承担分发职责的位置,优先让链接出现在原始 HTML 中。服务端渲染、静态化、预渲染都是常见路径,选哪种取决于现有架构和缓存策略,不必一步到位,先从流量最大的层级开始。
保留一份静态兜底
异步列表可以在 HTML 里先输出一小批链接,比如首屏若干条,脚本加载成功后再替换或追加。这样即使渲染环节被延后,链接依然存在。分页部分同理,除了「加载更多」按钮,最好保留可点击的页码链接,让翻页序列在源码里连续。
用 Sitemap 覆盖链接薄弱的页面
有些页面天然缺少内链入口,比如筛选组合页、归档页、专题页,它们更容易被漏掉。可以把这类 URL 明确写进 Sitemap,作为发现渠道的补充。注意只放返回 200 且内容正常的地址,把 404、空壳页混进去,会稀释整份文件的可信度。
内链结构仍然是最稳的一条路
一个 URL 如果能从多个相关页面通过正常的 a 标签到达,被抓取的路径就多几条;如果它只在某次脚本执行后才出现,就只剩下渲染队列这一条窄路。梳理栏目层级、补充上下文内链、给孤岛页面找归属,这些老办法在脚本站点上依然有效。
几个容易踩的坑
- 为了「让蜘蛛看到」堆砌隐藏链接或隐藏文字,风险大于收益。
- 把主导航完全交给脚本,虽有渲染兜底,但延迟和覆盖都不确定。
- 改版后只更新了前端路由,忘了同步 Sitemap 和旧链接的跳转规则。
- 认为提交了 Sitemap 就一定会被立刻抓取,忽略了抓取调度本身的时间成本。
判断标准可以很简单:关掉 JavaScript,用文本浏览器打开页面,还能不能顺着链接走到你想让搜索蜘蛛到达的页面。如果能,说明基础结构是通的;如果不能,至少要让关键层级在源码中保留一条通路。