
用过 Playwright 的人都知道这框架写自动化脚本是真的爽一套 API 同时管 Chromium、Firefox、WebKit还能录屏、拦截请求、生成 PDF。但第一次装 Playwright 的时候不少人都被同一个坑拦住pip install playwright几秒钟就装完了跑playwright install chromium却发现进度条死活不动要么卡在 Connecting要么直接 timeout最后只能干瞪眼。原因大家心里都有数浏览器二进制文件放在国外的 CDN 上国内直连下载速度慢到让人怀疑人生。这篇文章专门解决这个问题不折腾代理、不碰奇怪的工具就用最普通的下载方式把 Chromium 装好顺便把换源、环境变量、版本对应这些坑一次说清。无论你是做爬虫、自动化测试还是想在 AI 工具里集成浏览器操作这套手动安装流程都适用。1. 为什么 Playwright 会卡在下载这一步1.1 Playwright 的安装逻辑装框架为什么还要下浏览器先说清楚一件事pip install playwright装到的是 Python 包和驱动本质上是 Playwright 对外暴露的 API 层。真正执行页面渲染、模拟点击、处理 JavaScript 的是它内置的一个浏览器驱动进程而这个驱动进程必须配合一个真实的浏览器二进制才能工作。Playwright 的设计哲学是开箱即用所以官方在安装流程里加了一步浏览器下载。Python 环境里装完包之后需要手动执行playwright install这个命令会去官方 CDN 拉取浏览器压缩包然后解压到本机缓存目录。问题恰恰出在这里官方 CDN 用的 Azure 节点走国际链路下载一个一百多兆的 Chromium 压缩包速度经常只有几十 KB/s甚至直接连接超时。我记得最早用的时候还天真地等过一次进度条卡了二十多分钟最后报了个 timeout心态直接炸了。后来才意识到这不是代码问题就是网络链路问题。理解了这一点思路就清楚了既然直接下载慢那就换一个国内能跑满带宽的方式把浏览器包弄到本地。1.2 加速思路三种方案的对比与取舍网上关于 Playwright 安装加速的方案大致有三种这里先做个对比后面实操部分会展开讲前两种。方案核心思路优点缺点适用场景方案 A换国内镜像源设置PLAYWRIGHT_DOWNLOAD_HOST环境变量指向 npmmirror 镜像命令简单一条命令搞定镜像同步有延迟个别版本可能缺失大多数常规安装场景方案 B手动下载压缩包先获取精确下载链接用浏览器或下载器拉文件再解压到指定目录可控性最强不依赖镜像的完整性步骤多需要手动管目录结构镜像源缺文件、公司内网隔离、需要离线安装方案 C复用系统已装的 Chrome代码里通过channelchrome指定用系统浏览器完全不用下载 Chromium磁盘省几百兆依赖系统安装的 Chrome/Edge 版本本机已有 Chrome不想重复下载我个人推荐把方案 A 和方案 B 配合使用先尝试镜像源如果镜像缺版本或者网络还是不稳就直接走手动下载。方案 C 适合对浏览器版本不敏感、不想管额外磁盘占用的朋友但做爬虫和自动化测试的话我建议还是老老实实把 Chromium 装好因为 Playwright 对自家浏览器的兼容性是经过完整测试的稳定性比系统 Chrome 更可控。2. 动手前先搞懂三个关键概念2.1 PLAYWRIGHT_BROWSERS_PATH浏览器目录到底在哪Playwright 下载完浏览器之后会把它放到一个缓存目录。默认情况下Linux 和 macOS 是~/.cache/ms-playwrightWindows 是%USERPROFILE%\AppData\Local\ms-playwright。关键点是这个目录路径完全可以通过环境变量PLAYWRIGHT_BROWSERS_PATH来改变。一旦设置了它Playwright 查找浏览器时只认这个路径不会再去看默认目录。这个变量有两个高频用法一是把浏览器目录放到一个可控的位置方便管理。比如公司有统一开发机你把PLAYWRIGHT_BROWSERS_PATH设成/data/ms-playwright这台机器上所有项目都能共用同一份浏览器文件不用每个虚拟环境都下一遍。二是设置成0。这是 Playwright 提供的一个特殊值效果是把浏览器装到当前包的内部目录。这样做的好处是项目相对独立坏处是每个虚拟环境各存一份磁盘占用翻好几倍我一般不怎么推荐。需要特别提醒的是如果你手动解压浏览器到某个目录千万别忘了把环境变量同步设置过去。很多人辛辛苦苦把浏览器下好、解压好结果 Playwright 找不到报Executable doesnt exist就是因为环境变量没设置或者路径不一致。2.2 PLAYWRIGHT_DOWNLOAD_HOST换源的核心配置这个环境变量是整套加速方案的关键。它的作用是把 Playwright 下载浏览器时的基础 URL 替换成你指定的地址。官方的浏览器下载地址长这样https://cdn.playwright.dev/dbazure/download/playwright/builds/chromium/revision/chromium-linux.zip其中revision是 Chromium 的构建版本号后面实操部分会讲怎么拿到。我们需要做的就是把域名部分替换成国内镜像。目前最常用的是 npmmirror 的镜像https://npmmirror.com/mirrors/playwright/builds/chromium/revision/chromium-linux.zip通过下面的命令Playwright 在下载浏览器时会自动拼接镜像地址# Linux / macOS export PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright/ # Windows PowerShell $env:PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright/注意这个镜像本身是个静态文件托管不走 npm 的 registry 配置。有些文章让你配.npmrc或npm config set registry那只能解决 npm 包的下载解决不了浏览器二进制的下载别搞混了。2.3 revision 与 browsers.json版本对不上就是白搭Playwright 包内部有一个配置文件叫browsers.json它定义了当前版本的 Playwright 需要哪个版本的浏览器。里面记录了浏览器名称、平台、下载路径模板和 revision 号。revision 是一个纯数字编号每个版本的 Chromium 构建包都有一个唯一的 revision。比如某个版本的 Playwright 对应的 Chromium revision 可能是 1148下载 URL 里的revision就是这个数字解压后的目录名也会带这个数字比如chromium-1148。这里必须强调一个容易踩的坑Playwright 版本和浏览器 revision 是严格绑定的。你不能用 A 版本的浏览器包喂 B 版本的 Playwright轻则报路径错误重则启动时直接失败。所以每次操作之前第一步永远是确认当前项目的 Playwright 版本和对应的 revision而不是盲目下载最新的浏览器包。3. 手动安装 Chromium 的完整实操流程3.1 先锁定版本拿到精确的下载链接无论走哪条路第一步都是确认当前环境的 Playwright 版本。# 查看 Python 环境里的 Playwright 版本 pip show playwright | grep -i version # 或者运行一段小代码 python -c import playwright; print(playwright.__version__)确认版本后用--dry-run参数获取精确的下载信息。这个命令不会真正下载文件只是把浏览器下载计划打印出来非常实用。playwright install --dry-run chromium输出内容大致长这样browser: chromium version 1148 (revision: 1148) Install location: /home/user/.cache/ms-playwright/chromium-1148 Download url: https://cdn.playwright.dev/dbazure/download/playwright/builds/chromium/1148/chromium-linux.zip拿到两个关键信息一个是Download url这是官方链接另一个是Install location这是浏览器最终要放的位置。如果后面准备手动下载就直接复制这个 URL 去下载保证版本不会错。还有一个隐藏信息在browsers.json里。如果想知道当前 Playwright 到底支持哪些浏览器平台、有哪些构建变体可以打开这个文件看一眼python -c import pathlib, json; print(json.load(open(str(list(pathlib.Path(__import__(playwright).__file__).parent.joinpath(driver/package/browsers.json)).resolve()))))这个命令有点长平时用--dry-run就够不过了解browsers.json的位置和结构对排查问题有帮助。3.2 下载浏览器包手动下 vs 镜像自动下拿到下载链接之后有两条路可以走。方式一手动下载把Download url里的地址复制到浏览器里直接访问或者扔进迅雷、IDM 之类的下载工具。浏览器会自动开始下载这个 zip 包国内网络环境下用浏览器自带下载通常就能跑满带宽如果还是慢就用支持断点续传的下载工具。下载完成后注意不要急着解压先确认文件的完整性。官方链接会在下载完成后校验 sha256手动下载的话建议自行比对一次哈希值。校验命令如下# Linux sha256sum chromium-linux.zip # macOS shasum -a 256 chromium-linux.zip # Windows PowerShell Get-FileHash chromium-linux.zip -Algorithm SHA256然后对比--dry-run输出里的哈希信息不一致说明文件损坏需要重新下载。方式二镜像源自动下载如果你不想手动管这些直接设置环境变量后执行自动安装export PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright/ playwright install chromium这个方式最省事适合大多数情况。但有一点要注意npmmirror 同步可能有延迟如果你的--dry-run输出里的 revision 比较新镜像上可能还没有对应的目录此时会报 404。遇到这种情况切换到方式一手动下载反而更快。3.3 解压到正确位置目录名一个字都不能错下载完成后进入关键的目录结构环节。先创建目录再把压缩包解压进去。# 以 Linux 为例Windows/macOS 路径对应调整 mkdir -p ~/.cache/ms-playwright/chromium-1148 unzip chromium-linux.zip -d ~/.cache/ms-playwright/chromium-1148/解压完成后检查目录结构。Chromium 压缩包解压后通常是一个chrome-linux文件夹所以最终路径应该是~/.cache/ms-playwright/chromium-1148/chrome-linux/chrome也就是说压缩包里的内容解压到了chromium-1148目录下里面有一个内层目录chrome-linux。如果发现解压后结构不对比如多套了一层目录可以用mv调整# 如果压缩包解压后是 chromium-1148/chrome-linux/xxx但没有在预期位置 cd ~/.cache/ms-playwright mv chromium-1148/chrome-linux ./chromium-1148/chrome-linuxWindows 平台的版本目录名一般是chrome-win或chrome-win64macOS 是chrome-mac原理一样目录名别弄错就行。这里顺便说下新版 Playwright 的一个变化它把无头浏览器headless shell和完整浏览器分开打包了。如果你只跑headlessTrue实际用到的是chromium-headless-shell这个目录如果你要跑headlessFalse用到的是chromium-xxxx完整目录。手动安装时可以顺便把chromium-headless-shell也装上避免后续跑无头模式时又缺文件# 查看 headless shell 的下载信息 playwright install --dry-run chromium-headless-shell3.4 验证安装跑脚本和 codegen 双保险安装完成后赶紧验证一下。最简单的测试是跑一段最小脚本from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(https://www.baidu.com) print(页面标题:, page.title()) browser.close()如果脚本能正常启动浏览器并输出标题说明手动安装成功。如果只打算用无头模式可以把headlessFalse改成headlessTrue验证 headless shell 是否也正常。另一种更直观的验证方式是playwright codegen它启动后会自动打开录制窗口能边点边生成代码。如果窗口能弹出来说明浏览器整体没问题playwright codegen https://example.com验证过程中如果报错先别急着怀疑安装步骤对照下一节的排查清单看看。4. 安装过程中的高频翻车现场与排查技巧4.1 高频报错速查表手动安装最烦的就是报错提示看不懂。下面这些是我见过最多的几种情况整理成速查表按图索骥能省不少时间。报错信息原因解决办法Executable doesnt exist at ...浏览器文件不存在或目录名与当前 Playwright 版本不匹配检查PLAYWRIGHT_BROWSERS_PATH指向的目录里有没有chromium-revision确认 revision 是否和--dry-run输出一致Host platform is not supported当前操作系统/架构没有对应的浏览器构建包确认系统架构x64/arm64或改用channelchrome复用系统浏览器sha256 mismatch下载的压缩包损坏或下载了错误平台的包删掉重下手动下载时核对 sha256注意平台后缀linux/mac/winFailed to launch browser, exit code 127Linux 下缺动态链接库执行playwright install-deps chromium用系统包管理器把依赖补齐ConnectionAbortedError/ timeout网络连接官方 CDN 失败换镜像源或手动下载后解压到指定目录page.goto: net::ERR_CONNECTION_RESET浏览器能启动但请求被断换目标网址测试排除网站侧问题再看系统代理配置Linux 下缺动态链接库这个问题太典型了单独说一下。新版 Playwright 提供了install-deps命令直接执行sudo playwright install-deps chromium这个命令会根据当前系统自动用 apt/yum 把 Chromium 依赖的系统库装好。如果是在公司的内网服务器上可能还要额外配 yum 源或 apt 源否则这个命令也可能因为网络问题失败。4.2 三个排查命令省去瞎猜时间遇到问题时与其瞎猜不如用下面三个命令快速定位。第一个是带调试日志的安装命令DEBUGpw:browser* playwright install chromium在 Linux/macOS 上直接这么跑Windows PowerShell 用$env:DEBUGpw:browser*执行后会打印出每一步的详细日志包括尝试访问的下载地址、校验过程、解压过程。看到真实 URL 后可以直接复制这个地址到浏览器里打开确认是网络问题还是文件缺失问题。第二个是查看已安装浏览器列表playwright install --list这个命令会列出当前 Playwright 认识的所有浏览器类型并标注哪些已经安装、哪些缺失。如果列表里显示chromium还是未安装状态说明你的目录结构或环境变量有问题。第三个是直接检查目录结构# Linux/macOS ls -la ~/.cache/ms-playwright/ # 或者如果设置了自定义路径 ls -la $PLAYWRIGHT_BROWSERS_PATH看目录名和 revision 数字是否匹配。我自己就曾经犯过目录名写错一个字母的低级错误Playwright 找遍整个磁盘也找不到浏览器报错信息还特别长容易带偏思路。用ls直接扫一眼反而最快。5. 装好之后还有这些进阶玩法5.1 多项目共用浏览器缓存不再重复下载很多人的电脑上会同时开好几个 Python 项目每个项目都有自己的虚拟环境。如果每个环境都跑一次playwright install浏览器就会各自存一份磁盘直接爆炸。解决办法就是共用一份浏览器缓存。在 shell 配置里设置环境变量# 写入 ~/.bashrc 或 ~/.zshrc export PLAYWRIGHT_BROWSERS_PATH/data/ms-playwright设置完后所有新项目装好 Playwright 包跑playwright install时只要检测到公共路径下已经有对应 revision 的浏览器就会直接跳过下载。如果你的Playwright版本和已有浏览器版本刚好一样那连下载这一步都省了秒秒钟完成安装。这个思路放在 CI/CD 里同样适用。GitLab Runner 或 Jenkins 上可以先手工装好浏览器挂载到公共路径流水线里就不用每次重新下载了。5.2 复用系统 Chrome 或 Edge磁盘空间立省几百兆如果实在不想下载 Chromium可以考虑直接用系统里已经装好的 Chrome 或 Edge。代码里只需要指定channelfrom playwright.sync_api import sync_playwright with sync_playwright() as p: # 使用系统 Chrome不下载 Chromium browser p.chromium.launch(channelchrome) page browser.new_page() page.goto(https://example.com) print(page.title()) browser.close()支持的 channel 有chrome、chrome-beta、msedge、msedge-beta等。优点显而易见不占额外磁盘空间浏览器版本跟随系统更新。缺点也有比如系统 Chrome 的自动化兼容性没有 Playwright 内置 Chromium 那么稳定操作部分网站时可能因为浏览器指纹差异导致行为异常。如果你的目标是做大规模爬虫我建议还是装内置 Chromium因为内置浏览器和 Playwright 驱动是同步发版的遇到问题好排查。5.3 把浏览器带进 Docker换台机器也不慌做后端服务时经常要把 Playwright 跑在 Docker 容器里。这时候在网络受限的构建环境中下载浏览器是个大难题。推荐的方案是构建时用镜像源安装并把浏览器路径固定到容器内的一个目录FROM python:3.11-slim WORKDIR /app RUN pip install playwright你的版本号 \ PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright/ \ PLAYWRIGHT_BROWSERS_PATH/ms-playwright \ playwright install chromium --with-deps ENV PLAYWRIGHT_BROWSERS_PATH/ms-playwright这样镜像构建时会先设置镜像源再用--with-deps自动安装系统依赖一举两得。注意ENV里也要声明PLAYWRIGHT_BROWSERS_PATH不然运行时还是会去默认目录找浏览器。另一个思路是把宿主机已经下载好的浏览器目录直接 COPY 进镜像FROM python:3.11-slim WORKDIR /app # 宿主机目录/data/ms-playwright COPY /data/ms-playwright /ms-playwright ENV PLAYWRIGHT_BROWSERS_PATH/ms-playwright这种方式适合离线构建构建前先在宿主机把浏览器下载好部署时一步到位。安装完成之后再配合 pytest-playwright、Scrapy Playwright 中间件等工具写爬虫和自动化脚本的体验会顺滑很多。尤其是页面里有大量动态渲染的 iframe 或异步加载的内容时Playwright 比单纯解码页面接口的方式省力太多一句page.frame_locator()就能切进 iframe 拿到内容还能顺手监听页面请求、拦截响应这些都是纯请求库做不到的。我自己第一回手动装的时候也栽了几个跟头镜像源刚同步完缺版本、zip 解压后目录多套一层、headless shell 和完整版搞混全碰上了。后来学乖了每次操作前先跑一次--dry-run把 revision 和安装路径抄下来再决定是走镜像还是手动下载。安装这事真没多少技术含量版本号和路径管理好了剩下的就是下载器的事。最后再分享一个小习惯我会把常用的 Playwright 版本对应的浏览器 zip 包备份一份到本地网盘。哪天换新电脑、装新环境直接解压到路径里连镜像源都不用等整个过程不超过两分钟。这套流程虽然简单但确实能把 Playwright 下载失败 这个经典问题彻底挡在门外。