Playwright启动卡住或报TimeoutError,主因是服务器缺图形环境或系统依赖(如libnss3等),应优先用headless模式并运行playwright install-deps chromium补全依赖;页面内容未渲染完成需等待关键元素而非仅networkidle;登录态丢失多因page/context未复用;内存暴涨源于频繁新建page/context且未关闭,应复用context并禁用图片加载。

Playwright 启动浏览器时卡住或报 TimeoutError
Playwright 默认启动 Chromium,但很多服务器没装图形环境或缺少依赖库,一运行就卡在 browser_type.launch() 或直接抛 TimeoutError: Timeout 30000ms exceeded。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 先确认是否真需要 GUI:生产环境抓动态页,
headless=True(默认)即可,不用改;但某些反爬会检测无头特征,这时才考虑headless=False+ Xvfb - Linux 服务器必须装系统依赖:
libnss3、libatk1.0-0、libatk-bridge2.0-0等,缺一个都可能静默失败;用playwright install-deps chromium一键补全 - Windows/macOS 开发机若报错,优先重装浏览器二进制:
playwright install chromium,别信 pip 安装的 wheel 包自带浏览器 - 超时不是网络问题,是进程没起来。加
timeout=60000只是掩耳盗铃,得先解决依赖
页面加载完却拿不到渲染后的内容,page.content() 还是原始 HTML
Playwright 的 page.goto() 默认只等 DOMContentLoaded,但 React/Vue 页面真正数据往往靠 JS 异步拉取、再 patch 到 DOM,这时候 DOM 就绪了,内容还没出来。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 别只靠
wait_for_load_state="networkidle":它只等 fetch/XHR 静默,不保证 JS 渲染完成;对 SPA 更稳妥的是等某个关键元素出现:page.wait_for_selector("article.item", timeout=10000) -
page.content()拿到的是当前 DOM 快照,但有些内容是滚动触发动态加载的;得先page.evaluate("window.scrollTo(0, document.body.scrollHeight)"),再等新节点 - 避免用
time.sleep()硬等:既慢又不可靠;用page.wait_for_function()检查 JS 变量状态更准,比如page.wait_for_function("window.__DATA_READY__ === true")
登录后 Cookie 不自动带入后续请求,page.goto() 返回 403 或跳回登录页
Playwright 的 page 实例本身维护完整浏览器上下文,包括 Cookie、LocalStorage、SessionStorage —— 但前提是所有操作都在同一个 context 和 page 内完成。很多人分多个 page 或新建 context 导致状态丢失。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 登录操作和后续抓取必须用同一个
page(或至少同一个context);不要context.new_page()新开一页去访问目标 URL - 检查是否误用了
page.goto(url, wait_until="commit"):这个模式不等 JS 执行,可能导致登录态校验 JS 没跑完就被跳转;改用wait_until="networkidle"或显式等登录成功标志 - 有些网站把登录态存在
httpOnly Cookie里,Playwright 能自动携带,但如果你手动调了page.route()拦截请求并改了 headers,就可能把 Cookie 给删了
抓取速度慢、内存涨得快,跑几小时后 OOM 或变卡
Playwright 是真实浏览器,每个 page 占几十 MB 内存,频繁创建销毁 page/context,又不显式关闭,很容易撑爆内存。尤其用 for 循环抓一堆 URL 时最明显。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 复用
context:一个 context 可开多个 page,比反复browser.new_context()轻量得多;用完page.close(),但别急着关 context,留着下一轮用 - 禁用图片/字体加载省资源:
context = browser.new_context(**{"bypass_csp": True, "ignore_https_errors": True})不够,得加context.route("**/*.{png,jpg,gif,woff,woff2}", lambda route: route.abort()) - 关闭不需要的 API:
page.remove_listener("console", handler)如果你之前监听过 console 日志,记得清理;否则日志堆积也会吃内存 - 别在循环里
playwright.start()/browser.close():整个脚本生命周期只启停一次浏览器
动态页面抓取真正的复杂点不在代码怎么写,而在你得时刻分辨:当前看到的“空白”或“403”,到底是 JS 没执行完、Cookie 没传对、还是浏览器指纹被识破了——这些没法靠一个参数解决,得看 network 面板、console 输出、甚至对比手动操作时的 DOM 变化。


















