☰
VBA浏览器自动化利器SeleniumBasic:从入门到实战
2026/10/21 16:33:22 网站建设 项目流程

做了多年VBA和VBScript开发,我一直觉得“浏览器自动化”是又爱又恨的领域。爱的是它能轻松处理重复的网页操作和数据采集,恨的是传统方案太不争气。用IE对象控制浏览器,新页面一点动态渲染就抓瞎;用XMLHTTP去模拟请求,遇到要执行JS的表单,直接歇菜。后来接触了SeleniumBasic,这套路才彻底走通,它是被封装好的WebDriver库,让我在VBA里像写普通过程一样去控制Chrome、Firefox和IE,不需要部署Java、不需要额外跑服务端,配置起来比官方Selenium省心得多。

写这篇文章,主要是想给还在用老办法和网页斗智斗勇的朋友指条路。SeleniumBasic适合谁?你至少得会写一点VBA或者VBScript,Excel用得顺手,平时要处理网页登录、带条件的批量查询、数据回填、页面验证这类重复劳动。只要满足这些,接下来的内容能直接变成你的生产力工具。

1. 为什么VBA开发者离不开SeleniumBasic

1.1 它解决了什么痛点

以前VBA采集网页,最常用的无非两种方案。一种是IE对象,CreateObject("InternetExplorer.Application"),很简单,但IE本身是一个还在维护的浏览器,面对现在大量用Ajax、Vue、React渲染的页面,能取到的内容非常有限,经常是Dom里面空空如也。另一种是WinHttp/XMLHttp请求,这个只能拿到服务端返回的HTML源码,如果网页是动态生成数据,那只能对着空结果发愣。

SeleniumBasic的方案是模拟一个真实的浏览器操作过程。它封装了WebDriver协议,启动Chrome、Firefox之后,由浏览器自己渲染页面、执行JavaScript,再去DOM中寻找元素、点击、输入、读取内容。这意味着你把网页正常使用时的操作流程录下来,用代码重写一遍,基本都能跑通。现在许多系统登录后还要检查“登录状态”“CSRF Token”,这些都会被浏览器自动处理好,不用在代码里额外维护会话细节。

1.2 它和Python生态的Selenium有什么不同

很多Python开发者用Selenium,格外羡慕它天然适合写爬虫。但站在VBA开发者的视角,不同点其实很关键。其一,不需要安装Python运行环境,不需要在一堆依赖包里挣扎;只要Office环境和SeleniumBasic的DLL都在,就能直接跑。其二,SeleniumBasic在“体系结构”上很轻,安装后自带完整驱动,不像官方Selenium那样要手动去找匹配的浏览器驱动。其三,VBA能直接和Excel表格深度结合。采集到的数据可以马上写进单元格,再从Excel读取参数作为输入,循环处理上百条数据,这个流程在Python里反而要多写不少代码。

当然,Python生态也有一些优势,比如并发处理能力更强、支持无头模式更成熟。但SeleniumBasic在VBA阵营里的匹配度是无可替代的,它把WebDriver的能力,以COM方式开放出来,让VBA/VBScript直接调用,这就是它的核心价值。

1.3 底层原理与适用场景

看一眼架构会更好理解。你的VBA代码,实际是调用SeleniumBasic.dll,这个DLL组件再通过HTTP协议去驱动本地的浏览器驱动进程(比如chromedriver.exe),最后由驱动进程把指令转发给浏览器执行。整个过程不需要外部服务,完全在本地闭环。

场景上,我整理过最常见的几种:

场景类型典型需求SeleniumBasic能做什么
数据采集从业务系统抓取表格数据登录系统、翻页、读取表格内容并写入Excel
批量操作大量录入/回填表单循环执行填写、点击、提交、验证结果
自动化测试检查系统功能是否正常模拟用户操作,检查流程是否成功
报表生成从多个系统汇总数据并行/串行打开页面,提取数据后汇总计算
办公辅助从网页复制数据到Excel替代复制粘贴,减少人为失误

2. SeleniumBasic的安装与初始化

2.1 安装文件和DLL注册

SeleniumBasic的安装包通常是一个压缩包,解压后你会看到几个关键文件,包括SeleniumBasic.dll、SeleniumBasic.x64.dll以及不同浏览器对应的驱动exe,比如chromedriver.exe、geckodriver.exe、IEDriverServer.exe等。建议将这些文件放在一个固定目录,比如C:\SeleniumBasic,方便后续维护。

