搜索全站
百度SEO 9 分钟阅读

百度对 AJAX 加载内容的处理建议:为什么关键内容必须直出

列表用 AJAX 翻页、详情用接口异步拉,页面看着流畅,百度却只抓到一个空框架。本文讲清哪些内容必须服务端直出、哪些可以异步,以及在 WordPress 里的改造写法。

U
UFO_PF 2021-03-11 · 阅读 9 分钟
百度 百度SEO
阅读提示
本文约 3,517 字,预计阅读 9 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:无限滚动很酷,但百度只看到第一屏的东西

产品同学喜欢无限滚动、异步筛选、点击展开,用户体验确实好。问题在于这些交互把内容从 HTML 里搬到了接口里。百度蜘蛛访问一个 URL,拿到的是 HTML 源码,它不会去点「加载更多」,也不会等你的 fetch 请求回来再拼接 DOM。于是页面上写着「共 328 条评论」,源码里一条评论都没有;筛选器里有十几个标签页,源码里只有默认那一个。站长看到的是收录量上不去、页面被判定内容单薄,工程师看到的是接口一切正常,两边对问题的理解完全不在一个层面。解决办法不是砍掉交互,而是分清主次:参与排名的内容服务端直出,纯交互增强的部分异步加载。

原理分析:直出与异步的分界线在哪里

判断标准很朴素:这段内容是否希望能被单独搜索到。正文、标题、商品参数、评论主体、列表第一页——这些都参与排名,必须直出;点赞数、实时库存、个性化推荐、第二页以后的内容——这些不影响主页面的索引价值,可以异步。这条线画清楚后,大部分争议就消失了。

内容类型 建议方式 理由
文章正文、标题 直出 核心索引内容
列表第一页 直出 入口页要参与排名
列表第二页及以后 异步 + 独立 URL 通过分页 URL 让百度抓
评论主体 直出 长尾词来源
点赞、阅读数 异步 数值频繁变动,直出易造成快照不一致
个性化推荐 异步 与索引无关
筛选结果 异步 + 参数 URL 规范化 避免产生海量低质参数页

WordPress 里最常见的两个异步入口是 admin-ajax.php 和 REST API(/wp-json/wp/v2/posts)。这两个地址本身不需要被收录,也不应该在 sitemap 里出现;需要被收录的是承载它们的页面,而页面必须在首次响应里就带上正文。

保姆级实操

步骤 1:审计现有页面的异步依赖。 关闭浏览器 JS(或用 curl 直接请求),逐个看关键页面还剩多少内容。剩下的部分就是百度能看到的部分。

步骤 2:把列表首屏改成服务端查询直出。 下面是一个文章列表的首屏直出写法,第一页由 PHP 直接渲染,后续页交给前端异步拉取:

// 主题模板中:首屏 10 条由 WP_Query 直出,剩下的交给 JS
$args = [
    'post_type'           => 'post',
    'posts_per_page'       => 10,
    'paged'                => 1,
    'ignore_sticky_posts'  => true,
    'no_found_rows'        => true, // 首屏不需要总条数,省一次 SQL
];
$q = new WP_Query( $args );

echo '<ul class="post-list" data-page="1">';
while ( $q->have_posts() ) {
    $q->the_post();
    printf(
        '<li><a href="%s">%s</a><span>%s</span></li>',
        esc_url( get_permalink() ),
        esc_html( get_the_title() ),
        esc_html( get_the_date() )
    );
}
echo '</ul>';
wp_reset_postdata();

步骤 3:异步部分只补增强内容,不补关键内容。 前端拉取第二页时,同时把 URL 更新到带分页参数的形式,并为分页保留真实可访问的 URL:

const list = document.querySelector('.post-list');
let page = Number(list.dataset.page || 1);
let loading = false;

document.querySelector('#load-more')?.addEventListener('click', async () => {
  if (loading) return;
  loading = true;
  page += 1;

  const res = await fetch(`/wp-json/wp/v2/posts?page=${page}&per_page=10`);
  const posts = await res.json();

  posts.forEach((p) => {
    const li = document.createElement('li');
    li.innerHTML = `<a href="${p.link}">${p.title.rendered}</a>`;
    list.appendChild(li);
  });

  // 同步地址栏,并保证该地址可被直接访问与抓取
  history.replaceState(null, '', `/page/${page}/`);
  loading = false;
});

步骤 4:给每个异步内容块准备一个静态兜底 URL。 无限滚动的列表必须有 /page/2/、/page/3/ 这样的真实分页地址,且这些地址返回的是完整 HTML,不是 JS 拼出来的。可以用 paginate_links() 生成分页链接,再用 CSS 在移动端隐藏分页器——注意这里是视觉隐藏但 HTML 存在,属于合规做法。

步骤 5:确认异步接口不在抓取路径上。 在 robots.txt 里屏蔽 /wp-json/ 与 /wp-admin/admin-ajax.php,避免百度把接口地址当页面抓:

add_filter( 'robots_txt', function ( $output ) {
    $lines = [
        'User-agent: *',
        'Disallow: /wp-admin/admin-ajax.php',
        'Disallow: /wp-json/',
        'Allow: /wp-json/wp/v2/posts', // 若需对外提供数据则放行
        '',
    ];
    return implode( PHP_EOL, $lines );
}, 99 );

步骤 6:验证。 用抓取诊断看源码,确认正文、首屏列表、评论主体都在源码里,点赞数这类数字缺失是正常的。

常见踩坑

  1. 把评论做成异步加载,页面被判定为「内容单薄」,长尾词全丢。
  2. 案例: 某站把商品详情的规格参数改成接口异步获取,改完一个月内详情页收录掉七成,且快照里规格区域一直空白,排查时开发坚持「接口返回是好的」,最后用 curl 对比才确认源码里确实没有参数,改回直出后六周恢复。
  3. 无限滚动没做分页 URL,第二页以后的内容永远进不了索引。
  4. robots.txt 屏蔽了 /wp-json/ 却忘记主题依赖它渲染前台,结果前台直接白屏。
  5. 异步内容用 JS 动态写入 <title>,抓取时标题为空。
  6. 分页 URL 用 #page=2 的 hash 形式,百度不识别。
  7. 为了「直出」把整页改成服务端渲染,首屏时间从 0.8 秒涨到 3.5 秒,反而触发闪电算法的负面评价——直出不等于放弃性能。

总结

判断一个模块该不该异步,问一句:「这段内容我希望用户在百度搜到吗?」答案是肯定的,就必须出现在首次 HTML 响应里。行动清单:关 JS 审计页面 → 列表首屏与正文改服务端直出 → 交互增强部分保留异步 → 为异步列表补真实分页 URL → robots 屏蔽接口路径 → 抓取诊断验证源码。渐进增强不是妥协,而是同时满足体验与收录的正确姿势。相关做法可结合 百度对 JS 渲染内容的抓取现状 一起评估,参数化分页的处理见 站内搜索页与参数 URL 处理。

U
UFO_PF作者

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

留下你的想法

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