2. 开篇:Playwright 到底卡在哪一步?
做自动化测试或者爬虫的朋友,应该都对 Playwright 不陌生。它的定位很直接:一套 API 同时驱动 Chromium、Firefox、WebKit 三大浏览器内核,录制脚本、自动等待、多标签页管理这些能力开箱即用,比早期的 Selenium 方案省心不少。但有个问题始终绕不过去——在国内环境安装 Playwright,下载速度是真的难受。很多人卡在pip install playwright或者npm i playwright这一步,进度条半天不动,最后超时失败。
我最初接触这个项目时,装一次 Playwright 前后折腾了一个多小时,失败三次才成功。后来把整个安装链路拆开看了一遍,发现慢的原因其实很清晰:第一层是 Python 包或 npm 包本身的下载,第二层是安装后还要执行playwright install去拉取浏览器内核,这两个环节都依赖网络源,只要其中一个慢,整体安装体验就会非常差。
这篇东西就是把我后来沉淀下来的一整套提速方案整理出来,从镜像源配置讲起,最后落到一个一键安装脚本上。目标很明确:让一条干净的命令在几分钟内跑完整个安装流程,不管你用的是 pip 还是 npm,都能直接抄作业。适合正在被 Playwright 安装问题折磨的测试开发、爬虫工程师,以及刚入门自动化测试但还不熟悉国内环境加速套路的朋友。
3. 拆解安装链路:为什么慢,慢在哪里
3.1 安装分几个阶段,每个阶段吃什么网络资源
Playwright 的安装不是一条命令就完事的,它内部明显分为两个大阶段。第一个阶段是运行时核心库的安装,比如pip install playwright拉取的其实只是 Python 客户端代码和内置的 driver,这个包本身不算特别大,但依赖有些零碎。第二个阶段才是大头,执行playwright install chromium或者不指定浏览器直接全装,此时会去下载对应浏览器内核的压缩包。以 Chromium 为例,单个包体积通常在 150MB 到 200MB 之间,Firefox 和 WebKit 也类似,三个浏览器全装合计超过 500MB 很常见。
问题在于,这两个阶段的下载源默认都指向国外服务器。Python 包默认走 PyPI,npm 包默认走官方 registry,浏览器二进制则是从 Playwright 自己维护的 CDN 地址拉取。国内访问这些源的时候,延迟高、带宽不稳定,有时候甚至直接连接重置。尤其是浏览器二进制这一步,经常出现下载到一半断掉的情况,而 Playwright 的下载逻辑遇到中断后不会自动续传,只会报错让你重试,体验非常糟糕。
还有一个很多人忽略的阶段是系统依赖安装。在 Linux 环境下,执行playwright install --with-deps会调用系统的包管理器去安装一堆动态链接库,比如 libnss3、libatk 这类。如果是 Debian 系,会用 apt-get;如果是 Fedora 系,会用 dnf。这一步同样走系统软件源,如果你的服务器软件源没配置成国内镜像,光装依赖也可能卡半天。
3.2 提速的核心逻辑:把三个下载源全部换掉
理解了阶段划分后,提速方案其实就一句话:把 Python 包下载源、npm 包下载源、浏览器内核下载源全部替换为国内可稳定访问的镜像地址。这三者缺一不可,只换其中一两个,安装过程还是会卡在最慢的那个环节上。
先说 Python 包这一层。针对 pip,最通用的做法是配置清华源或者阿里源。我个人推清华源多一点,因为它同步频率高、稳定性好,而且对 Playwright 这种更新节奏快的库比较友好。配置方式有两种:一种是临时指定-i参数,另一种是写进全局配置文件。临时参数适合一次性操作,全局配置则适合长期使用,后者更省事。
npm 这边类似,用淘宝镜像(registry.npmmirror.com)是主流选项。npm 的镜像切换也是两种方式:命令行临时指定,或者用.npmrc文件固定。
浏览器二进制这一层是 Playwright 特有的。Playwright 默认下载浏览器时,会从一个固定的 CDN 地址拼接版本号和文件名后发起请求。它提供了环境变量PLAYWRIGHT_DOWNLOAD_HOST来覆盖这个基础地址,国内一般使用https://npmmirror.com/mirrors/playwright/这个镜像路径。设置好这个环境变量后,再执行playwright install,下载速度会有质的提升。
提示:
PLAYWRIGHT_DOWNLOAD_HOST环境变量是 Playwright 官方支持的配置项,不是第三方 hack,所以不用担心版本升级后失效的问题。它从很早期的版本就存在,目前所有主流版本都兼容。
把这三层逻辑理清楚之后,剩下的就是怎么优雅地把它们组合起来,写成一个别人拿到就能用的安装脚本。
4. 镜像配置实操:pip、npm、系统源三步走
4.1 pip 源配置:从临时参数到全局配置
先看最常见的情况,用 pip 安装 Python 版的 Playwright。如果你只是临时想装一次,可以直接带上-i参数:
pip install playwright -i https://pypi.tuna.tsinghua.edu.cn/simple这样做的问题是,后续如果你要升级 Playwright,或者装其他比较冷门的库,还得再手动带一次参数,很容易忘。而且有些工具链在内部调 pip 时并不会继承你这个命令行参数,就会重新落入默认源。所以我一般建议直接把源写进 pip 配置。
Linux 环境下,pip 的配置文件在~/.pip/pip.conf或者~/.config/pip/pip.conf,不同版本有点差异。一个比较保险的做法是手动创建~/.pip/pip.conf文件,写入以下内容:
[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple trusted-host = pypi.tuna.tsinghua.edu.cnWindows 环境下,配置文件的路径通常是%APPDATA%\pip\pip.ini,内容是一样的。这里有个细节:trusted-host这行不是必需的,因为清华源本身有有效的 HTTPS 证书,但如果你所在的环境有额外的证书代理,或者你用的是一个非标准端口的内部镜像,加上trusted-host可以减少一些麻烦。
配置完成之后,试着执行一遍pip config list或者直接再日常安装一个小库验证下。如果输出里的index-url变成了清华源的地址,就说明配置生效了。
4.2 npm 源切换:临时指定与持久化配置
Node.js 生态的 Playwright 安装路径是另一条独立链路。基础的安装命令是:
npm i playwright如果没配置镜像,npm 会直接去官方 registry 拉包。Playwright 的 npm 包加了一堆依赖,虽然不至于像浏览器二进制那么大,但下载速度依然受制于网络状况。临时指定镜像的命令是这样:
npm i playwright --registry=https://registry.npmmirror.com持久化配置则可以直接改项目根目录的.npmrc文件,写入:
registry=https://registry.npmmirror.com如果你有多个项目要管理,也可以在用户主目录下维护一个全局.npmrc,作用域更广。这里提醒一句:.npmrc的配置优先级是项目级大于用户级大于全局级,所以如果你发现某个项目安装还是走了官方源,先看看项目目录下有没有一个写错内容的.npmrc把它覆盖了。
npm 包安装完成之后,同样需要执行npx playwright install来下载浏览器内核。这一步的环境变量配置方式和 pip 版完全一致,后面单独说。
4.3 系统依赖源:Linux 用户别漏掉这一层
前面提到过,Linux 环境下如果执行playwright install --with-deps,会触发系统包管理器的依赖安装。以 Ubuntu/Debian 系为例,它会调apt-get update然后安装一批包。如果你的/etc/apt/sources.list里还是默认的官方源,那apt-get update的速度就基本取决于你对官方源的实际访问速度。
我个人的习惯是先把 apt 源换成清华源或者阿里源。Ubuntu 的源配置文件在/etc/apt/sources.list,也有新版系统拆分成/etc/apt/sources.list.d/下的独立文件。换源有一个易踩的坑:不同 Ubuntu 版本对应的源地址格式略有差异,不能随便抄一个就上去。建议去清华源网站找到对应版本号的配置,复制后替换。替换完成后一定要执行:
sudo apt-get update确认输出里没有报错再继续。如果apt-get update就失败,那后续的依赖安装一定会失败,所以这一步其实是整个 Playwright 安装流程的前置条件,别等脚本跑挂了才回头排查。
CentOS/RHEL 系则是配置/etc/yum.repos.d/下的 repo 文件,把mirrorlist和baseurl替换成国内地址,这里不展开细说,用 CentOS 的朋友应该已经有一套自己的习惯了。
5. 浏览器内核下载提速:环境变量与验证方法
5.1 设置 PLAYWRIGHT_DOWNLOAD_HOST 的正确姿势
浏览器内核下载是整个过程里最耗时的部分,也是提速效果最明显的地方。Playwright 官方支持通过环境变量自定义浏览器下载地址,核心就是PLAYWRIGHT_DOWNLOAD_HOST。
最基本的用法是每次安装前在当前 shell 窗口导出生效:
export PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ playwright install如果你用的是 Windows 的 PowerShell:
$env:PLAYWRIGHT_DOWNLOAD_HOST="https://npmmirror.com/mirrors/playwright/" playwright install但这种方式有个问题:环境变量只在当前会话里有效,换一个终端窗口又得重新设置。所以更好的做法是把它写进 shell 的配置文件。bash 用户写到~/.bashrc,zsh 用户写到~/.zshrc,在文件末尾追加:
export PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/之后每次打开终端,这个变量就自动生效了。
5.2 镜像地址的选择:一个就够了,别患选择困难症
浏览器内核镜像地址现在其实不少,常见的有https://npmmirror.com/mirrors/playwright/和https://playwright.azureedge.net(这个不是国内镜像,不推荐)。还有一个思路是可以用PLAYWRIGHT_DOWNLOAD_HOST指向你自己的内网服务器或者对象存储,如果你所在团队有统一的二进制分发平台,完全可以把自己下载好的浏览器包传到内网,然后把环境变量指向内网地址。这样团队成员安装时完全不走外网,速度最快。
但个人使用或小团队场景下,我实测下来 npmmirror 的 Playwright 镜像已经足够稳定,没必要自己再包一层服务。它的目录结构和 Playwright 官方 CDN 保持一致,路径拼接规则也兼容,直接替换主机名就行。
这里要说清楚一个细节:设置环境变量只需指定到镜像的根路径,不要在后面多拼具体的浏览器版本路径。Playwright 内部会自己去拼接chromium-XXXXX.zip这样的文件名。如果你多写了一段路径,反而会导致 404。
5.3 验证下载源是否生效:从日志里找线索
设置完环境变量之后,怎么确认配置真的生效了?最简单的方法是看 Playwright 的下载日志。执行playwright install chromium时,正常输出会显示当前正在从哪个 URL 下载文件。如果你看到类似Downloading from https://npmmirror.com/mirrors/playwright/...这样的输出,就说明环境变量已经生效。
如果输出显示的仍然是官方 CDN 地址,那说明环境变量没被正确读取。这时候依次检查三件事:第一,环境变量名是否拼写正确(PLAYWRIGHT_DOWNLOAD_HOST,注意是DOWNLOAD_HOST不是DOWNLOAD_URL,我见过多次拼错的情况);第二,当前 shell 是否执行过source ~/.bashrc或重启过终端;第三,是否有代理工具在拦截环境变量。
5.4 pnpm 和 yarn 用户怎么处理
现在很多前端项目用 pnpm 而不是 npm,Playwright 的安装命令也对应变化,但环境变量的逻辑不变。pnpm 安装 Playwright 包:
pnpm add playwright然后:
npx playwright install这里npx playwright install其实就是在调用 Playwright CLI 做浏览器下载,和 npm 安装后执行的命令是同一个。yarn 用户也一样,核心的浏览器下载逻辑都封装在 Playwright CLI 内部,只要你设置了PLAYWRIGHT_DOWNLOAD_HOST,它就会读取。
有个别版本在 pnpm 下遇到过一个奇怪问题:浏览器二进制下载完后还会做一次完整性校验,如果校验失败会重试,但重试的时候没有再次读取环境变量,导致重复失败。这种情况通常发生在早期版本,后来的版本已经修掉了。如果还有人遇到,最简单的绕法是把PLAYWRIGHT_DOWNLOAD_HOST同时写进系统的全局环境变量和项目的.env文件,双保险。
6. 一键安装脚本:从零到能跑测试的完整流程
6.1 脚本设计思路:先探测环境,再分步安装
有了前面这些配置,手动安装已经很顺畅了,但人总会犯错,而且团队里每个人都要手动配置一遍也很烦。所以更有价值的部分是把它封装成一个一键脚本。脚本的目标是在一台几乎全新的机器上,一条命令完成所有安装工作,最终能够顺利运行 Playwright 的示例测试。
设计脚本时我遵循了几个原则。第一,幂等性,脚本可以重复执行,不会因为之前跑过一半而报错。第二,尽量少干扰系统,不擅自修改系统级的配置文件,改为在项目目录或用户级别写配置。第三,失败要可识别,每步结束都检查退出码,有问题就停下来输出提示,而不是黑盒式地继续。
脚本语言我选择 bash,因为它在绝大多数 Linux 环境里开箱即用,macOS 也默认带了 bash。Windows 环境则可以单独写一个 PowerShell 版本,或者建议直接使用 WSL 后在 Linux 环境里跑这个 bash 脚本,这样适配性更好。
6.2 核心实现:环境变量、pip/npm 双支持、浏览器下载
看一段完整的 bash 脚本核心部分:
#!/usr/bin/env bash set -e PYPI_MIRROR="https://pypi.tuna.tsinghua.edu.cn/simple" NPM_MIRROR="https://registry.npmmirror.com" PLAYWRIGHT_CDN_MIRROR="https://npmmirror.com/mirrors/playwright/" export PLAYWRIGHT_DOWNLOAD_HOST="$PLAYWRIGHT_CDN_MIRROR" echo ">>> 配置 pip 源" mkdir -p ~/.pip cat > ~/.pip/pip.conf <<EOF [global] index-url = $PYPI_MIRROR trusted-host = pypi.tuna.tsinghua.edu.cn EOF echo ">>> 配置 npm 源" if [ -f ".npmrc" ]; then echo "" >> .npmrc echo "registry=$NPM_MIRROR" >> .npmrc else echo "registry=$NPM_MIRROR" > .npmrc fi echo ">>> 安装 Python 版 Playwright" if command -v pip3 >/dev/null 2>&1; then pip3 install playwright elif command -v pip >/dev/null 2>&1; then pip install playwright else echo "未找到 pip/pip3,请先安装 Python" exit 1 fi echo ">>> 安装 Node 版 Playwright(可选)" if command -v npm >/dev/null 2>&1; then npm install playwright --registry=$NPM_MIRROR fi echo ">>> 安装浏览器内核" if command -v playwright >/dev/null 2>&1; then playwright install chromium else python3 -m playwright install chromium fi echo ">>> 完成"这段脚本有几个细节值得展开。首先,set -e让脚本在任意一条命令返回非零退出码时立即停止,避免后面继续跑一些没有意义的内容。其次,pip 源配置直接写入了~/.pip/pip.conf,这一层是用户级的,不需要 sudo,但要注意如果之前已经存在这个文件且里面有自定义配置,直接用cat >覆盖可能把原有内容冲掉。更保险的写法是先备份再覆盖。
npm 源配置写到了项目级.npmrc,这样只影响当前项目,不会全局干扰。但如果你这台机器上所有项目都用默认官方源也没配过其他私有源,直接写到用户级~/.npmrc也完全可以,看使用习惯。
6.3 只装 Chromium 还是全装?合理取舍
脚本里我只写了playwright install chromium,没有把三个浏览器全部装齐。这是因为在实际的自动化测试和爬虫场景里,Chromium 占据了绝大多数用例,Firefox 和 WebKit 更多用在浏览器兼容性测试里。全部安装意味着要多下载两三百 MB 的包,很多场景根本用不上,平白增加安装时间和磁盘占用。
如果你的需求确实需要多浏览器内核,把脚本里的安装命令展开成:
playwright install chromium firefox webkit就可以了。但注意,WebKit 的包体积不小,而且它在 Linux 下编译得比较晚,版本更新也慢一些,如果不是明确要做 WebKit 内核的测试,我通常不建议主动装。
在 macOS 上,playwright install webkit有时候还会遇到一些系统权限问题,比如需要额外的开发者工具组件。这种问题不会在安装脚本层面体现,而是第一次启动 WebKit 环境时报错,属于另一个维度的坑了。
6.4 脚本的可扩展性:参数化与超时控制
上面这个基础脚本可以进一步优化。比如允许通过命令行参数传入浏览器列表:
./install_playwright.sh firefox webkit实现方式是在脚本里用$@接收额外参数,如果没有传就默认chromium。还可以加超时控制,防止某个下载任务长时间卡住不报错也不完成:
timeout 180 curl -fsSL -o /dev/null -w "%{http_code}" "$PLAYWRIGHT_DOWNLOAD_HOST" || { echo "无法访问镜像地址,请检查网络" exit 1 }这个探测请求放在脚本的最前面比较合适,提前发现网络不通,比跑到一半才发现有意义得多。
6.5 Docker 场景下的一键化思路
如果你的 Playwright 跑在容器里,那脚本的思路要稍微变一下。用 Docker 官方镜像mcr.microsoft.com/playwright的话,其实镜像内部已经帮你装好了浏览器和系统依赖,你只需要在构建自己镜像时设置环境变量即可。但国内拉取mcr.microsoft.com的镜像速度可能也比较感人。
解决办法是在 Dockerfile 里直接设置 Playwright 的下载镜像,并用 apt 镜像源来装系统依赖:
FROM ubuntu:22.04 ENV PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \ apt-get update && apt-get install -y python3 python3-pip RUN pip3 install playwright -i https://pypi.tuna.tsinghua.edu.cn/simple && \ playwright install --with-deps chromium这样构建镜像的过程就会快不少,但注意在 Dockerfile 里设置ENV PLAYWRIGHT_DOWNLOAD_HOST之后,如果后续执行playwright install命令,环境变量一定会被读取,这个方式比在 base image 里预先下载更可控一些。
7. 常见问题与排查技巧实录
7.1 下载进度条卡住不动,是不是要等?
这个是新手最容易陷入的困境。Playwright 下载浏览器的时候,进度条有时候长时间停在某几个百分比上,但网络其实还在跑,只是显示不够平滑。我的经验是:先观察五分钟,如果五分钟内进度条一点没动,而且某一侧的网卡流量统计也完全没有变化,基本可以判断当前离线了。
这时候直接开新的安装命令前,先不要急着重试。因为 Playwright 每次下载失败重试都是从头开始的,不会续传。正确做法是,检查当前是否有人正在下载或者是代理策略有问题,然后确认镜像地址可访问后重新执行。实测中如果网络环境能稳定访问 npmmirror 镜像,这个卡住的情况会变得极少。
7.2 浏览器下载成功,但启动时报缺少系统库
这个问题的表现是:playwright install看上去一切正常,浏览器文件也完整下载了,但真正执行playwright.launch()时,弹出类似error while loading shared libraries: libnss3.so这样的错误。
原因是浏览器内核是编译好的二进制文件,运行它需要系统里有一堆动态链接库。解决办法是使用 Playwright 自带的依赖检测能力:
playwright install-deps它会自动对照当前系统的包管理器和 Playwright 要求的依赖列表,把缺失的库补装完。在 Ubuntu 上这个命令会执行一系列apt-get install,在 Fedora 上则调用dnf install。如果你的网络源没换,这步可能也会慢,所以前面说的系统源配置要提前做好。
7.3 公司内网环境,有代理但证书不信任
公司内网开发环境下,很多人会配置 HTTP 代理来访问外网。Playwright 下载浏览器时,走的还是常规的 HTTP/HTTPS 请求,所以它会透传HTTP_PROXY、HTTPS_PROXY这些标准环境变量。但有个隐患:如果你的代理服务器是自签证书的,而系统根证书里没有它,那 HTTPS 请求会直接失败。
这时候不要尝试去信任一个你拿不准的证书再说,先明确一个范围:命令行工具下载阶段受代理影响大,浏览器内核运行阶段通常不受影响。如果你只想解决安装问题,可以设置环境变量跳过验证:
export NODE_TLS_REJECT_UNAUTHORIZED=0但这是开发调试用的临时手段,不建议长期使用。更稳妥的做法是把公司的根证书加入系统信任链,这个可以让运维的同事给一份统一说明。
7.4 pip 安装成功但命令行找不到 playwright
如果你用 pip 安装的是用户级而不是系统级(比如加了--user参数),那么playwright这个命令行工具可能不在 PATH 里。这时候要么直接把安装路径找出来配置到 PATH,要么干脆不依赖命令行,改成 Python 代码里调用:
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() page.goto("https://example.com") print(page.title()) browser.close()这段代码不依赖命令行,只要 Python 能 import playwright 成功,就能正常跑。所以很多时候我并不强求playwright命令在 PATH 里,能跑代码就行。
7.5 如何确认所有浏览器都下载完整
如果下载过程中有某一次失败,之后重新执行playwright install可能会发现它跳过了之前已经下载成功的部分。Playwright 的机制是,浏览器内核解压后放在用户缓存目录(Linux 在~/.cache/ms-playwright/下),下次安装时检测到这个目录里有对应版本的文件,就会认为这一步已完成,直接跳过。
所以如果你想彻查所有浏览器文件是否完整,就去看这个缓存目录。里面每个子目录对应一个浏览器,进入子目录后会有对应的可执行文件。用 Chromium 的话,通常能看到chrome-linux/chrome这样的路径。如果某个浏览器目录存在但里面文件明显不完整,删掉这个目录再重新执行对应的playwright install [browser]就好。
这个缓存目录的位置可以通过环境变量PLAYWRIGHT_BROWSERS_PATH修改,如果你想把浏览器内核下载到项目目录里一起打包分发,这个环境变量是很好的工具。只是注意,修改路径后,Playwright 会认为新的路径是唯一存储位置,旧的缓存就不再管了。
8. 收尾:我的实际经验和一点额外建议
整套方案用下来,我最直观的感受是:Playwright 在国内环境安装的痛点,本质上不是工具本身的问题,而是网络链路上多个环节没有一个统一的加速方案。单独看每一步,改镜像、设环境变量都不复杂,但如果你不系统化地处理,就会反复在某个随机环节上卡住。
我个人的习惯是,在新机器上部署 Playwright 之前,先把三样东西准备好:pip 的全局镜像配置、npm 的 registry 配置、以及PLAYWRIGHT_DOWNLOAD_HOST的导出。这三样配齐后,安装流程几乎不会遇到什么意外。然后才是跑脚本。
说到脚本,再补充一个实际操作中用得比较多的小技巧:脚本里加一个自动写入环境变量的步骤。虽然前面建议把环境变量写进.bashrc,但有些团队会用别的 shell,或者在 CI 环境里根本不会加载.bashrc。所以最开始那一版脚本里我用的是export指令,这只对当前会话有效。如果你面向的是 CI 场景,更合适的方式是直接在任务的全局环境变量里配置好PLAYWRIGHT_DOWNLOAD_HOST,或者把导出语句写进 CI 平台的预构建命令里。
另外关于浏览器内核的版本匹配问题也要小心。Playwright 升级版本后,通常会对应新的浏览器内核版本,如果你只升级了 Python 包但没有重新执行playwright install,运行时可能会提示浏览器内核版本不匹配。版本不一致时,Playwright 会明确报错,不会默默跑起来,所以这一点不用太担心,只要记得升级后手动跑一次安装脚本就行。反过来说,如果你想固定整个环境,建议在项目里锁住 Playwright 版本,避免偶尔的自动升级打乱浏览器内核的对应关系。
最后分享一个我在多人协作项目里的做法:把整个安装脚本纳入项目仓库,不放在个人目录下。新同事入职或者换新机器时,一条命令拉完代码再跑一次脚本,所有环境就绪。过程中如果有人遇到问题,更新脚本本身也比各自口头传递经验要可靠得多。脚本里所有镜像地址都抽成了变量,以后如果某个镜像地址失效了,改一行就能全局替换,维护成本很低。
Playwright 本身是个好工具,安装这一关跨过去后,后面编写用例、调试脚本的过程会顺很多。希望这套镜像配置加一键脚本的思路,能帮你少走一些我当年走过的弯路。