注册DLL这一步常被忽略。有些朋友安装后直接在VBA里引用,发现“找不到Selenium Type Library”,基本就是没注册。注册方法:

  1. 以管理员身份打开命令提示符。
  2. 进入文件所在目录:cd C:\SeleniumBasic
  3. 执行注册命令:regsvr32 SeleniumBasic.dll

如果系统提示注册成功,再去VBA的“工具 → 引用”中就能看到SeleniumBasic的相关项目。如果是64位Office且需要64位环境,还要另外注册SeleniumBasic.x64.dll。这里有个常见坑,Office本身是32位的,哪怕Windows是64位,也只能注册32位版本,选错文件就会出现“模块找不到”或加载失败的报错。

2.2 浏览器的安装与驱动匹配

SeleniumBasic不是只要装上DLL就完事,它还需要一个真实浏览器,以及对应版本的驱动文件。比如用Chrome,就要求分辨出你的Chrome大版本,再换成匹配的chromedriver.exe。SeleniumBasic的早期版本是捆绑一批常用版本的驱动,如果浏览器更新到较新的大版本,原有驱动就无法启动,启动时直接报错,提示“The driver executable does not exist”或者sessionnotcreated。所以养成习惯,安装新版本后先确认驱动是否兼容,不兼容就去更新SeleniumBasic或者单独把新版本驱动的exe文件替换进去。

2.3 VBA最小化启动示例

在VBA中启动Chrome,代码量很少。先保证通过“工具 → 引用”勾选了SeleniumBasic对象库,然后新建模块写这段代码:

Sub DemoLaunch() Dim driver As New Selenium.ChromeDriver driver.Get "https://example.com" MsgBox "页面标题是:" & driver.Title driver.Quit End Sub

运行后,会看到一个新Chrome窗口打开example.com,弹窗显示页面标题,最后自动关闭。这就算环境跑通了。

VBScript里的写法也几乎一样:

Set drv = CreateObject("Selenium.ChromeDriver") drv.Get "https://example.com" MsgBox "页面标题是:" & drv.Title drv.Quit

是不是感觉没什么门槛?最难的往往是环境问题,一旦环境OK,后面自然就顺起来。

2.4 自动化的前置检查

浏览器被自动化驱动后,会在窗口顶部显示“Chrome正受到自动测试软件的控制”。这是正常现象,不是报错。但有一些页面检测到自动化环境后会人为限制互动,比如要求验证码。我的建议是:遇到验证码不要硬闯,停在当前状态,让人工手动完成一次,再让代码继续往下跑。市面上流传的自动绕过方案不仅不稳定,还可能违反平台规则,得不偿失。

3. 核心API精讲,从定位元素到表单操作

3.1 定位元素的几种方式

SeleniumBasic的核心操作就是“找元素”。可以在WebDriver或某个WebElement上调用查找方法。常见方法有:

driver.FindElementById("username") driver.FindElementByName("password") driver.FindElementByClassName("btn-primary") driver.FindElementByTagName("button") driver.FindElementByCss(".nav > li > a") driver.FindElementByXPath("//div[@id='main']/table/tbody/tr[2]/td[1]")

如果页面有多个满足条件的元素,使用FindElementsBy...方法,返回的是一个WebElements集合,可以遍历。还有一个需要注意的地方:SeleniumBasic的返回对象是Selenium.WebElement,但旧版本中有部分查找方法要写成FindElementByXPath这种命名,如果你使用的版本较老,方法名可能有些差异,写代码前先看一眼对象浏览器(F2)里的成员列表。

选哪个定位方式?我个人的经验:

  • 有id优先用id,id是页面中最稳定的属性。
  • 没有id,有name或者class也可以,但要留意class中是否包含动态生成的空格或序号。
  • XPath最强大,适合复杂结构,但写不好容易脆弱,反而不如多写几步。

例如CssSelector在SeleniumBasic里也支持,但如果你对CSS选择器语法不熟,直接用XPath更直白。

3.2 表单填写与点击

让我写一个有参考价值的例子。假设要登录一个业务系统,这个系统需要输入用户名、密码,再点击登录按钮,最后等待跳转:

Sub LoginSystem(driver As Selenium.WebDriver) driver.FindElementById("username").SendKeys "admin" driver.FindElementById("password").SendKeys "123456" driver.FindElementById("loginButton").Click End Sub

