Playwright抓取SSR页面需等待hydration完成而非禁用JS;应使用page.wait_for_function检测window.__NEXT_DATA__等框架状态,避免依赖time.sleep或仅等待元素出现。

Playwright 本身不区分 SSR 还是 CSR,它操作的是最终渲染完成的 DOM —— 所以“抓取 SSR 页面”这件事,本质上只是确保页面内容已加载完毕再提取,而不是等 JS 渲染。关键在等待策略,不是渲染模式。
为什么 page.goto() 后不能直接 page.content()
SSR 页面虽然服务端已输出 HTML,但前端框架(如 Next.js、Nuxt)常会注入 hydration 脚本,在客户端重新挂载或补全数据(比如用户头像、实时价格)。此时 page.content() 返回的是初始 SSR HTML,可能不含这些动态补全的内容。
- 现象:抓到的 HTML 里有
<div id="app"></div>却没内容,或关键字段为空 - 原因:JS 尚未执行完 hydration,或异步数据请求还没返回
- 错误做法:用
time.sleep(2)—— 不可靠,慢且易断
用 page.wait_for_function() 等待 hydration 完成
比 wait_for_selector 更精准:直接检测框架是否完成挂载。例如 Next.js 会在全局挂载 window.__NEXT_DATA__,Vue SSR 应用常暴露 window.__NUXT__ 或 window.$nuxt。
await page.goto("https://example.com")
await page.wait_for_function("window.__NEXT_DATA__ !== undefined && window.__NEXT_DATA__.props")
# 或针对 Vue: await page.wait_for_function("window.__NUXT__ && window.__NUXT__.state")
- 这个检查既快又语义明确:不是等某个元素出现,而是等框架状态就绪
- 避免误判:比如某个 loading 元素消失 ≠ 数据已填入,但
__NEXT_DATA__.props存在基本意味着 hydration 成功 - 注意:某些部署会移除
__NEXT_DATA__(如启用了hybrid模式或自定义序列化),此时需退回到监听关键业务元素
抓取时要不要禁用 JavaScript?
不要禁用。SSR 页面的交互、懒加载、分析脚本、甚至部分核心内容(如评论区、推荐列表)都依赖 JS 执行。禁用后你拿到的只是骨架 HTML,和 curl 效果差不多。
立即学习“Python免费学习笔记(深入)”;
- 验证方式:在 Playwright 的 headed 模式下打开页面,手动禁用 JS,看关键区域是否空白
- 例外情况极少:仅当你明确只需原始 SSR 输出(如 SEO 抓取 meta 标签),才考虑
java_script_enabled=False,但这时其实用requests+ 解析更轻量 - 性能影响可控:Playwright 默认启用 JS,且现代硬件下开销可忽略;相比频繁重试或超时,这是最稳的选择
真正容易被忽略的点是 hydration 和数据请求的先后关系 —— 有些页面先 hydration 再发 API 请求(此时要等接口响应),有些则把数据内联在 __NEXT_DATA__ 里(此时等 hydration 就够了)。得看目标站点实际行为,不能一概而论。


















