Selenium 到 Playwright 的切换记录

📘 开发记录 · 14 天前 · 19 人浏览
Selenium 到 Playwright 的切换记录

从 Selenium 到 Playwright:我的浏览器控制切换之旅

最近在折腾一个远程浏览器自动化的项目,最开始是用 Selenium 控制浏览器执行一系列开锁流程,后来因为种种问题,最终还是换成了 Playwright。这里简单记录一下整个过程中遇到的一些坑、思考和切换过程。


初始方案:Selenium 控制浏览器

一开始整个系统的架构是:

  • 多线程管理浏览器池(初始化多个浏览器放进浏览器池)
  • 每个浏览器执行任务:打开页面 → 登录 → 操作页面 DOM
  • 控制流程主要使用 find_element, click, send_keys 等操作

Selenium 用着也没啥大问题,配合 Chrome + chromedriver(chromedriver版本很重要),流程基本能跑通。但随着任务量增加、并发变多、页面交互越来越复杂,问题也逐渐暴露出来:

  • 有时候页面切换 iframe 很慢,Selenium 报错找不到元素
  • 弹窗不稳定,处理逻辑需要各种 try/except
  • 多线程下的浏览器状态管理很混乱,有时线程没结束浏览器还开着

于是我开始思考,有没有更现代、更轻量的替代方案?


Playwright 更香?

Playwright 这个名字其实早听说过,一直没仔细研究。这次真碰到实际问题了,就开始试着改一下逻辑,把 Selenium 整体替换成 Playwright。

我用了它的 同步模式sync_playwright()),主要是因为现有代码结构基于同步逻辑,异步改起来太麻烦。

改造的重点包括:

  • 启动浏览器流程:从 webdriver.Chrome() 换成 p.chromium.launch(...)
  • 页面控制逻辑:换成 Playwright 的 locator, click, fill, frame() 等 API
  • 最关键:不再直接返回 page 对象,而是封装成 LockIndex.enter() 返回 browser,保持浏览器常驻

最大的坑:Playwright 不能跨进程传 browser 对象

这点一开始没注意。在 Selenium 模式下,浏览器 driver 可以传来传去(虽然也不推荐)。但 Playwright 的 browser 对象不能跨进程传递,哪怕你 pickle 了也会报错。

我原本在主进程调度任务,然后把 browser 对象传过去执行操作,结果直接报错。

于是我重构了架构:


新方案:多进程 + Playwright 控制

最终的架构大致如下:

  1. 主进程 管理多个任务队列(一个队列对应一个浏览器进程)
  2. 每个子进程 启动自己的浏览器,进入锁控页面,接收任务 → 执行 → 回传结果
  3. 调度器 轮询分发任务,比如当前是第 0 个浏览器空闲,就发给它
def dispatch_task(task):
    task_queues[current_index].put(task)
    current_index = (current_index + 1) % len(task_queues)

任务本质上是“给某个 code 和 id 做一次 DOM 操作”。浏览器常驻,执行完一个任务继续待命。

这个结构的好处:

每个浏览器独立管理,状态更清晰

多进程隔离,互不干扰

Playwright 控制页面的效率、容错能力都明显强于 Selenium

最后一点感慨

其实如果不是这次遇到实际需求,我也不会下定决心从 Selenium 迁移。现在的架构逻辑更清晰了,浏览器也更稳定了,维护起来也更方便。

小结

  • 如果你还在用 Selenium 控制浏览器,建议试试 Playwright。
  • 使用多进程时,不要跨进程传 page、browser 对象。
  • 合理设计任务调度和进程架构,能让系统更稳定。

希望这篇文章能帮到也在做浏览器自动化的朋友们。如果你也在做类似项目,欢迎留言交流。

Under CC BY NC-SA License.
Powered by Typecho | Theme by Jasmine
您是第 29001 位访客