就这么简单。但有些细节要说明一下:

  • SendKeys是给输入框发送键盘输入,能模拟用户真实打字。
  • 在输入前最好先调用.Clear清空,防止输入框里已有默认值。
  • 如果输入框是密码框,SendKeys同样适用。
  • 下拉框如果用的是原生select标签,可以尝试调用.Select "选项文本";但很多现代页面用div模拟下拉,那就需要先点击下拉框,再点击弹出的选项元素。

比如处理自定义下拉:

driver.FindElementById("citySelect").Click Application.Wait Now + TimeSerial(0, 0, 1) driver.FindElementByXPath("//li[@data-value='beijing']").Click

这个思路在SeleniumBasic里能应对多数“假下拉框”。

3.3 多窗口、IFrame和弹窗

页面里出现iframe时,直接在原始DOM里寻找元素注定找不到。要先“切进去”。SeleniumBasic的做法是:

Dim frame As Selenium.WebElement Set frame = driver.FindElementByTagName("iframe") driver.SwitchToFrame frame ' 在iframe内部寻找元素 driver.FindElementById("innerInput").SendKeys "你好" ' 切回主文档 driver.SwitchToDefaultContent

如果是点击链接后弹出新窗口,想在新窗口里继续操作:

driver.SwitchToNextWindow

这个方法会切换到下一个窗口,再用driver.Title或driver.Url确认是否切换成功。多窗口切换时,为了保险,可以提前保存当前窗口句柄,切完再切回:

Dim mainHandle As String mainHandle = driver.WindowHandles(0) driver.SwitchToNextWindow ' 在新窗口里做完事后 driver.SwitchToWindow mainHandle

弹窗(JavaScript的Alert)属于另一种特殊处理。SeleniumBasic有driver.Alert对象,可以做Accept、Dismiss、SendKeys操作。一个常见的坑是,弹窗出现后如果再调用别的查找方法,程序会一直卡住,正确的姿势是发现弹窗后马上处理:

driver.Alert.Accept

3.4 显式等待与动态加载

现代网页几乎没有秒开的,登录、查询、翻页都有加载过程。如果页面还没渲染完成就去找元素,最常见的报错是“未找到指定元素”。这时候就得靠等待机制。SeleniumBasic没有默认的“隐式等待”选项,所以我一般是写一个自定义等待函数:

Public Function WaitElement(ByVal driver As Selenium.WebDriver, _ ByVal selectorType As String, _ ByVal selector As String, _ ByVal timeoutSeconds As Integer) As Selenium.WebElement Dim i As Integer On Error Resume Next For i = 1 To timeoutSeconds * 2 Set WaitElement = Nothing Select Case LCase(selectorType) Case "id" Set WaitElement = driver.FindElementById(selector) Case "class" Set WaitElement = driver.FindElementByClassName(selector) Case "xpath" Set WaitElement = driver.FindElementByXPath(selector) Case Else Set WaitElement = driver.FindElementByCss(selector) End Select If Not WaitElement Is Nothing Then Exit Function Application.Wait Now + TimeSerial(0, 0, 0.5) Next i End Function

这里的逻辑其实就是“轮询”:每半秒尝试查找一次,超过指定秒数就放弃。这种轮询方式比直接Sleep一大坨要稳定得多,因为加载时间不稳定时,等待1秒不够,等待5秒可能又太多。轮询能刚好等到元素出现再继续。

用起来也简单:

Dim obj As Selenium.WebElement Set obj = WaitElement(driver, "class", "result-table", 10) If obj Is Nothing Then MsgBox "超时未找到结果表格" Else ' 执行后续操作 End If

4. 实战:用SeleniumBasic写一个批量数据采集工具

4.1 任务拆解与准备工作

这里用一个模拟场景说明完整的采集流程。假设我们要在某网站查询一批订单号,页面需要登录,登录后输入订单号查询,页面返回结果后我们读取订单当前状态。

先拆解步骤:

  1. 启动Chrome,打开登录页。
  2. 输入用户名密码登录。
  3. 等待登录成功,进入查询页。
  4. 循环读取Excel中待查询的订单号。
  5. 在查询输入框逐个输入订单号,点击查询。
  6. 等待查询结果渲染完成。
  7. 提取订单状态,写回Excel。

这是非常经典的数据采集流程,几乎每家公司都可能有类似需求。

4.2 核心采集代码走读

先定义一个主过程,参数是Sheet中从第2行开始到有数据的最后一行:

