Selenium太慢太脆?8款更省事的Web自动化替代工具推荐
2026/9/5 9:19:16 网站建设 项目流程

做 Web 自动化这些年,Selenium 基本是我最早接触的框架。前几年只要聊自动化,大家默认就是去写 WebDriver 脚本;但这两年越来越多的人开始吐槽脚本慢、用例脆、前端一改就挂,连驱动下载都要小心翼翼配版本。我也被这些问题折磨过很多轮,后来陆续上手 Playwright、Puppeteer、Cypress、DrissionPage 这类工具,才意识到“低效脚本”很多时候不是写代码的人不行,而是 Selenium WebDriver 这套同步命令模型本身把效率和稳定性限制住了。

这篇文章就结合我之前写脚本、跑自动化测试、维护回归用例时踩过的坑,聊 8 款更省事的 Selenium 替代工具。覆盖 UI 自动化测试、浏览器爬虫脚本、老用例渐进迁移等场景。不管你是测试开发、后端偶尔写爬虫,还是前端想给页面做回归,应该都能从里面找到适合自己的一款。

1. 为什么越来越多人想换掉 Selenium?先看清楚瓶颈在哪

1.1 WebDriver 的同步命令,天生就容易“一问一答”

Selenium 的操作模型基于 WebDriver 协议,每个 findElement、click、sendKeys 都像一次独立的远程调用。浏览器端执行完一条,再等客户端下一条指令,中间还有网络转发和驱动进程的损耗。对普通网页来说问题不大,但一旦页面是重度 SPA,元素加载顺序由接口和数据状态决定,这种同步模型就会被放大成大量等待和超时。

可以打个比方:WebDriver 像你打电话给另一个人,让他帮你操作电脑,你说一句他做一步,还要不断确认“看到按钮了吗”“点击成功了吗”。而现代工具更多是直接坐在电脑前操作,或者能订阅浏览器内部的事件流,状态一变就知道该干什么。后者显然更省事。Playwright、Puppeteer 这类工具底层转向了 Chrome DevTools Protocol,也就是 CDP,很多操作并不需要像 WebDriver 那样每个命令都走一遍完整请求链,整体体感会轻快不少。

1.2 隐式等待解决不了动态页面,显式等待又写到手酸

Selenium 官方建议不要同时使用隐式等待和显式等待,否则超时时间会叠加,很容易出现“本来 5 秒能等出来,结果硬生生等了 20 秒”的怪现象。可现实里大家图省事,要么直接开一个全局隐式等待,要么在关键操作前塞time.sleep(2)。前端加载快的时候,sleep 纯属浪费时间;前端加载慢的时候,2 秒又不够用。等得短了报 no such element,等得长了整个脚本变成龟速。

显式等待能精准一些,但每条关键操作都要写WebDriverWait(driver, 10).until(EC.element_to_be_clickable(...)),脚本一多,代码量蹭蹭涨。我见过一个维护了两年的 UI 回归项目,一千多行脚本里,至少有三分之一代码和“等待”相关,真正在验证业务逻辑的反而不多。这个问题不是靠“更会写 Selenium”能解决的,而是框架本身需要提供更聪明的默认策略。现代替代工具基本都内置了动作可用性等待:元素不可见就一直等,可点击才去点,中间不需要开发者自己手写各类 ExpectedConditions。

1.3 驱动与浏览器版本绑定,连安装这一步都劝退很多人

热搜里有“web自动化selenium浏览器驱动怎么判断下载哪个区别”,说明大家被这个问题卡过。Chrome 每个大版本更新,chromedriver 版本就要跟着换;还要搞清楚 driver 和本地 Chrome 版本号第一位要对齐。浏览器自动更新一次,第二天 CI 里的用例就可能全部失败,报错信息还特别抽象。

想当年 Selenium 2/3 时代要额外配 selenium-server,到了 4 虽然情况好点,但 chromedriver 的下载问题仍然没有消失。替代方案里,要么像 Puppeteer 安装时直接下载配套 Chromium,要么像 Playwright 提供一个playwright install命令自动装好所有浏览器,要么像 DrissionPage 那样直接接管本机已有的 Chrome 调试端口。对只想写业务脚本的人来说,省掉驱动配置这一步,已经能节约大量无意义时间。

1.4 脚本健壮性差,前一步挂了后面全线崩盘

