搜索抓取

蜘蛛渲染页面的边界:JS 生成的内容和链接能被抓到多少

页面用 JavaScript 生成内容和链接时,蜘蛛第一次拿到的 HTML 里可能什么都没有。本文把抓取和渲染拆成两个阶段来看,说明渲染队列、超时、视口与交互带来的限制,并给出让重要 URL 直接出现在原始 HTML 里的具体做法和自查顺序。

搜索抓取

蜘蛛渲染页面的边界:JS 生成的内容和链接能被抓到多少

不少站点把列表、详情链接和正文都交给 JavaScript 生成,然后在日志里发现蜘蛛只访问了首页和几个入口。问题通常不是蜘蛛“不愿意抓”,而是它第一次拿到的 HTML 里根本没有那些 URL。把抓取和渲染分成两件事来看,排查思路会清楚很多。

抓取和渲染是两个阶段

蜘蛛请求一个地址,最先拿到的是服务器返回的原始 HTML。这一阶段做的事情很朴素:从响应里提取链接和文本,把新 URL 放进队列。如果原始 HTML 里没有某个链接,这个地址往往连被发现的机会都没有。

渲染是后面的事情。蜘蛛会把其中一部分页面送进渲染队列,执行页面上的 JS,等 DOM 相对稳定后再提取一次链接和内容。这一步有排队、有资源上限,也有失败概率,和第一次抓取不是同一条流水线。

哪些内容必须依赖渲染才能出现

  • 列表由前端框架请求接口后拼出来的分页和条目链接
  • 详情页地址由 JS 拼接字符串生成,HTML 里只有一个空容器
  • 正文完全来自接口返回,服务端没有任何兜底输出
  • 跳转写成点击事件或按钮,而不是带 href 的 a 标签
  • 需要登录、需要选择地区、需要点击“展开”才显示的内容

渲染这一环的限制在哪里

  • 排队:渲染不是同步发生的,从抓取到渲染完成可能有明显延迟,甚至长时间不渲染。
  • 超时与失败:脚本执行过久、依赖被阻塞、请求报错,都会让这次渲染被放弃,且不一定立刻重试。
  • 视口:渲染通常在固定视口下进行,位于视口之外、需要滚动才显示的内容不一定被加载。
  • 交互:滚动、点击、“加载更多”这类用户行为一般不会自动执行。
  • 资源可访问性:被 robots.txt 或权限拦住的 JS、CSS 会影响解析结果,渲染出来的结构可能和你在浏览器里看到的完全不同。

让 URL 出现在原始 HTML 里的几种做法

  1. 列表页做服务端渲染或预渲染,至少让条目链接以 a href 的形式直接输出。
  2. 分页使用可点击链接,给出真实可访问的网址,而不是纯 JS 按钮。
  3. 图片和模块懒加载尽量用原生属性,并在 noscript 中留一份链接或文本兜底。
  4. 无限滚动配一套分页地址,让滚动到底后存在一个可被抓取的等价 URL。
  5. 让 Sitemap 与站内链接互为备份,重要地址至少在其中一处出现。
  6. 移动端与桌面端模板差异较大时,确认两套模板输出的链接集合不要相差太多。

渲染不是免费的通行证

即使页面能被渲染,代价也比静态 HTML 高:排队更久、失败概率更大、重抓间隔通常也更长,而且渲染结果一旦出错,你很难立刻从日志里看出来。能直接输出的内容,尽量直接输出。

不要假设蜘蛛会滚动、点击或等待你的“加载更多”按钮。任何需要用户动作才出现的 URL,都按“默认不存在”来设计兜底方案。

自查的顺序

  • 先看源代码而不是审查元素,确认链接是否出现在原始 HTML 中。
  • 用抓取工具对比渲染前后的链接差异,找出只在渲染后出现的 URL。
  • 检查 JS 与接口请求是否被 robots.txt 或权限规则挡住。
  • 用站长平台的 URL 检查工具查看渲染快照,确认渲染出来的页面是否符合预期。
  • 把 Sitemap 中的 URL 与抓取日志做差集,差集里长期不被访问的部分,优先补内链或改成静态输出。

把站点的发现路径做成“不依赖 JS 也能走通”的结构之后,渲染问题就从阻塞项变成了优化项。剩下的工作才是提升渲染成功率、缩短渲染与重抓之间的间隔。