Sub BatchQueryOrders() Dim driver As New Selenium.ChromeDriver Dim ws As Worksheet Dim lastRow As Long Dim i As Long Dim orderId As String Dim statusEl As Selenium.WebElement Set ws = ThisWorkbook.Sheets("订单") lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row driver.Get "https://demo-system.com/login" ' 登录 driver.FindElementById("username").SendKeys "tester" driver.FindElementById("password").SendKeys "test123" driver.FindElementById("loginBtn").Click ' 等待登录后跳转 Application.Wait Now + TimeSerial(0, 0, 3) For i = 2 To lastRow orderId = ws.Cells(i, 1).Value If orderId <> "" Then ' 回到查询页(这里假设登录后已在查询页,也可用driver.Get重新打开) driver.FindElementById("searchKey").Clear driver.FindElementById("searchKey").SendKeys orderId driver.FindElementById("searchBtn").Click ' 等待结果出现 Set statusEl = WaitElement(driver, "class", "status-text", 8) If Not statusEl Is Nothing Then ws.Cells(i, 2).Value = statusEl.Text Else ws.Cells(i, 2).Value = "未查到结果" End If ' 每次查询间隔一点时间,避免请求过密 Application.Wait Now + TimeSerial(0, 0, 1) End If Next i driver.Quit MsgBox "批量查询完成" End Sub

这里有个很关键的点:查询按钮点击后,结果状态可能不是立即渲染的,我通过自定义的WaitElement去等待.status-text这个元素出现,比固定Sleep更稳妥。

4.3 翻页与大量数据的拉取

如果查询结果不止一页,需要在循环里不断点击“下一页”按钮,直到没有下一页。我常用的写法:

Do ' 采集当前页的数据 Call ExtractCurrentPage(driver, ws, rowIndex) ' 查找“下一页”按钮 Set nextBtn = Nothing On Error Resume Next Set nextBtn = driver.FindElementByXPath("//a[contains(@class,'next')]") On Error GoTo 0 If nextBtn Is Nothing Then Exit Do nextBtn.Click Application.Wait Now + TimeSerial(0, 0, 2) Loop

其中ExtractCurrentPage里再写一个循环,遍历table底下的行,把每个单元格的文本读出来。

4.4 把临时脚本变成运维工具

脚本一旦频繁使用,就要考虑日志和异常处理。否则跑到一半卡住,去任务管理器里结束chrome都来不及。

我会在循环外加重试机制:

Dim retryCount As Integer retryCount = 0 Do On Error Resume Next driver.FindElementById("searchBtn").Click If Err.Number = 0 Then Exit Do Err.Clear retryCount = retryCount + 1 Application.Wait Now + TimeSerial(0, 0, 2) Loop While retryCount < 3

还需要把每行数据采集过程用On Error GoTo包住,如果某一行失败,记录到日志列,然后继续下一行。千万不要让整个循环因为一个数据异常而崩溃。

5. 常见报错与排查实录

5.1 高频错误速查表

报错现象可能原因解决思路
找不到元素 “element not found”页面未加载完,或选择器不对用WaitElement轮询,或调整XPath/CSS选择器
SessionNotCreatedExceptionChrome版本与驱动不匹配下载对应版本的chromedriver
启动时弹出“系统找不到指定的程序”驱动exe被删,或路径不对检查SeleniumBasic安装目录里的driver是否存在
注册DLL时报“模块无法注册”32位/64位架构不匹配确认Office位数,换对应DLL注册
运行时错误“未设置对象变量或With block变量”查找语句没找到元素先判断元素是否为空,再调用后续方法
屏幕上的元素明明存在却click无效页面存在遮罩层或元素不可见先点击遮罩关闭,或改用JS方式滚动到可见位置
浏览器不断弹出广告/新窗口网站机制用driver.WindowHandles过滤并关闭非目标窗口

5.2 定位不到的顽固元素

这是SeleniumBasic使用中最普遍的问题。元素在浏览器里肉眼可见,但代码就是找不到。原因通常有这么几种:

  • 元素在iframe里。这个前面提过,需要先SwitchToFrame。
  • 元素是动态生成的,页面里暂时不存在,需要等待。
  • 元素隐藏属性不是“display:none”,而是用CSS类控制,可视但不可点击,这时可以先通过.ClearText? 不对,正确做法是滚动到元素位置或使用JavaScript点击。SeleniumBasic提供了driver.ExecuteScript方法,可以执行一段JS:
driver.ExecuteScript "arguments[0].click();", element

这种基于JS的点击方式能绕过一些元素被遮挡、被其他层盖住导致的Click无效问题。

  • XPath写得太死,比如//td[2],页面结构偶有变化,建议用更长但更稳定的路径,或通过文字内容定位://div[contains(text(), '订单')]。

