一次 Playwright + Docker 的踩坑记录
最近在折腾一个小功能。
我想给 QQ 机器人加一个通知:当玩家上线的时候,自动打开 BlueMap 网页,截一张图,然后发到群里面。
本质上就是后台启动浏览器,打开网页,截图,再把图片发出去。
技术上我用的是 Playwright。
它是一个无头浏览器方案,可以在没有图形界面的情况下直接启动 Chromium,然后加载网页、执行脚本、截图,非常适合服务器环境。
本地开发阶段一切正常。
问题基本全部出现在部署到服务器之后。
第一个坑:Playwright 浏览器下载不了
服务器的网络环境非常差。
pip install playwright 装完之后,还需要额外执行:
python -m playwright install chromium
这个命令会下载一整套无头 Chromium,体积非常大。
在国内服务器上:
下载慢
容易失败
Docker build 阶段基本不可用
也就是说,正常在线安装浏览器这条路根本走不通。
我的做法:直接离线拷贝浏览器
我最后用了一个比较投机取巧的方法。
在我的 Linux 笔记本上挂 VPN 先把 Chromium 下载好,然后把整个浏览器目录直接复制出来,放进 Docker 的工作目录里。
文件夹在:/home/[Username]/.cache/ms-playwright/ ,Username 是你的用户名。root 就在 root 的.cache 下
再通过设置 docker 环境变量:
PLAYWRIGHT_BROWSERS_PATH=/workspace/ms-playwright
强制让 Playwright 使用这个离线浏览器。
等于完全跳过在线下载这一步。
第二个坑:浏览器在,但启动不了
结果浏览器文件虽然在,但还是启动失败。
报错基本都是:
error while loading shared libraries: libxxx.so: cannot open shared object file
后来才意识到:
浏览器本体可以离线拷贝,
但 Chromium 依赖的系统动态库还是必须安装。
否则根本跑不起来。
所以即使不执行 playwright install,Docker 里也必须把 Chrome 的运行时依赖补齐。
另外一个问题:⬜字体和中文字体
默认环境没有字体,中文等字符会显示⬜,此外 Linux 下的 Chrome / Playwright 经常会调用到日文字形,截图出来很奇怪。
所以我顺便装了 Noto CJK 字体,并且手动调整字体优先级,让:
SC > TC > JP
这样中文显示就正常了。
最终 Dockerfile
下面就是我最后能稳定运行的完整 Dockerfile。
# =========================================================
# Base: Python slim
# =========================================================
FROM python:3.12-slim
# =========================================================
# 时区
# =========================================================
ENV TZ=Asia/Shanghai
RUN apt update && apt -y install tzdata \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
&& echo $TZ > /etc/timezone
# =========================================================
# 系统依赖 + 浏览器运行库 + 字体
# =========================================================
RUN apt update && apt -y install \
# ===== chromium / chrome 运行时依赖 =====
libnss3 \
libnspr4 \
libxss1 \
libatk-bridge2.0-0 \
libgtk-3-0 \
libasound2 \
libcups2 \
libdrm2 \
libgbm1 \
libx11-xcb1 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
libpangocairo-1.0-0 \
libpango-1.0-0 \
libxshmfence1 \
libglu1-mesa \
ca-certificates \
curl wget gnupg unzip \
\
# ===== 字体(关键) =====
fontconfig \
fonts-noto \
fonts-noto-cjk \
fonts-noto-color-emoji \
fonts-dejavu-core \
fonts-dejavu-extra \
fonts-liberation \
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
# =========================================================
# 中文字体优先级:SC > TC > JP
# 解决 Chrome / Playwright 使用日文字形问题
# =========================================================
RUN mkdir -p /etc/fonts/conf.d && \
printf '%s\n' \
'<?xml version="1.0"?>' \
'<!DOCTYPE fontconfig SYSTEM "fonts.dtd">' \
'<fontconfig>' \
' <alias>' \
' <family>sans-serif</family>' \
' <prefer>' \
' <family>Noto Sans CJK SC</family>' \
' <family>Noto Sans CJK TC</family>' \
' <family>Noto Sans CJK JP</family>' \
' </prefer>' \
' </alias>' \
' <alias>' \
' <family>serif</family>' \
' <prefer>' \
' <family>Noto Serif CJK SC</family>' \
' <family>Noto Serif CJK TC</family>' \
' <family>Noto Serif CJK JP</family>' \
' </prefer>' \
' </alias>' \
'</fontconfig>' \
> /etc/fonts/conf.d/99-cjk-prefer-sc.conf && \
fc-cache -f -v
# =========================================================
# 工作目录
# =========================================================
WORKDIR /workspace
# =========================================================
# pip 国内镜像
# =========================================================
ENV PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
ENV PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn
# =========================================================
# 虚拟环境
# =========================================================
RUN python -m venv /workspace/.venv \
&& /workspace/.venv/bin/pip install --upgrade pip setuptools wheel
ENV PATH="/workspace/.venv/bin:$PATH"
启动命令
最后用下面这条命令作为 dokcer 启动命令启动:
bash -c "PLAYWRIGHT_BROWSERS_PATH=/workspace/ms-playwright ./venv/bin/python app.py"
核心就是指定浏览器路径,让 Playwright 使用离线拷贝的 Chromium。
其实用 docker 环境变量指定也是一样的,我 bash 更熟悉一点,就投机取巧了。
总结
这次踩坑主要就三件事:
国内服务器下载 Chromium 很痛苦
离线浏览器还需要系统动态库
Linux 字体优先级会导致中文异常
最后的做法也很简单:
浏览器离线拷贝 + 手动安装依赖库 + 调整字体。
记录一下,省得以后再踩一次。