1. 从 OpenClaw 说起:为什么大家都在找替代方案
OpenClaw 这个项目,早期确实解决了不少人的燃眉之急。它把浏览器自动化、页面元素抓取、表单填写、定时任务这些能力打包成一套相对统一的接口,让不写前端的人也能快速搭出一套“能自己点页面”的工具链。我最早接触它是在一个数据采集的小项目里,当时团队里没人愿意碰 Selenium 那套繁琐的等待和选择器逻辑,OpenClaw 的封装确实省了不少事。
但用得越久,问题就越明显。首先是维护节奏,OpenClaw 的更新频率在过去一年里明显放缓,issue 区里堆着不少关于新版浏览器内核兼容性的反馈,官方响应周期从最初的两三天拉长到两三周甚至更久。其次是扩展性,它的插件机制设计得比较早,面对现在动辄需要多标签页协同、跨域请求拦截、动态渲染等待的场景,改起来很别扭。再就是文档,很多参数的实际行为跟文档描述有出入,新手照着文档配一遍,跑不通是常态。
所以“替代方案”这个需求不是凭空冒出来的。它背后是三类人在找出路:一类是原来用 OpenClaw 做数据采集和流程自动化的开发者,想找个维护更活跃、API 更现代的库;一类是做测试自动化的同学,希望工具能同时覆盖 Web 和桌面端;还有一类是刚入门的爱好者,被 OpenClaw 的配置门槛劝退,想找个上手更顺滑的起点。
下面这 5 款方案,是我在过去半年里陆续试过、并且认为“日趋成熟”的选项。它们各自解决不同层面的问题,没有哪一款能通吃所有场景,但组合起来基本能覆盖 OpenClaw 留下的空缺。我会把每款的定位、核心机制、实操配置、踩过的坑都摊开讲,你可以根据自己的场景对号入座。
2. 方案一:Playwright——浏览器自动化的现代基线
2.1 它到底解决了 OpenClaw 的哪些痛点
Playwright 是微软团队主导的项目,定位就是“跨浏览器、跨语言、跨平台的端到端自动化”。跟 OpenClaw 相比,它最直接的优势是原生支持 Chromium、Firefox、WebKit 三套内核,而且每个内核都绑定了对应的浏览器版本,不需要你手动去对齐驱动和浏览器版本。OpenClaw 时代最让人头疼的“驱动版本不匹配导致启动失败”,在 Playwright 里基本消失了。
另一个关键改进是自动等待机制。OpenClaw 里写一个点击操作,你得自己加 sleep 或者轮询判断元素是否可点击,稍不注意就报“element not interactable”。Playwright 的click()、fill()这些方法内置了可操作性检查,元素没渲染完、被遮挡、不可见,它会自动等待到超时为止。这个设计看起来小,实际写脚本时能省掉大量防御性代码。
还有一点是网络拦截能力。OpenClaw 对请求的拦截和修改支持得比较粗糙,Playwright 提供了page.route()这样的接口,可以精确匹配 URL 模式,修改请求头、返回 mock 数据、阻断特定资源加载。做数据采集时,你可以直接拦掉图片和字体请求,页面加载速度能提升一大截。
2.2 核心机制拆解:为什么它比 OpenClaw 稳
Playwright 的稳定性来自几个底层设计。第一,它通过CDP(Chrome DevTools Protocol)或各浏览器对应的调试协议直接与浏览器通信,而不是走 WebDriver 那套中间层。少一层转发,指令延迟更低,状态同步更及时。第二,它维护了一个浏览器上下文(BrowserContext)的概念,每个上下文相当于一个独立的隐身窗口,cookie、localStorage、缓存互不干扰。OpenClaw 里要模拟多用户登录,得反复清缓存或者开多个进程,Playwright 里开几个 context 就搞定了。
第三是选择器引擎。除了 CSS 和 XPath,Playwright 支持按文本、按角色、按测试 ID 定位元素。比如page.get_by_role("button", name="提交")这种写法,比page.click("#submit-btn")更贴近用户视角,页面改版时不容易失效。这个特性对做长期维护的自动化脚本特别友好。
2.3 上手实操:从安装到跑通第一个脚本
安装很简单,Python 环境下:
pip install playwright playwright install第二条命令会自动下载三套内核的浏览器二进制文件,放在用户目录下的缓存文件夹里。国内网络环境下这一步可能比较慢,可以设置镜像源加速,具体方法各平台文档都有说明,这里不展开。
一个最小可运行的采集脚本长这样:
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=True) context = browser.new_context( viewport={"width": 1280, "height": 800}, user_agent="Mozilla/5.0 ..." ) page = context.new_page() page.goto("https://example.com/list") page.wait_for_selector(".item-card") items = page.query_selector_all(".item-card") for item in items: title = item.query_selector(".title").inner_text() print(title) browser.close()这段代码里,wait_for_selector会等到元素出现才继续,query_selector_all返回的是元素句柄列表。注意inner_text()和text_content()的区别:前者返回渲染后的可见文本,后者返回 DOM 里的原始文本,包含隐藏内容。采集时一般用inner_text()更符合预期。
2.4 实操心得与避坑清单
- headless 模式下的字体渲染问题:Linux 服务器上跑 headless Chromium,中文有时会显示成方块,导致截图或文本提取异常。解决办法是安装中文字体包,或者在启动参数里指定字体配置。
- context 复用要谨慎:虽然 context 隔离性好,但开太多会吃内存。实测下来,单机并发 10 个 context 是比较稳的区间,再多就要考虑分布式了。
- 超时设置别用默认值:Playwright 默认超时 30 秒,做批量采集时一个页面卡住会拖慢整体进度。建议在
context层面设置default_timeout,比如 15 秒,配合重试逻辑使用。 - 注意反自动化检测:部分站点会检测
navigator.webdriver属性。Playwright 可以通过add_init_script注入脚本覆盖这个属性,但不要滥用,遵守目标站点的使用条款是底线。
3. 方案二:Puppeteer Extra——生态插件补全的路线
3.1 定位差异:它跟 Playwright 不是二选一
Puppeteer 本身是 Chrome 团队维护的 Node.js 库,控制 Chromium 的能力跟 Playwright 有重叠。但Puppeteer Extra这个社区封装版的价值在于插件生态。OpenClaw 用户经常需要的一些“偏门”能力,比如模拟真人鼠标轨迹、绕过简单的指纹检测、自动处理验证码弹窗,在 Puppeteer Extra 里都有现成插件。
它的设计思路是“核心保持精简,能力通过插件挂载”。你装一个puppeteer-extra-plugin-stealth,它就自动帮你抹掉一堆自动化特征;装puppeteer-extra-plugin-adblocker,页面广告直接被拦掉,采集时少很多干扰。这种按需组合的方式,比 OpenClaw 那种大而全但每样都不精的封装要灵活。
3.2 插件机制与常用插件说明
Puppeteer Extra 的插件通过puppeteer.use()注册,执行顺序会影响效果。常用的几个:
| 插件名称 | 作用 | 适用场景 |
|---|---|---|
| stealth | 修改浏览器指纹特征 | 需要降低被识别概率的采集 |
| adblocker | 拦截广告和追踪脚本 | 提升页面加载速度 |
| recaptcha | 辅助处理 reCAPTCHA 弹窗 | 表单提交类自动化 |
| user-data-dir | 持久化用户数据 | 需要保持登录态的任务 |
| anonymize-ua | 随机化 User-Agent | 多账号轮换场景 |
需要说明的是,这些插件的能力有边界,不是万能的。stealth 插件能改掉一些明显的自动化标记,但面对行为分析类的检测,效果有限。把它当成“减少麻烦”的工具,而不是“隐身”的工具。
3.3 配置示例与参数解读
const puppeteer = require('puppeteer-extra'); const StealthPlugin = require('puppeteer-extra-plugin-stealth'); const AdblockerPlugin = require('puppeteer-extra-plugin-adblocker'); puppeteer.use(StealthPlugin()); puppeteer.use(AdblockerPlugin({ blockTrackers: true })); (async () => { const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); await page.setViewport({ width: 1366, height: 768 }); await page.goto('https://example.com', { waitUntil: 'networkidle2' }); const content = await page.content(); console.log(content.length); await browser.close(); })();headless: 'new'是 Puppeteer 新版推荐的无头模式,跟旧版true的行为有差异,渲染更接近真实浏览器。waitUntil: 'networkidle2'表示网络连接数少于等于 2 个时才认为加载完成,比load事件更宽松,适合有异步请求的页面。
3.4 踩坑记录:插件冲突与版本锁定
我遇到最多的问题是插件之间的执行顺序冲突。比如 stealth 和 adblocker 同时启用时,如果 adblocker 先注册,stealth 的某些指纹修改会被覆盖。解决办法是调整use()的调用顺序,把 stealth 放在前面。
另一个坑是版本漂移。Puppeteer Extra 的插件更新频率不一致,有时候核心库升级了,某个插件还没跟上,跑起来就报错。我的做法是在package.json里锁定所有相关依赖的版本号,不用^或~,等确认新版本稳定后再统一升级。这个习惯帮我省了很多排查时间。
4. 方案三:Selenium 4 + 网格部署——老牌工具的现代化转身
4.1 Selenium 4 带来了哪些实质变化
Selenium 是自动化测试领域的老牌选手,很多人对它的印象还停留在“配置麻烦、速度慢、API 啰嗦”。但 Selenium 4 的改动其实挺大:原生支持 CDP,可以直接调用 Chrome DevTools 的能力;相对定位器,支持“在某个元素上方/下方/附近”这种语义化定位;网格(Grid)架构重写,部署分布式节点比以前简单不少。
对于原来用 OpenClaw 做大规模并发采集的团队,Selenium Grid 的价值在于横向扩展。你可以在多台机器上注册节点,中央调度器自动分配任务,单机跑 5 个浏览器、10 台机器就是 50 个并发。OpenClaw 在这方面几乎没有官方支持,得自己造轮子。
4.2 网格部署的实操步骤
先启动中央调度器:
java -jar selenium-server-4.x.jar hub然后在各节点机器上注册:
java -jar selenium-server-4.x.jar node --hub http://<hub-ip>:4444节点会自动检测本机安装的浏览器和驱动。如果驱动版本不对,Selenium 4 内置了Selenium Manager,会尝试自动下载匹配的驱动,省去了手动配置 PATH 的步骤。
客户端代码跟单机版几乎一样,只是把连接地址指向 hub:
from selenium import webdriver from selenium.webdriver.common.by import By options = webdriver.ChromeOptions() options.add_argument("--headless=new") driver = webdriver.Remote( command_executor="http://<hub-ip>:4444", options=options ) driver.get("https://example.com") elements = driver.find_elements(By.CSS_SELECTOR, ".item") for el in elements: print(el.text) driver.quit()4.3 并发参数怎么算:一个实际案例
假设你有 3 台节点机,每台 8 核 16G 内存。Chrome 单实例在 headless 模式下大约占 300-500MB 内存,CPU 占用取决于页面复杂度。保守估计每台机器跑 6 个实例,3 台就是 18 个并发。如果页面有大量 JS 计算,降到 4 个实例更稳。
调度器的maxSession和maxInstances参数要配合设置。maxInstances控制单个节点能同时跑多少个同类型浏览器,maxSession控制总会话数。设置不当会出现“节点明明有空闲资源但任务排队”的情况。我的经验是maxInstances设为实际能承载的 80%,留点余量给系统调度。
4.4 注意事项:网格模式的隐性成本
- 网络延迟:客户端到 hub、hub 到节点的通信都走网络,截图和 DOM 查询这类高频操作会明显变慢。建议客户端和 hub 部署在同一内网。
- 会话清理:脚本异常退出时,会话可能残留在节点上不释放。要配置
session-timeout和定期清理任务,否则节点会被僵尸会话占满。 - 日志分散:每个节点各自记日志,排查问题时要挨个登录查看。建议统一收集到中央日志服务,或者至少配置远程日志输出。
5. 方案四:DrissionPage——轻量级国产方案的实用主义
5.1 它为什么在国内采集圈流行起来
DrissionPage 是一个国内开发者维护的项目,定位介于 requests 和 Selenium 之间。它的核心卖点是**“能发请求也能控浏览器,两者可以随时切换”**。很多采集场景其实不需要完整渲染页面,直接发 HTTP 请求拿数据更快;但遇到需要登录、需要执行 JS 的页面,又得切到浏览器模式。OpenClaw 在这两种模式之间切换很笨重,DrissionPage 把这件事做得比较顺。
另一个优势是中文文档完善,API 命名贴近中文使用习惯,新手看文档就能上手,不用在英文 issue 里翻找答案。对于英语阅读有压力的同学,这一点很实际。
5.2 双模式切换的核心逻辑
from DrissionPage import ChromiumPage, SessionPage # 请求模式,速度快,适合静态页面 sp = SessionPage() sp.get('https://example.com/api/list') data = sp.json # 直接解析 JSON 响应 # 浏览器模式,适合动态渲染页面 cp = ChromiumPage() cp.get('https://example.com/dashboard') cp.ele('text:登录').click() cp.ele('#username').input('user') cp.ele('#password').input('pass') cp.ele('text:提交').click()两种模式共享 cookie 和会话状态,登录一次后可以切到请求模式批量拉数据,省去重复渲染的开销。这个设计在采集列表页+详情页的场景里特别高效:列表页用浏览器模式翻页,拿到详情链接后切请求模式并发抓取。
5.3 元素定位的语法糖
DrissionPage 的定位语法比原生 Selenium 简洁不少:
ele('text:提交')按文本找元素ele('@class=item')按属性找ele('tag:div@@class=card')组合条件eles('xpath://div[@id="list"]/a')直接用 XPath
这种写法减少了样板代码,脚本可读性提升明显。不过要注意,语法糖背后还是走的选择器引擎,复杂页面上性能跟原生方式差不多,不要指望有数量级提升。
5.4 适用边界与不适用场景
DrissionPage 适合中小规模的采集和自动化任务,单机几十个并发、页面结构相对稳定的场景。它的分布式能力比较弱,没有官方的网格方案,要大规模并发得自己封装。另外,它的浏览器控制基于 CDP,目前主要支持 Chromium 内核,Firefox 和 WebKit 的支持有限。如果你的项目需要跨内核测试,还是得回到 Playwright 或 Selenium。
6. 方案五:Stagehand——AI 驱动的新思路
6.1 用自然语言操作浏览器的尝试
Stagehand 是这两年在 AI 自动化浪潮里冒出来的项目,思路跟前面几款完全不同。它把大语言模型接进浏览器控制流程,你可以用自然语言描述操作意图,比如“找到价格最低的商品并加入购物车”,它来负责翻译成具体的点击和输入动作。
这个方向解决的是页面结构频繁变化的痛点。传统脚本靠选择器定位元素,页面一改版就失效;Stagehand 靠语义理解,只要页面上还有“加入购物车”这个按钮,不管它的 class 叫什么、位置怎么变,都能找到。对于维护成本敏感的项目,这个特性很有吸引力。
6.2 工作原理与依赖条件
Stagehand 底层还是用 Playwright 控制浏览器,上面加了一层“观察-决策-执行”的循环。它会把当前页面的可交互元素提取出来,连同你的指令一起发给语言模型,模型返回下一步该操作哪个元素,Stagehand 执行后再进入下一轮。
这意味着它有几个硬性依赖:需要能调用语言模型的 API,每次操作都有网络往返延迟,执行成本比传统脚本高。一个原本 10 秒跑完的流程,用 Stagehand 可能要 30 秒以上,因为每步都要等模型响应。
6.3 代码示例与成本估算
from stagehand import Stagehand stagehand = Stagehand( model="gpt-4o", headless=True ) page = stagehand.page page.goto("https://example.com/shop") page.act("搜索框输入'无线耳机'并回车") page.act("点击第一个搜索结果的标题") price = page.extract("提取当前页面的商品价格") print(price)act方法执行动作,extract方法提取数据。每次调用都会消耗模型 token。按当前主流模型的价格估算,一个包含 20 步操作的流程,成本大概在几毛到几块钱之间,取决于页面复杂度和模型选择。批量跑任务时这个成本要算进预算。
6.4 当前阶段的局限与适用建议
Stagehand 目前还不适合高频、大批量的任务,延迟和成本都扛不住。它更适合低频但页面多变的场景,比如每周跑一次的竞品价格监控、需要人工判断的复杂表单填写。另外,模型偶尔会“理解错”指令,执行了错误的操作,所以关键步骤后面要加校验逻辑,不能完全放手。
我的建议是把它当成辅助工具而不是主力方案。用传统脚本处理稳定的主流程,把容易变化、需要语义判断的环节交给 Stagehand,两者结合着用。
7. 五款方案横向对比与选型建议
7.1 关键维度对照表
| 维度 | Playwright | Puppeteer Extra | Selenium 4 Grid | DrissionPage | Stagehand |
|---|---|---|---|---|---|
| 上手难度 | 低 | 中 | 中高 | 低 | 中 |
| 跨浏览器 | 三内核 | Chromium 为主 | 多内核 | Chromium 为主 | Chromium |
| 分布式支持 | 需自建 | 需自建 | 官方网格 | 弱 | 无 |
| 插件生态 | 中 | 丰富 | 丰富 | 少 | 少 |
| 执行速度 | 快 | 快 | 中 | 快 | 慢 |
| 维护活跃度 | 高 | 高 | 高 | 中 | 中 |
| 适合场景 | 通用自动化 | 采集+反检测 | 大规模测试 | 轻量采集 | 多变页面 |
7.2 按场景选型的具体建议
如果你是个人开发者做数据采集,页面结构相对稳定,优先考虑 DrissionPage 或 Playwright。前者中文友好、双模式切换省资源;后者生态成熟、长期维护有保障。
如果你是团队做测试自动化,需要覆盖多种浏览器和大量并发,Selenium 4 Grid 是稳妥选择,虽然配置麻烦点,但架构成熟、资料多。
如果你需要处理反检测和复杂插件,Puppeteer Extra 的插件生态是优势,但要接受 Node.js 技术栈。
如果你的目标页面经常改版,维护脚本的人力成本高,可以试试 Stagehand,但要控制使用频率,算好成本账。
7.3 迁移成本评估:从 OpenClaw 换过来要改多少
迁移成本主要看两点:API 映射的复杂度和现有脚本的量级。OpenClaw 的核心操作无非是打开页面、找元素、点击、输入、提取文本,这些在五款方案里都有对应方法,逐个替换不算难。真正花时间的是等待逻辑的重写,OpenClaw 的等待机制跟这几款都不一样,需要重新调整超时和重试策略。
我的做法是先把一个代表性脚本迁移过去跑通,记录下遇到的问题和解决方式,形成一份内部迁移笔记,再批量处理其他脚本。这样比一上来就全面铺开要稳,也能提前发现坑。
8. 实操中反复踩到的几个共性问题
8.1 无头模式与有头模式的差异
很多脚本在本地有头模式下跑得好好的,放到服务器无头模式就出问题。常见原因包括:视口尺寸不同导致元素布局变化、字体缺失导致文本提取异常、GPU 加速不可用导致渲染差异。解决办法是在无头模式下显式设置视口尺寸,安装必要字体,必要时禁用 GPU 相关特性。
8.2 动态加载页面的等待策略
不要用固定 sleep,这是最容易被页面速度波动坑到的做法。优先用等待特定元素出现或等待网络空闲。如果页面有无限滚动,要结合滚动到底部和元素计数来判断是否加载完毕。Playwright 的wait_for_load_state('networkidle')和 Selenium 的WebDriverWait配合自定义条件,都能实现比较可靠的等待。
8.3 会话保持与登录态复用
需要登录的任务,每次跑都重新登录效率太低。通用做法是首次登录后保存 cookie 或 storage state,后续任务直接加载。Playwright 的context.storage_state()可以导出登录态到文件,下次new_context(storage_state='state.json')直接复用。Selenium 和 DrissionPage 也有类似的 cookie 存取机制。注意登录态有有效期,要加失效检测和自动重登逻辑。
8.4 资源清理与进程管理
浏览器实例不关干净,跑一段时间后内存会被吃满。除了在代码里确保close()被调用,还要在系统层面加监控,定期清理僵尸进程。用 Docker 跑的话,给容器设置内存上限,超了自动重启,比人工盯着省心。
9. 我个人在实际操作中的体会
这五款方案我都在真实项目里用过至少一轮,最大的感受是:没有银弹,只有匹配。OpenClaw 当年能流行,是因为它在那个时间点把一堆零散能力打包得还算顺手;现在这些替代方案各自在某个维度上做得更深,但也都放弃了“大而全”的路线。
如果非要给一个起步建议,我会说:先用 Playwright 把基础能力跑通,遇到反检测需求再引入 Puppeteer Extra 的插件思路,需要大规模并发时上 Selenium Grid,轻量任务用 DrissionPage 省资源,页面多变且预算允许时试试 Stagehand。这套组合不是理论推演,是我自己项目里实际演进出来的路径。
最后分享一个小技巧:不管用哪款工具,都建议在项目里维护一个**“选择器健康检查”脚本**,定期跑一遍核心页面的元素定位,发现失效的提前修,别等到任务跑挂了才去排查。这个习惯帮我减少了很多半夜被报警叫醒的情况。