Selenium 脚本“脆”是出了名的。页面结构一变,XPath 失效;某个异步接口稍微慢一点,下一步点不到;弹窗、iframe、新标签页只要切换时机不对,马上报错。最让人头疼的是 Selenium 没有默认的失败重试机制,用例一旦挂掉就要靠外层框架来做恢复和重跑。

不是说 Selenium 完全不能做好,只是它提供的是“底层能力”,真正要跑得稳还得自己搭建很多基础设施。后来我转到自带超时重试、自动等待、拦截网络请求的工具时,最大的感受不是“功能更多”,而是“脚本不再那么容易被临时问题打断”。维护成本下降,才敢把自动化用例的数量扩起来。

2. 8 款更省事的替代工具:先按场景把方向定下来

2.1 先看总表,快速定位你属于哪类使用者

很多人看到“8 款工具”就头大,觉得又要学一堆新东西。其实不用。工具没有绝对优劣,只有适不适合当前场景。下面这张表我按“工具类型、底层思路、主语言、典型场景”做了个归纳,你可以先凭直觉找到最接近自己处境的那个。

工具底层思路主要语言最擅长场景
PlaywrightCDP + 自建跨浏览器协议,支持 Chromium/Firefox/WebKitPython / Node / Java / .NETUI 自动化测试、跨浏览器回归、爬虫、录制生成脚本
Cypress浏览器内运行测试代码,配套 Runner 调试JavaScript / TypeScript前端开发本地自测、组件级到端到端回归
PuppeteerCDP,直接操作 ChromiumNode.jsChrome 批量截图、PDF、采集脚本、轻量任务编排
DrissionPage控制本地 Chrome 调试端口,页面与数据包双模式PythonPython 生态下的浏览器操作、网页数据采集
WebdriverIO兼容 WebDriver 协议,也可切 DevTools 模式JavaScript / TypeScript老 Selenium 团队平滑迁移、JS 技术栈的测试项目
SeleniumBase对 Selenium 做封装增强,底层仍是 WebDriverPython不想抛弃 Selenium 但又想省写法、自带报告的人
TestCafe无 WebDriver,自带浏览器驱动层JavaScript / TypeScript免配置驱动、远程浏览器、云测试环境
Katalon Studio低代码平台,内部集成多种自动化方案图形化 + Groovy/Java 脚本质量团队低代码搭用例、测试资产管理

2.2 主攻 UI 自动化回归:Playwright 与 Cypress

如果目标是正儿八经的 Web UI 回归测试,我最推荐先看 Playwright。它解决了 Selenium 最痛的几个点:默认等待、自动重试、多页面上下文、拦截网络请求、录制脚本。你用playwright codegen打开一个站点,点几次按钮,它能直接生成一份可用的测试脚本,自己再改成断言的逻辑就行。初次接手的人可以在十分钟内跑通第一个用例,这种正向反馈在 Selenium 时代几乎不可能。

Cypress 适合离前端更近的团队。它不是在外面驱动浏览器,而是直接跑在浏览器里,Runner 界面会记录每一步操作前后的页面快照,断言失败时你能像看录像一样看到当时页面长什么样。对前端开发来说,写完组件后用 Cypress 补一条冒烟测试非常顺手,不需要额外维护 driver 和远程浏览器。不过它的多标签页和跨域支持没有 Playwright 那么开放,如果你重度依赖多窗口操作,建议提前验证。

2.3 主攻浏览器脚本与数据采集:Puppeteer 与 DrissionPage

如果你的“自动化”主要不是测试,而是想写脚本批量处理页面,Puppeteer 是 Node 生态里很成熟的选择。Puppeteer 安装时会自动下载 Chromium,不需要去配 chromedriver。它的 API 围绕 page 对象展开,跳转、点击、截图、导出 PDF、拦截请求,写起来比 Selenium 丝滑很多。像是浏览器长期驻留、重复点击、记录状态变化这种“设备老化测试全自动执行脚本”,用它控制单个浏览器实例反而更稳,不用反复创建新进程。

Python 生态下,DrissionPage 是这几年口碑上涨很快的库。它不像 Selenium 那样要求先启动一个“受控浏览器”,而是直接通过调试端口连接本机已经打开的 Chrome,所以很多 Selenium 时代常见的“webdriver 被识别”“driver 版本不匹配”问题都绕开了。对只想做数据采集、页面填报、简单批量操作的人来说,DrissionPage 的代码可读性好,上手成本比 Playwright Python 还要低一些。

2.4 想低代价离开裸 Selenium:SeleniumBase 与 WebdriverIO

