你辛辛苦苦搭的SPA站,百度就是不收录?问题出在哪
你用 Vue 或 React 搭了个挺顺的单页应用,首页动效丝滑、交互流畅,结果一查百度收录——首页勉强在,其他页面全没影。不是百度故意卡你,是它真没“看见”那些藏在 JS 后面的内容。
SPA 的页面结构和百度爬虫的抓取习惯,本来就不在一个频道上。它不像人一样等 JS 跑完再看内容,而更像一个赶时间的快递员:门开了,扫一眼屋里有没有货,没看到就走。你得让它进门第一眼就看见“货”,而不是对着个空盒子发呆。
SPA收录难,难在哪三个核心点上?
第一,JS 渲染太晚,爬虫等不及
百度爬虫确实能跑 JS,但执行有超时限制。如果你的页面依赖 JS 动态插入主体内容,它很可能只拿到一个光秃秃的 <div id="app"></div>,连标题都没渲染出来。
第二,路由是假的,爬虫认不出来
传统网站每个 URL 对应一个真实 HTML 文件;而 SPA 大多靠 history.pushState() 或 # 切换视图,所有页面共享同一个 index.html。爬虫不会主动点“关于我们”按钮去触发路由跳转,它只按 URL 抓——可你的“关于我们”页面根本没有独立 URL。
第三,首屏加载太重,爬虫直接放弃
一个打包后 1.8MB 的 JS 文件,下载+解析+执行要好几秒。百度爬虫的等待窗口比你想象中短得多,往往 JS 还没跑完,连接就已经断了。
我之前优化过一个本地生活类 SPA,首页 JS 加载慢、路由全靠 history,上线两个月百度只收录了首页,而且标题还是默认的 “Vue App”。
策略一:预渲染能否解决SPA收录问题?怎么落地?
预渲染适合内容稳定、更新不频繁的页面,比如企业官网、产品介绍、帮助文档这类。它的逻辑很直白:你在本地或构建时,用工具(比如 prerender-spa-plugin 或 vite-plugin-prerender)把每个路由提前“快照”成静态 HTML,部署时直接扔到服务器对应路径下。爬虫来的时候,看到的就是带标题、带正文、带 SEO 标签的完整页面。
举个真实例子:一个做工业设备展示的 SPA 站,20 个页面全是静态信息。上线后百度只收了首页。我们加了预渲染,把 /products/ /about/ /contact/ 全部生成 HTML 放进 dist 目录,重新部署。一周后,收录数从 1 个涨到 18 个,几乎全覆盖。
但注意:预渲染对“活数据”无效。比如用户登录后的个人中心、实时报价面板、评论区——这些内容没法提前快照,渲染出来也是空的或者过期的。别硬套,该用 SSR 的地方别省事。
策略二:服务端渲染(SSR)是捷径还是坑?什么场景必须用?
SSR 是目前最靠谱的解法,尤其适合内容驱动型站点:电商商品页、新闻详情、博客文章、政策解读类页面。Nuxt.js 和 Next.js 都原生支持,原理也简单:用户或爬虫一请求,Node 服务就立刻把页面 HTML 渲染好再吐出去,爬虫打开就是满屏内容。
但它不是“装上就灵”。
- 服务器扛不住:每个请求都实时渲染,CPU 压力肉眼可见。我帮一个政务资讯站上 Nuxt SSR,流量稍一上来,服务器负载直接飙到 70%+,后来加了内存缓存才稳住。
- 开发要绕坑:
window、document这些浏览器专属对象,在 Node 环境里压根不存在。组件里随便写个document.querySelector,服务端就报错崩掉。 - 缓存得自己管:如果商品价格每小时变一次,你缓存一天,爬虫抓到的就是过期价。
所以,如果你的站点以静态内容为主,或者更新频率低(比如每周发三篇博客),优先选预渲染;如果是高频更新、强 SEO 依赖的业务(比如电商、媒体、政府服务),SSR 值得咬牙上。
策略三:动态渲染(Dynamic Rendering)适合谁?怎么配置?
动态渲染适合那种代码已经跑在线上、又不想大改架构的团队。思路就一句话:对人用 SPA,对百度用预渲染 HTML。靠一个中间层(比如 Rendertron 或自己搭的 Puppeteer 服务)识别 User-Agent,发现是 Baiduspider 就调用无头浏览器跑一遍 JS,截取最终 HTML 返回;其他请求照常走前端路由。
配置不难:在 Nginx 或反向代理里加几行判断逻辑,检测到 Baiduspider 就把请求转发过去。
但它有两个现实问题:
- 响应慢:Puppeteer 启动、加载、等待 JS 执行,平均要 1–2 秒。百度对响应时间敏感,稍慢一点就可能放弃抓取。
- 多一台机器:你得单独维护一个渲染服务,内存和 CPU 开销不小。有个 SaaS 工具站试过这方案,百度收录量确实上去了,但服务器月费翻了一倍多。
所以,除非你团队人力紧张、技术债重、又急需提升收录,否则建议优先考虑预渲染或 SSR。动态渲染是“救急用的拐杖”,不是长期拐杖。
策略四:如何用<link>标签和sitemap帮百度爬虫找到SPA页面?
就算你上了 SSR 或预渲染,百度也可能漏抓某些页面。这时候,<link> 标签和 sitemap 就是你的导航仪。
- 在
index.html的<head>里加<link rel="canonical" href="https://yourdomain.com/">,告诉百度“这是主地址”,避免它把/和/index.html当成两个页面。 - 加
<link rel="alternate" hreflang="zh-cn">,明确语言版本,尤其当你有中英文双语时别省这行。 - 最重要的是 XML sitemap:生成一个包含所有公开路由的
sitemap.xml(比如/products/a,/news/2024-06-01),提交到百度资源平台。记得填lastmod(最后修改时间)和changefreq(更新频率),比如文章页设daily,公司页设yearly。
我优化过一个法律咨询类 SPA 博客,每天更新 3–5 篇文章,但百度只收了不到一成。我们补全了 sitemap,把所有 /article/xxx 都列进去,changefreq 设为 daily,提交后三周,收录量明显提升。
另外,检查你的 robots.txt:别写 Disallow: /js/ 或 Disallow: /css/。百度需要这些文件才能执行 JS、还原样式。真正该屏蔽的,只有 /admin/、/api/ 这类后台路径。
策略五:百度爬虫抓取SPA时,有哪些JS执行的坑要避开?
百度的 JS 执行能力,远不如 Chrome 浏览器。有些你以为“小问题”的写法,对爬虫就是致命伤。
别等
DOMContentLoaded或window.onload再挂内容
爬虫不一定会等这些事件触发。核心内容(标题、摘要、正文)最好由 SSR 或预渲染兜底,JS 只负责交互动画、懒加载图片这类增强项。别用
setTimeout延迟渲染关键内容
比如为了“用户体验”,2 秒后再显示文章正文——爬虫很可能在 1 秒内就收工了,只抓到 loading 占位符。第三方脚本别拖后腿
广告、统计、客服插件如果阻塞主线程,会导致整个页面渲染卡住。我见过一个医疗科普站,因为某个客服 SDK 加载失败,JS 报错中断,百度抓到的全是空白页。解决方案很简单:给第三方脚本加async,或者用try...catch包一层,确保出错也不影响主流程。小心浏览器专属 API
localStorage、sessionStorage、window.location.hash在爬虫的 Headless Chrome 环境里不一定可用。一个组件用了hash解析路由,结果爬虫环境下hash是空字符串,直接undefined报错,整页白屏。修复方式:加判断,或者用URLSearchParams这类更稳妥的方式读参数。
结尾:今天就能执行的3步操作
别收藏吃灰。现在就打开你的项目终端和浏览器,按顺序做这三件事:
- 打开 Chrome,右键 → “查看网页源代码”,重点看
<title>和<meta name="description">是否正常出现,正文内容是否已渲染。如果只看到<div id="app"></div>,说明爬虫看到的也是这个——立刻在vite.config.ts或vue.config.js里接入vite-plugin-prerender或prerender-spa-plugin,先给首页、产品页、文章列表页加上预渲染。 - 用 VS Code 打开项目根目录,新建
sitemap.xml,把所有公开路由手动列进去(哪怕先写 5 个),保存后上传到网站根目录,然后去百度资源平台 → “站点管理” → “链接提交” → “Sitemap” 提交。顺便检查robots.txt里有没有误屏蔽/js/或/css/。 - 在百度资源平台 → “抓取诊断” → 输入你的首页 URL,点“抓取”。等结果出来,对比“抓取到的 HTML”和你在浏览器里“查看网页源代码”的内容。如果有差异,就用
curl -A "Baiduspider" https://yourdomain.com在终端里模拟爬虫请求,看看返回的是不是空壳——是的话,回去查 JS 报错、查路由初始化逻辑、查document.write这类危险操作。
做完这三步,不用等下周,明天就能在百度搜索框里搜 site:yourdomain.com 看变化。