Playwright 在 C# 里的玩法,和 Node.js 那套是两码事。不少人在 NuGet 里搜 playwright 包就直接装了,结果项目跑起来就报错——因为压根不是那个东西。C# 这边官方唯一支持的是 Microsoft.Playwright,而且装完包还不算完,浏览器二进制得单独下一遍。这三个坑,我先帮你拆解几个关键点,一个个来看。
安装与初始化:别踩这些常见坑
先说第一步,很多人以为 dotnet add package playwright 能搞定,实际会收到一个找不到包的报错。正确的做法是:
- 运行
dotnet add package Microsoft.Playwright添加引用 - 安装完必须手动执行一次
playwright install chromium(或者换成firefox/webkit),否则当你调用await Playwright.CreateAsync()时,会直接抛出PlaywrightException: Failed to launch browser—— 连浏览器都找不到 - 在 CI/CD 或部署脚本里,推荐加上
playwright install --with-deps,它会把系统级依赖(比如 libglib、libnss)一并装好,避免因为环境缺失导致启动失败 - 如果你用 Docker,基础镜像别选 alpine —— Playwright 不支持 musl,老老实实用
mcr.microsoft.com/dotnet/sdk:8.0-jammy(Ubuntu 22.04)吧
截图黑屏怎么办?
接下来是截图问题。代码写好了,Page.ScreenshotAsync() 也调了,结果返回一个空文件或者黑屏图片。这大概率不是代码逻辑的锅,而是 Chromium 启动时默认走了 headless 模式,但某些页面偏偏依赖 GPU 或系统字体渲染,headless 下根本绘制不出来。
几种解法:
- 临时方案:启动时加上
--disable-gpu --no-sandbox --font-render-hinting=none这几个参数,通过BrowserTypeLaunchOptions.Args传入 - 更稳的做法:调试时切到 headful 模式,
new BrowserTypeLaunchOptions { Headless = false },就能看到浏览器实际运行的样子。不过这个生产环境别用 - 截图前务必等关键元素加载完成,别指望
WaitForTimeoutAsync(2000)这种硬等 —— 用await page.WaitForSelectorAsync("main")这类语义化等待更靠谱 - 如果页面上有 canvas 或 WebGL 内容,截图黑屏大概率是 Chromium 版本太老。升级到 Playwright v1.40+(对应 Chromium 122+)能缓解这个问题
登录态保持与多页复用
最后说说登录态。每次调用 browser.NewContextAsync() 都会得到一个干净的会话,page 一旦关闭,Cookie 和 localStorage 全丢了 —— 这不是 bug,框架就这么设计的。
- 要保持登录态,得把
IBrowserContext提升成类字段,或者在服务生命周期内做成单例,别在方法里临时创建 - 不要在一个
context里反复调用page.GotoAsync()切换不同域名 —— 跨域会清空部分存储。每个主域名单独建一个context - 需要导出/导入登录态时,用
context.StorageStateAsync()拿到 JSON,再通过new BrowserTypeLaunchOptions { StorageState = storageStatePath }加载回去 - 留意
context.CloseAsync()会销毁所有关联的page,如果只是想清理缓存,用context.ClearPermissionsAsync()+context.ClearCookiesAsync()更精准
不过值得一提的是,Playwright 的 C# 绑定对异步取消的支持比较弱,比如 page.WaitForNa vigationAsync() 传入 CancellationToken 可能不响应。真正稳的做法是设置超时参数:new PageWaitForNa vigationOptions { Timeout = 15000 },而不是依赖 token 中断。这点细节,踩过的都知道。