有人会问:我的团队代码全是 Selenium,直接换 Playwright 风险太大,有没有过渡选项?有。SeleniumBase 严格说是 Selenium 的增强封装,但它替代了你原来“裸写 WebDriver”的体验。它把常见等待、失败截图、报告生成、录制、Pytest 插件都做成了默认能力。老用例可以一行一行搬过来,大部分 findElement 写法都能映射成更简洁的 API。

WebdriverIO 是 JavaScript/TypeScript 世界里比较老牌的测试框架。它既支持 WebDriver 协议,也能选择 DevTools 协议跑。这个属性让它很适合从 Selenium JS 写法迁移过来的团队。你的既有测试对象、断言风格、Page Object 设计基本能延续,但换上了更现代的异步 API 和命令式等待,写起来干净不少。如果团队前端基础不错,把 WebdriverIO 和 WDIO 的插件体系用起来,效果不会比 Cypress 差太多。

2.5 不想自己维护一堆脚本:TestCafe 与 Katalon Studio

TestCafe 最大的卖点是不需要 WebDriver,也不需要你手动下载浏览器驱动。它会自己找到本机安装的浏览器并建立连接,对远程浏览器、云测试平台的支持也比较友好。如果你只是希望回归用例能稳定跑起来,不想每天处理驱动和浏览器版本之间的关系,TestCafe 可以当做一个省心选项。

Katalon Studio 则更偏平台型工具。它把页面对象、测试用例、测试套件、报告和 CI 集成都做成图形化界面,不会编程的人也能录制操作生成用例。当然,它商业味道比较重,还引入了自己的工程结构和存储格式,小团队或个人项目用起来可能觉得笨重。但如果你在给一个不懂代码的质量团队搭自动化体系,Katalon 这类低代码工作台确实比教他们写 Selenium 要现实得多。

3. 换工具之前,先判断这三个问题

3.1 你是想解决“测试管理”问题,还是“脚本效率”问题

很多人动不动就说要换掉 Selenium,但聊深了发现,团队真正缺的不是工具,而是用例组织和结果管理。如果只是觉得脚本乱、等待满天飞,那么像 SeleniumBase 这种轻量封装就能解决不少问题,不必立刻全盘迁移到新语法。如果问题是“每个前端版本迭代都要花两天修定位器”,那从 XPath 依赖里跳出来才是关键,这时候选用 Playwright 或 Cypress,配合 testid、role 这类更稳定的定位策略,效果会明显很多。

我自己建议先用两周时间给现有脚本做个体检:统计一下因为等待失败的比例、因为定位器变更失败的比例、因为环境或驱动问题失败的比例。哪个占比最高,就选针对这个痛点的工具,而不是只听说“Playwright 很火”就立刻重写全部用例。

3.2 团队技术栈和浏览器矩阵决定了迁移成本

如果你所在的团队只会 Python,硬要迁移到 Cypress 是给自己找麻烦。Python 技术栈下最平滑的选择是 Playwright Python,或者 DrissionPage;如果还想沿用 unittest/pytest 的习惯,SeleniumBase 也不错。如果团队本来就是 Node 技术栈,Cypress 和 WebdriverIO 会容易落地,Puppeteer 也能作为轻量脚本补充。

还要考虑浏览器覆盖要求。只测试 Chrome 内核,几乎上面提到的工具都能胜任。如果正式环境还要覆盖 Firefox、Safari,那么优先考虑 Playwright,因为它的跨浏览器支持是相对完整的。WebdriverIO 也能接不同的 driver,但需要额外配置;Cypress 对 Safari 的支持不算好,适合纯 Chrome 场景的快速反馈。

3.3 旧 Selenium 用例别急着推翻,关键是切成小步走

我见过程序员打算用一个周末把所有 Selenium 代码改写成 Playwright,结果改到一半发现很多用例本身就有问题,并不是 Selenium 写不出来,而是产品逻辑已经变了。与其花大力气一次性迁移,我更推荐“新用例新工具,老用例按模块迁移”。比如首页、登录、订单这几条核心链路,先用新工具写一版,和旧脚本并行跑一个星期,对比结果一致性和维护时间。

等团队熟悉了新工具,再把老脚本里经常失败的模块挑出来迁移。这样每次交付都能验证一版,风险会小很多。别被网上那些“三分钟迁移全部用例”的文章骗了,真实项目里最大的成本从来不是 API 差异,而是业务规则梳理和定位器策略调整。

4. 实操对比:同样的“登录+搜索”,不同工具写法差多少

