先讲个上周刚发生的事。有朋友做电商代运营,每天早上第一件事就是把竞品价格爬回来自动比价,用的就是Selenium。他跟我吐槽说,本来以为Selenium自动化脚本写一次就一劳永逸了,结果浏览器一升级、驱动一换版本,脚本就集体罢工,光排查环境问题就耗掉半天。他问我:网上天天吹的替代工具,到底哪款能让我少加两天班?
这问题我太熟了。做了这么多年自动化采集和测试,Selenium确实是经典,但它的痛点也足够经典:配置麻烦、启动慢、代码啰嗦,还有那个一升级就头疼的WebDriver版本匹配问题。更别提现在的网站上各种反爬校验和滑块验证,Selenium那套浏览器特征实在太明显,脚本经常搞不定。所以我后来在大部分场景里都换掉了Selenium,有些替代工具省事程度真的超出预期。
这篇文章就把我用过、踩过坑、认可有效的8款Selenium替代工具一次性拆完。不吹不黑,结合真实场景讲清楚每款适合干什么、代码怎么写、坑在哪儿,最后再给一张对比表和一套个人搭配方案,帮你直接照着选。
1. 为什么要换掉 Selenium:先看清它的问题在哪
1.1 配置成本高:驱动与浏览器版本匹配是老大难
Selenium要跑起来,必须额外下载一个与浏览器版本严格对应的WebDriver。Chrome一更新,chromedriver不跟着换,脚本直接报错说版本不匹配。这个“更新地狱”在团队协作里尤其致命:有人浏览器停在旧版,有人自动升级到新版,同一个脚本在不同机器上表现完全不一致。
更要命的是,不是只有Chrome有这个问题。Firefox要用geckodriver,Edge要用msedgedriver,Safari还要单独开启远程自动化开关。你要是负责维护一套跨浏览器用例,光是驱动列表就够写张Excel了。替代工具里,Playwright、Puppeteer这类新型框架普遍把浏览器下载和驱动管理内置了,装一个包就自带对应浏览器版本,彻底摆脱手动匹配。
1.2 运行效率低:每个操作都在完整浏览器里走一圈
Selenium的设计初衷是模拟真实用户操作,所以每个find_element、click、send_keys都要通过WebDriver协议在浏览器和脚本之间做一次往返通信。这种架构稳定,但性能开销巨大。跑一百个页面的回归用例,Selenium可能要一小时,而同样逻辑用Playwright或Puppeteer,可能十几分钟就结束了。
我做爬虫项目时对此感受更深。Selenium启动一个完整Chrome实例,内存随随便便吃掉几百兆,并发开三个实例就卡得不行。后来换成协议级控制的替代工具,配合无头模式,并发能力翻了好几倍。如果你每天要抓大量数据,这个效率差距就是实打实的成本差距。
1.3 代码冗余:简单操作被复杂API放大了
Selenium的API设计比较“教科书”,每个步骤都写得很死。想在输入框里填个值、点个按钮,你得先显式等待元素出现,再滚动到元素位置,再判断可点击,最后才操作。这一套下来,一个简单动作写上七八行代码是常态。而Playwright的auto-wait、Cypress的链式调用、DrissionPage的简洁选择器,都能把同样的事情压缩到两三行。
更不用说Selenium里那些容易踩的坑:元素被遮挡、iframe里定位不到、阴影DOM穿透不了……每一样都要自己查资料、写Workaround。说白了,Selenium把大量的底层复杂度直接暴露给了使用者,而优秀的替代工具会帮你把这些复杂度吞掉。
1.4 反爬特征明显:正则检测和滑块校验容易失败
如果你的用途是数据采集而不是纯测试,那你应该深有体会:大量网站已经开始在JavaScript里检测navigator.webdriver、window.chrome这些浏览器指纹。Selenium驱动起来的浏览器,这些特征跟真实用户浏览器差距很大,网站很容易识别出是自动化工具在访问,轻则弹验证码,重则直接封IP封账号。
这也是为什么后来出现了DrissionPage这类走“接管真实浏览器”路线的工具,以及各种stealth插件。我自己实测下来,Selenium跑不过的滑块校验,DrissionPage配合轨迹模拟大概率能过;Playwright配合Stealth插件也有不错效果。如果你还在为验证码头秃,工具选对了能省下一大半工夫。
2. 选型逻辑:替代工具不是越新越好,场景要匹配
2.1 先区分你的任务类型:爬虫、测试还是混合型
很多人问我“哪款是Selenium的最强替代”,这是个伪命题。替代工具之间不是简单的谁比谁强,而是赛道不同。
任务可以大致分成三类。第一类是爬虫和数据采集,核心诉求是快、隐蔽、能扛并发;第二类是自动化测试,核心诉求是稳定、好断言、好调试;第三类是两者混合,比如登录然后抓数据,既要操作页面又要批量取数。你得先想明白自己主要干哪类活,再去挑工具,否则很容易选错方向。
比如你只是写个脚本每天定时抓几个静态页面的价格,那根本没必要上浏览器自动化,Requests加BeautifulSoup就够了,速度快十倍还不容易被封。反过来,如果你要做的是电商下单流程的UI回归测试,那Requests这种方案完全使不上劲,得靠Playwright或Cypress这类测试框架。
2.2 再看你的技术栈:Python、Node.js还是Java
语言生态也是硬约束。Python是自动化爬虫的大本营,DrissionPage、Scrapy、Playwright的Python binding都是成熟方案;JavaScript/TypeScript环境下,Puppeteer和Cypress是主流;Java后端项目里,虽然Playwright也支持,但HtmlUnit这种免驱动轻量方案有时候更省心。
还有一点容易被忽略:团队里其他人的维护能力。我见过不少项目用Puppeteer写得飞起,但整个团队没人熟悉Node,后续维护全靠作者一个人。选工具时要把团队的平均水平和后续接手成本都算进去,不然省了技术债,欠了人情债。
2.3 动态页面占比决定要不要“真浏览器”
选工具的另一个关键判断点是目标页面到底有多少内容是JavaScript动态渲染的。
如果页面数据主要是服务端渲染,打开就有,那用Requests或Scrapy直接拿HTML解析最划算,速度和稳定性都碾压任何浏览器方案。如果页面大量依赖Ajax请求动态加载,那可以用两层方案:先用Requests分析接口,直接调接口拿JSON,效率最高;接口加密破解不了时,再上无头浏览器做兜底。
这个判断多做几次之后,你会发现真正需要完整浏览器自动化的场景其实没有想象中那么多。多数情况下,接口直连加少量渲染兜底就能覆盖九成需求。替代Selenium,不只是换工具,更是换思路。
3. 8款省事实用的替代工具逐款拆解
3.1 Playwright:自动等待+免驱动,最省心的全能型选手
Playwright是微软开源的新一代自动化测试框架,支持Chromium、Firefox、WebKit三款浏览器引擎,Python、JavaScript、Java、.NET四种语言都有官方binding。它最大特点是“免驱动”和“自动等待”。安装时自带浏览器,不用额外下载driver;写代码时不用显式写sleep和WebDriverWait,元素不出现就自动重试等待,稳定性和代码简洁度都比Selenium提升一大截。
我用Python版给大家演示一段最简单的搜索操作:
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") page.fill("#kw", "python") # 自动等待输入框可操作 page.click("#su") # 自动等待按钮可点击 page.wait_for_timeout(2000) # 简单停留观察结果 print(page.title()) browser.close()这里没有一行等待代码,没有driver路径配置,都是框架自动处理的。Playwright还自带playwright codegen命令,你打开一个页面手动点几下,它就能自动录制成脚本,做原型验证非常爽。
需要注意:Playwright的文件下载处理也比较顺手。以前用Selenium下载文件要配preferences、判断文件是否下载完成,Playwright可以用page.expect_download()上下文管理器等待下载事件,逻辑清晰很多。还有网络拦截功能,可以直接中止图片和CSS请求,减少无意义的流量消耗,实测大型页面加载能快30%以上。
3.2 DrissionPage:国产开源工具,把反爬和滑块难题降维处理
DrissionPage是我近两年最喜欢推荐给爬虫玩家的工具,作者是国内的开发者。它的核心思路是用一个库同时管理浏览器控制和数据请求:既能像Requests一样直接发HTTP请求,也能像Selenium一样操作浏览器页面,而且两边数据还能无缝互传。
最吸引人的是它对反爬的处理。Selenium的浏览器会被检测出webdriver特征,而DrissionPage可以接管你自己启动的Chrome浏览器,本质上就是一个真实用户在操作真浏览器,指纹特征跟普通用户没有区别。应对滑块验证码时,配合轨迹模拟函数,成功率比我用Selenium加stealth插件高得多。
举个例子,这是DrissionPage实现搜索操作的代码:
from DrissionPage import ChromiumPage page = ChromiumPage() page.get("https://example.com") page.ele("#kw").input("python") page.ele("#su").click()是不是感觉特别像Requests的简洁风格?它对中文网站的特性适配很好,很多抓取任务都能直接定位元素搞定,不需要绕来绕去找XPath。DrissionPage还有个专门做数据采集的SessionPage模式,针对无需渲染的接口请求比requests库还顺手。
我自己在帮客户处理电商详情页抓取时,大量用了DrissionPage。那些带滑块、带行为验证的站点,Selenium几乎全军覆没,DrissionPage配合合理操作频率能稳定跑一整天。如果你做爬虫经常被验证码卡脖子,这个工具值得第一个试。
3.3 Puppeteer:Node生态里的最强无头浏览器库
Puppeteer是Chrome官方DevTools团队维护的Node.js库,主要控制Chromium或Chrome。它的最大优势是背靠Chrome团队,API设计紧跟浏览器能力,性能和兼容性都不用担心。相比Selenium,Puppeteer的安装过程会主动下载指定版本的Chromium,不需要再手动管理driver。
Node.js环境下,Puppeteer代码写起来也很直观:
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.goto('https://example.com'); await page.type('#kw', 'python'); await page.click('#su'); await new Promise(r => setTimeout(r, 2000)); console.log(await page.title()); await browser.close(); })();Puppeteer的截图和PDF生成能力是全行业公认的强。做定时任务每天晚上自动给数据报表截图、生成PDF发给老板,用Puppeteer几十行代码就能稳定跑。它也很适合做SPA页面的预渲染和SEO优化。
但需要注意,Puppeteer只支持Chromium系浏览器,跨浏览器测试场景就力不从心了。另外它默认是无头模式,如果目标网站对无头浏览器有检测策略,要记得开headless: false或使用puppeteer-extra-plugin-stealth插件隐藏特征。我的经验是:纯Node团队、主要跑Chrome场景,选Puppeteer非常顺手;需要跨浏览器测试就上Playwright。
3.4 Cypress:前端测试的体验派标杆,测试断言写得像聊天
如果你做的是前端项目的E2E测试,不是爬虫,那Cypress可能是最提升幸福感的选择。它不像Selenium那样通过WebDriver远程控制浏览器,而是直接跑在浏览器内部,所以执行速度极快,又有时间旅行调试能力——每跑一步你可以回放看当时页面的状态。
Cypress的测试代码写法很自然,自带断言和等待机制,没有Selenium那种“等待+查找+操作”的割裂感:
describe('登录功能测试', () => { it('输入正确账号密码可以跳转首页', () => { cy.visit('/login') cy.get('#username').type('admin') cy.get('#password').type('123456') cy.get('button[type="submit"]').click() cy.url().should('include', '/dashboard') cy.contains('欢迎回来').should('be.visible') }) })Cypress还自带Test Runner可视化界面,用例跑起来每一步都有实时视频和DOM快照,定位问题效率极高。不用像Selenium那样另外搭报告框架,内置的Mocha风格报告足够日常使用。
它的局限也很明显:主要支持Chromium系和Firefox,对多标签页、跨域操作支持不好,也不能用两个浏览器同时操作来测实时通信场景。可以说,Cypress是“测试专用工具”,适合纯前端团队用来做业务回归,不适合数据采集类任务。
3.5 Scrapy:纯爬虫场景的工业级持久方案
如果在你的实际工作里,需要每天都抓大量不同网站的同类数据,那Scrapy才是比Selenium聪明得多的方案。它不是浏览器自动化工具,而是一个完整的爬虫框架——自带异步调度、并发下载、数据管道去重、中间件扩展、日志统计,几乎所有爬虫工程化的问题都帮你考虑过了。
Scrapy抓一个带翻页的列表站点,核心代码不过如此:
import scrapy class QuotesSpider(scrapy.Spider): name = "quotes" start_urls = ["https://quotes.toscrape.com/"] def parse(self, response): for quote in response.css("div.quote"): yield { "text": quote.css("span.text::text").get(), "author": quote.css("small.author::text").get(), } next_page = response.css("li.next a::attr(href)").get() if next_page: yield response.follow(next_page, self.parse)这个爬虫天生支持并发请求,默认配置下每秒抓十几个页面毫无压力,这是Selenium永远追不上的。很多看似需要自动化浏览器的任务,其实页面数据就在那个接口里,只是你还没找到规律。先用开发者工具分析网络请求,找到数据接口,再用Scrapy请求接口拿JSON,比开浏览器省力一百倍。
如果页面确实需要JS渲染,Scrapy也能通过搭配scrapy-playwright中间件或Splash渲染服务来解决,做到“静态用Scrapy,动态才渲染”的组合策略。我的建议是:所有长期运行的采集项目,首选Scrapy做地基,浏览器自动化只做兜底环节。
3.6 BeautifulSoup + Requests:最轻量的“伪替代”,静态页面一把梭
要说“更省事”,没有比Requests加BeautifulSoup更省事的组合了。一个负责发请求,一个负责解析HTML,代码量最小,部署最方便,也几乎没有被反爬检测的风险,因为你的请求看起来就是一个普通的HTTP访问。
最简单的使用方式:
import requests from bs4 import BeautifulSoup r = requests.get("https://example.com") soup = BeautifulSoup(r.text, "html.parser") for item in soup.select(".title"): print(item.get_text())这个方案只适合页面数据直接写在HTML里的场景。但现实中很多网站的数据是接口动态返回的,这时候你可以退一步:用开发者工具看到接口返回的JSON,直接用Requests调接口拿数据,解析都省了。
我见过太多朋友一上来就开Selenium去抓数据,其实那个页面根本不需要执行JavaScript。动手写代码之前先花十分钟分析页面结构,能用Requests解决的坚决不上浏览器,这个习惯能帮你省下大量时间和带宽。轻量方案看着不酷,但最多人靠它干活。
3.7 TestCafe:跨浏览器测试不用单独下载驱动
TestCafe是DevExpress出品的Node.js端到端测试框架,相比Selenium最大的优势是免驱动,而且免浏览器的远程设置。你只要装了Node.js,写个脚本就能直接跑在Chrome、Firefox、Edge甚至Safari的远程实例上,配置成本几乎为零。
示例代码和Cypress风格接近,但TestCafe更偏向“面向浏览器自动化”的通用能力,对多标签页、文件下载、屏幕截图的支持都比Cypress更灵活:
import { Selector } from 'testcafe'; fixture('登录测试').page('https://example.com/login'); test('输入正确账号密码可以登录', async t => { await t .typeText('#username', 'admin') .typeText('#password', '123456') .click('button[type=submit]') .expect(Selector('body').innerText).contains('欢迎回来'); });它内置了等待机制和智能选择器,不用写timeout参数,还有自动并发跑用例的能力。适用于需要同时覆盖多浏览器的测试团队,但如果你只是做爬虫,它不太合适。
3.8 HtmlUnit:Java后端里的轻量无头浏览器选择
HtmlUnit是Java生态里一个老牌的无头浏览器实现,支持JavaScript执行、表单提交、Cookie管理,而且不需要启动额外浏览器进程,速度比Selenium快得多。如果你的项目是Java后端,又不想在服务器上装一堆浏览器和驱动,HtmlUnit是个值得留意的选择。
一个简单的Java例子:
import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlPage; public class Example { public static void main(String[] args) throws Exception { try (WebClient webClient = new WebClient()) { webClient.getOptions().setJavaScriptEnabled(true); webClient.getOptions().setCssEnabled(false); HtmlPage page = webClient.getPage("https://example.com"); System.out.println(page.getTitleText()); } } }需要说明的是,HtmlUnit的JavaScript引擎和真实浏览器有差距,复杂的现代前端框架页面它不一定能渲染完整,遇到这类场景还是得请出Playwright或者真实浏览器。但在Java服务里做轻量数据解析、定时巡检,它足够便宜高效。
4. 一张对比表:快速找到适合你的那一款
工具选型最怕选择困难,我做了一张速查表,从语言生态、驱动要求、适用场景等维度把8款工具放在一起对比。没有最好的工具,只有最匹配你当前任务的工具。
| 工具 | 语言生态 | 免驱动 | 执行JS | 典型场景 | 反爬友好度 | 上手难度 |
|---|---|---|---|---|---|---|
| Playwright | Python/JS/Java/.NET | 是,安装自带浏览器 | 完整 | E2E测试、复杂爬虫、多浏览器 | 中 | 低 |
| DrissionPage | Python | 是,接管真实浏览器 | 通过真实浏览器执行 | 国内站点、滑块验证、文件下载 | 高 | 低 |
| Puppeteer | Node.js | 是,安装自带Chromium | 完整 | 截图、PDF、SPA抓取、测试 | 中 | 中 |
| Cypress | JS/TS | 是,驱动内置 | 完整 | 前端E2E测试、回归测试 | 不适用 | 低 |
| Scrapy | Python | 无浏览器 | 不支持,可扩展 | 大规模静态数据采集 | 高 | 中 |
| BeautifulSoup+Requests | Python | 无浏览器 | 不支持 | 轻量静态抓取、接口调试 | 高 | 极低 |
| TestCafe | JS/TS | 是,免驱动 | 完整 | 跨浏览器E2E测试 | 不适用 | 低 |
| HtmlUnit | Java | 是,内置引擎 | 部分支持 | Java后端轻量解析 | 中 | 中 |
如果你是非Java的爬虫玩家,这表格里你只需要重点关注前两行和第五六行。Playwright适合结构化测试和重交互抓取,DrissionPage适合跟反爬斗智斗勇的场景,Scrapy加Requests组合适合长期大规模采集。
如果你是前端测试工程师,Cypress和TestCafe选一个就行,团队在意调试体验就选Cypress,需要更宽浏览器矩阵就选TestCafe。
5. 常见问题与排查心得:替代过程中最容易踩的坑
5.1 页面一直加载不出来或者执行超时怎么办
很多人在从Selenium切换到Playwright后遇到的第一个问题,是新框架的等待策略不够符合预期。Selenium默认是找不到元素就立刻报错,而Playwright默认会等满超时时间,所以某些场景下反而会觉得“卡住了”。
建议做法:全局设置超时时间,同时手动关闭页面里不必要的阻塞资源。
browser = p.chromium.launch() context = browser.new_context() context.route("**/*.{png,jpg,jpeg,css}", lambda route: route.abort()) page = context.new_page() page.set_default_timeout(10000) # 10秒超时 page.goto("https://example.com", wait_until="domcontentloaded")wait_until="domcontentloaded"能减少等资源加载的时间。如果页面数据是Ajax延迟加载的,建议用page.wait_for_selector()等待具体的业务元素出现,而不是无脑用固定sleep。
5.2 点击按钮下载图片或文件时,脚本没反应或文件保存不下来
这种情况我遇到过很多次,根因通常是:点击下载按钮后,文件下载是走浏览器下载机制的,而不是正常的页面跳转。Selenium对下载预处理的配置很繁琐,而Playwright可以直接监听下载事件,DrissionPage也有专门的下载管理器。
DrissionPage的处理逻辑就简单很多,点击下载后直接用库的下载等待API获取文件,不需要自己去猜下载路径。如果目标网站的下载按钮实际上是发送一个GET或POST请求,那更推荐的做法是用抓包先拿到真实下载链接,直接用Requests来拉文件,并发和稳定性都会好很多。
5.3 滑块验证或行为验证过不去,加了延时也没用
滑块过不去的核心问题有两个:一是浏览器自动化特征太明显,二是滑动轨迹不像真人。Selenium驱动的浏览器很容易被检测出webdriver特征,所以先换用能接管真实浏览器的DrissionPage,或者给Playwright加stealth插件。
第二步是轨迹模拟。真人滑动是先快后慢、中间有微小停顿和抖动,不是匀速直线。你可以自己在代码里模拟一下这段轨迹:
import random import time def human_like_track(distance): track = [] current = 0 while current < distance: step = random.randint(3, 12) current += step track.append(current) time.sleep(0.01) # 最后再调整一两步到目标位置 track.append(distance) return track关键是不追求完美,而是追求“不像机器人”。当然,验证码方案本身也在进化,反爬技术永无止境,这里只是基础思路。如果目标网站验证强度特别高,我建议直接考虑接口逆向或第三方打码服务。
5.4 Cookie登录态怎么复用,才能避免每次跑脚本都要重新登录
很多系统要求登录态,每次都重新登录不仅慢,还可能触发风控。替代工具普遍提供了上下文存储机制,可以把登录态保存下来,下次直接加载。
Playwright的写法大概是:
context = browser.new_context() page = context.new_page() page.goto("https://example.com/login") # 手动登录一次 context.storage_state(path="state.json") # 下次脚本里直接加载登录态 context = browser.new_context(storage_state="state.json")DrissionPage更直接,它接管的是真实浏览器,登录态天然保留在浏览器Profile里,你只要指定用户目录启动,cookie、localStorage全都还在,跨脚本复用登录态非常自然。
5.5 并发跑多个任务,开太多浏览器把内存撑爆了
浏览器自动化不是无节制并发的东西,一个Chromium实例空闲时也要占用两三百兆内存。我的经验是单个浏览器实例控制在3到5个页面以内,再多就考虑用线程或进程池横向扩展机器,或者优化逻辑优先用接口方案而不是开浏览器。Playwright支持同一个Context下开多个Page,任务之间可以复用一个浏览器进程,能省不少资源。
6. 我的实际搭配方案:不同任务怎么组合用
替代Selenium不是“换一把锤子砸所有钉子”,而是一套组合拳。我现在做项目时的固定搭配是这样的:长期采集任务用Scrapy打底,处理静态页面和接口数据;遇到必须走浏览器交互、又怕反爬拦截的场景,上DrissionPage接管真实浏览器;如果是做测试框架建设或者需要跨浏览器验证功能,选Playwright;如果只是临时爬个静态页面,Requests加BeautifulSoup,五分钟后出结果。
给正准备从Selenium迁移的朋友一个建议:不要一次性把全部脚本重写。先把任务按类型归类,优先迁移那些“最容易崩溃”和“维护成本最高”的脚本,用新工具跑通之后再逐步扩大范围。这样既能快速看到效果,也能控制风险。
最后分享一个让我感触很深的小细节。以前我用Selenium写爬虫脚本,每天最怕看到的就是清晨的告警邮件;现在换了工具组合,告警邮件几乎绝迹了。技术选型这件事,最值得投入时间的不是学更多API,而是想清楚每个任务到底需要多重的工具。工具省事,工作才会省心。