搜索全站
Google SEO 8 分钟阅读

Googlebot 的抓取与渲染机制:两阶段处理对 SEO 的实际影响

Googlebot 采用先抓取 HTML、再排队渲染的两阶段机制,JavaScript 站点因此存在索引延迟。本文讲清抓取队列、Crawl Queue 与 Render Queue 的工作方式、对 JS 站点的实际影响,并给出验证与规避方案。

U
UFO_PF 2020-09-14 · 阅读 8 分钟
Go GOOGLE SEO
阅读提示
本文约 3,011 字,预计阅读 8 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:页面明明有内容,Google 却说「未检测到内容」

用前端框架做的站点常遇到这种情况:URL 检查工具里「已抓取的 HTML」是空的,或者「已编入索引」但搜索结果摘要里空空如也。浏览器里打开一切正常,Google 却看不到内容。原因是 Googlebot 处理页面的方式与我们打开浏览器的方式不同——它分两步走,第二步可能要等很久,甚至可能不做。

原理分析:两阶段抓取到底发生了什么

Googlebot 的处理流程可以拆成三段:

阶段 动作 产出 时间特征
抓取 下载 HTML、CSS、JS 等资源 原始 HTML 存入抓取队列 秒级
渲染 用渲染服务执行 JS,生成 DOM 渲染后的 HTML 可能延迟数秒到数天
索引 基于渲染结果抽取内容与链接 加入索引 再一次排队

关键点在于:Googlebot 首次抓取时只拿 HTML,不执行 JS。JS 生成的 DOM 要等渲染队列排到才处理。而渲染队列是有成本的,需要真实的浏览器环境执行脚本,因此 Google 会把它作为第二优先级处理。对于大型 JS 站点,这个延迟可以是数天。

由此产生两个必须理解的现实:

第一,原始 HTML 里没有的内容,在渲染完成前不可见。这包括内容,也包括链接——用 JS 动态生成的 <a> 标签,在渲染前不会被发现,内部链接结构因此失效。

第二,渲染不是无限次重试的。脚本报错、接口超时、资源被 robots.txt 屏蔽(导致渲染时拿不到 CSS/JS),都会让渲染结果不完整,最终索引的是残缺内容。

Google 官方文档对 JS SEO 给出的核心建议是:确保服务器返回的 HTML 已包含主要内容(服务端渲染或预渲染),并把 JS 作为增强而非内容载体。同时,robots.txt 不应屏蔽 CSS 与 JS 资源,否则渲染会失败。

保姆级实操

步骤一:用 URL 检查工具对比原始与渲染结果
在 Search Console 的网址检查里点「查看已抓取的页面」,切到「HTML」与「屏幕截图」两个标签,对比原始 HTML 与渲染结果。如果原始 HTML 里没有正文而渲染结果里有,说明是纯客户端渲染。

步骤二:用 curl 模拟无 JS 环境
最直接的自查方式:

# 不带 JS 执行,看原始 HTML 里有没有正文
curl -s https://mimangfei.com/sample-post/ | grep -c "<article"
curl -s https://mimangfei.com/sample-post/ | sed -n 's/.*<title>\(.*\)<\/title>.*/\1/p'

步骤三:确认 robots.txt 没有屏蔽渲染资源
这是最容易被忽略的渲染失败原因:

User-agent: Googlebot
Allow: /
Allow: /wp-includes/
Allow: /wp-content/themes/
Allow: /wp-content/plugins/
Disallow: /wp-admin/

步骤四:把关键内容放到服务端输出
WordPress 主题侧要确保正文与链接在 PHP 阶段就输出,而不是靠 JS 补。检查主题的 content.php 是否直接输出 the_content():

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
    <h1><?php the_title(); ?></h1>
    <div class="entry-content">
        <?php the_content(); ?>
    </div>
    <?php wp_link_pages(); ?>
</article>

步骤五:链接必须是真实 <a href>
用 onclick 或 router.push 跳转的链接,Googlebot 在渲染前发现不了。确保导航与相关文章输出的是标准锚点:

$related = get_posts( array(
    'category__in'   => wp_get_post_categories( get_the_ID() ),
    'posts_per_page' => 6,
    'exclude'        => array( get_the_ID() ),
) );
foreach ( $related as $r ) {
    printf( '<a href="%s">%s</a><br>', esc_url( get_permalink( $r ) ), esc_html( get_the_title( $r ) ) );
}

步骤六:用 Search Console 的 URL 检查看渲染状态
网址检查工具会给出「已抓取的页面」的截图与 HTML,也能看到资源是否被屏蔽。若看到「页面资源无法加载」,优先排查 robots.txt 与资源 URL 的协议(HTTP 与 HTTPS 混用会被拦截)。

步骤七:必要时做预渲染
对必须依赖 JS 的模块,可用预渲染服务或构建期静态化。判断标准是:这一块内容是否参与排名。不参与排名的部分(评论区、实时数据)用 JS 完全没问题。

常见踩坑

  1. 正文完全由前端框架渲染,原始 HTML 只有一个空的 <div id="app">。
  2. robots.txt 屏蔽了 /static/ 或 /assets/,导致渲染拿不到 CSS/JS,渲染结果残缺。
  3. 内链用 onclick 跳转,渲染前无法被发现,内部链接结构失效。
  4. 依赖客户端调用接口取数据,接口超时导致渲染结果为空。
  5. 认为「URL 检查工具的截图正常」就等于没问题,忽略了渲染延迟对新页面的影响。
  6. 案例:某站点用 Vue 重写了文章页,上线两周内新文章的收录率从 92% 掉到 31%。排查发现正文由接口异步加载,渲染队列平均延迟 4 天,且部分请求因接口限流返回空。改为服务端输出正文后,收录率一周内回到 88%。
  7. 用 JS 做分页加载,第 2 页之后的内容没有任何静态链接入口。

总结

理解两阶段机制的判断标准是:把 JS 全部禁用后,页面还剩多少可索引内容。剩下的部分就是 Googlebot 首次抓取能拿到的全部。落地做法是先用 curl 看原始 HTML,再确认 robots.txt 未屏蔽渲染资源,最后把参与排名的正文与内链改为服务端输出。JS 可以作为增强,但不要作为内容的唯一载体。渲染延迟带来的收录滞后可以通过 抓取预算优化 与 服务器日志分析 来观测和验证;如果站点同时面向多地区,还要注意 国际站点URL结构 带来的版本数量放大效应——每个语言版本都要单独排队渲染。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。