4.1 Selenium 传统写法:等待写到手酸

下面是用 Selenium 完成一个“登录后搜索”的 Python 示例。为了稳定,我不得不频繁加显式等待,代码里到处是 WebDriverWait。

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Chrome() driver.get("https://example.com/login") driver.find_element(By.ID, "username").send_keys("tester") driver.find_element(By.ID, "password").send_keys("password123") driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click() search = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, ".search-box")) ) search.send_keys("selenium") search.send_keys("\n") driver.quit()

这段代码能跑,但它默认了页面跳转不会超过 10 秒,也默认了输入框一定可以立即输入。真实项目里,如果登录接口慢一点,search按钮还没渲染出来,脚本就挂了。Selenium 使用者的日常就是在这些 WebDriverWait 里反复调超时时间。

4.2 Playwright:把操作可用性判断交给框架

同一个场景用 Playwright 写,是这样的:

from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto("https://example.com/login") page.fill("#username", "tester") page.fill("#password", "password123") page.click("button[type=submit]") # fill 会自动等待元素可见、可用 page.fill(".search-box", "playwright") page.keyboard.press("Enter") browser.close()

注意我不需要手工写显式等待。Playwright 的fillclick会等待元素被附加到 DOM、可见、不被遮挡、可交互,默认超时时间可以全局配置,超时后的报错信息还会告诉你具体卡在哪一步。这种默认规则让脚本的耐药性强了很多。

4.3 Puppeteer:适合 Node 里的批处理任务

Puppeteer 写起来和 Playwright 有点相似,但语言环境更偏向 Node.js:

const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({ headless: 'new' }); const page = await browser.newPage(); await page.goto('https://example.com/login'); await page.type('#username', 'tester'); await page.type('#password', 'password123'); // 点击登录,同时等待页面跳转,避免丢失导航事件 await Promise.all([ page.waitForNavigation(), page.click('button[type=submit]') ]); await page.type('.search-box', 'puppeteer'); await page.keyboard.press('Enter'); await browser.close(); })();

Puppeteer 的 API 比较贴近底层,如果你想控制浏览器的每个行为细节,它很合适。上面的Promise.all是个经验点:点击按钮触发跳转时,先注册waitForNavigation()再执行 click,能避免导航事件被错过。Selenium 时代很多人就是因为少了这一步,时不时会看到点击已经发生但脚本仍在等待下一页元素的诡异问题。

4.4 Cypress:前端自测的体验确实舒服

如果这个登录流程交给前端开发做自测,Cypress 的体验会更快乐:

describe('login flow', () => { it('logs in and searches', () => { cy.visit('/login'); cy.get('#username').type('tester'); cy.get('#password').type('password123'); cy.get('button[type=submit]').click(); cy.get('.search-box', { timeout: 10000 }).type('cypress{enter}'); cy.url().should('include', '/search'); }); });

Cypress 命令自带重试,不需要每行都写等待。它最大的差异是 Runner 界面会在每一步旁边留下页面快照,断言失败时你直接点一下命令就能看到当时 DOM 状态,排错成本很低。不过需要注意,Cypress 对多标签页和跨域页面的访问限制比较多,如果你的系统经常弹出新窗口或者跳到另一个域名执行第三方登录,要先确认这个工具能不能覆盖。

4.5 DrissionPage:Python 爬虫脚本的“无驱动”体验

最后看看 DrissionPage。它连接本机 Chrome 的调试端口,不需要下载 driver,也不要求浏览器处于传统“受控模式”。这也是它让很多爬虫和办公自动化脚本维护者眼前一亮的原因。

from DrissionPage import ChromiumPage, ChromiumOptions co = ChromiumOptions() # 指定本机 Chrome 的用户数据和端口,首次需要手动开启调试端口 co.set_local_port(9222) page = ChromiumPage(co) page.get('https://example.com/login') page.ele('#username').input('tester') page.ele('#password').input('password123') page.ele('button[type=submit]').click() page.ele('.search-box').input('drip')

DrissionPage 的.ele()返回元素对象,.input()会补齐输入细节。它没有 Playwright 那么庞大的跨浏览器野心,但如果你只和 Chrome 打交道,熟悉之后会觉得 Python 脚本写起来能少很多 Selenium 的仪式感代码。

上面的几段代码并不是让你全部学,而是提供一个直观感受:不同工具不只是语法不同,连“需要显式处理等待”这件事都从框架层面被优化

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询