5.3 页面加载耗时太长

如果一个页面本身加载很慢,但只需操作一部分内容,可以考虑先设置页面加载策略,让浏览器不要等待整个页面所有资源都加载完。在SeleniumBasic中可能没有直接暴露该参数,但可以通过driver.capabilities? 这个比较复杂。更实际的做法是:启动后立刻用WaitElement去等关键元素,不需要等页面完全就绪。如果确实要操作一个尚未渲染完的按钮,就只能在按钮出现后再等一小段时间。

5.4 自动化操作识别的防御

部分网站会检测浏览器是否自动化控制,进而限制操作。我不会建议规避人工验证,但有一些现实经验是:不要用太快的操作节奏,比如连续点击之间加随机间隔,模拟真人盯屏幕的时间;不要在并发请求上做得太夸张。这些做法本质上是降低对目标系统的压力,也更容易把事情做长久。验证码往往在规定次数后出现,遇到就让人员介入,手动完成后继续。

6. 必须处理好的收尾、清理和代码组织

6.1 释放浏览器进程与内存管理

如果代码结束时只是关掉了浏览器窗口,没有调用driver.Quit,那么chromedriver.exe进程很可能残留在任务管理器里。残留进程多了,之后的自动化就越来越不稳定。正确做法是:

driver.Quit Set driver = Nothing

如果因为异常跳过了这行,可以在过程开头声明一个布尔变量并配合On Error GoTo,确保过程结束后一定会执行清理。万一脏进程还是出现了,在任务管理器里结束chromedriver.exe和chrome.exe是最后的兜底。

6.2 封装成通用类模块

项目写多了就会发现,大量代码是重复的。公共等待函数、登录逻辑、Excel交互,都可以抽到一个标准模块。进一步可以封装一个类,比如CreateDriver、Login、SearchOrder、ReadStatus。在VBA里这算高级玩法,但收益很直观,一个公司的不同部门可以共享同一套类,后续维护只需要改类内部的方法,而不是改几十个脚本。

给别人维护时,类的优势尤其明显。写一个简单的类,外部使用变成:

Dim bizTools As New OrderTool bizTools.InitializeDriver "chrome" bizTools.Login "tester", "test123" bizTools.QueryOrders ws, "A", "B" bizTools.Cleanup

这里OrderTool就是你的类模块,里面封装了前面提到的所有底层层逻辑。这样主程序看起来顺畅清晰,也不会因为页面的偶尔改版影响整体。

6.3 从VBA迁移到VBScript和批处理

有些环境不想要Excel,只想用VBScript实现自动化。其实SeleniumBasic完全支持。VBScript的好处是双击就能跑,无需打开Excel。可以写成.ps脚本由计划任务调用,或者直接把它挂到Windows的定时任务里。这在日常运维里特别实用,比如每天早上自动登录内部系统、抓取报表并发送至指定目录。

Set drv = CreateObject("Selenium.ChromeDriver") drv.Get "https://demo-system.com/report" ' ... 操作逻辑 drv.Quit

注意VBScript对类型不那么敏感,错误处理靠On Error Resume Next,如果之前VBA代码里用了Application.Wait,VBScript里没有Workbook对象,需要改用WScript.Sleep 2000。

6.4 版本维护与项目升级

SeleniumBasic的维护频率并不高,但如果你长期依赖它,最好固定一套经过验证的浏览器版本,不要总把浏览器升级到最新的大版本,却忘了检查SeleniumBasic是否跟进。内部工具的稳定性,比追新重要得多。若实验室或团队有条件,建议把驱动文件、安装包、注册脚本放到共享目录里,新同事接手时可以一键完成环境搭建。

最后的一点个人经验

接触SeleniumBasic这几年,最大的体会是,它能省下大量纯手工操作的精力,但不是“装完就能躺赢”的工具。浏览器版本要盯着,页面结构细微变化需要同步修改,等待逻辑更是要反复打磨。把核心流程封装成类模块之后,日常维护会轻松很多。我现在越来越倾向于让Python去处理更复杂的定时任务和数据处理,但凡是Excel和VBA周围的流程,SeleniumBasic依然是最省心的选择。如果你正准备尝试,建议从一个小场景开始,比如自动登录系统,跑通环境,再一步步加上查询、翻页、采集、写回Excel的流程。只要走通一条完整链路,后面扩展就是复制、剪贴、加参数的事。

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

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

立即咨询