很多人以为蜘蛛抓一个 URL 就是一次动作:来、下载、走。实际情况更接近两次到访——第一次只拿走 HTML 源码,第二次才在另一条管线里执行 JavaScript。分不清这两步,就很容易把链接放在蜘蛛第一次看不到的地方。
第一次到访:拿到的只是源码
蜘蛛发起请求时,服务器返回的 HTML 响应体就是它第一眼看到的东西。这一刻,页面还没有"运行":由 JS 注入的导航、列表、分页链接、正文内容,全都不在里面。
这带来一个直接的后果:如果站内链接是脚本生成的,URL 的发现就被推迟到渲染阶段。而渲染不是每个 URL 都会做,也不是立刻做。有些页面等到了,有些页面一直没等到。
第二次到访:渲染队列是另一条管线
渲染管线会执行页面上的脚本、发起额外请求、构建最终的 DOM,再从里面提取链接和内容。它比单纯抓 HTML 贵得多:占资源、要排队、有超时,通常只对一部分页面触发。
- 渲染有等待上限,第三方接口慢,页面就可能渲染不全;
- 渲染通常在无头环境里跑,滚动、点击、登录态这类交互不会发生;
- 渲染产出会进缓存,但缓存什么时候刷新、是否刷新,站点无法直接控制。
换句话说,渲染是"补救",不是"默认"。把关键链接和内容押在渲染上,等于把抓取节奏交给了一条你不能直接调的队列。
哪些写法会拖慢链接发现
- 用 div 或 span 加 onclick 跳转,没有真正的 a 标签和 href;
- 前端路由切换后,地址栏变了,但页面里没有可爬的链接指向那些 URL;
- 无限滚动:内容靠滚动触发加载,渲染环境下不会滚;
- 分页是按钮,实际 URL 不可直接访问;
- 链接依赖第三方接口返回后才渲染出来。
这些做法对用户体验未必有问题,但对第一次到访的蜘蛛来说,页面里确实什么都没有。
让两条管线都省事的做法
- 导航、栏目、分页、文章列表用真实 a 标签,href 指向可访问的 URL;
- 首屏关键内容和链接尽量由服务端输出,SSR、SSG 或预渲染都可以;
- 分页给出稳定的 URL 结构,让每个页码都能被直接请求;
- 必须用 JS 渲染的部分,也留一条 HTML 兜底路径,例如 noscript 中的链接或静态列表页;
- 把重要 URL 同时放进 Sitemap,作为发现渠道的补充。
怎么验证自己的站点
抓取工具里关闭 JavaScript,看剩下多少链接和内容;再打开 JavaScript 对比一次。两次差异越大,说明站点对渲染管线的依赖越重。服务器日志里,如果出现大量 CSS、JS 等静态资源的请求,往往就是渲染管线的访问痕迹,可以和纯抓取请求分开观察。
链接发现靠的是源码可见性,渲染只是第二道保险。第一眼看不到的链接,抓取节奏就只能听天由命。
顺着这条思路检查一遍站点,通常能发现几处"只有渲染后才存在"的链接。把它们挪回 HTML 源码,URL 发现的节奏会立刻稳定不少。