1. 项目概述:当VBA遇见SeleniumBasic
如果你和我一样,常年和Excel、Word、PPT打交道,那你一定对VBA(Visual Basic for Applications)又爱又恨。爱的是,它确实能帮我们处理很多重复性的报表整理、数据清洗工作,把我们从“表哥表姐”的苦海中稍微解放出来一点。恨的是,它的能力边界太明显了——VBA本质上是个“办公室宅男”,它最擅长在Office套件内部折腾,一旦需要它走出办公室,去网页上抓个数据、去外部系统里填个表单,它就立刻显得力不从心。你得调用各种复杂的API,处理千奇百怪的验证码和动态加载,代码写起来又长又容易出错。
所以,当几年前我第一次接触到 SeleniumBasic 这个项目时,感觉就像给VBA这位老将配上了一套外骨骼装甲。Selenium本身是Web自动化测试领域的王者,用它可以模拟真人操作浏览器,点击、输入、下拉、截图,无所不能。而SeleniumBasic,简单来说,就是为VBA环境量身打造的一个Selenium客户端库。它让你能在VBA里,直接调用Selenium的强大功能,去控制Chrome、Firefox、Edge这些主流浏览器。
这带来的改变是革命性的。想象一下这些场景:你每天需要从十几个不同的内部管理网站导出销售数据,然后手工复制粘贴到Excel里做分析;或者,公司新上了一套Web版的报销系统,你每个月都要手动录入上百张发票信息;再或者,你需要定时监控某个竞品网站的价格变动。以前,这些要么靠人力,要么得请IT部门开发专门的爬虫或接口,周期长、成本高。现在,你坐在自己的工位上,用你最熟悉的Excel VBA,写几十行代码,就能让浏览器自动完成所有这些操作。数据自动抓取、自动填入Excel指定位置,甚至自动生成图表和报告。这就是 “办公自动化的智能革命” 的核心——将外部Web世界的海量信息和交互能力,无缝接入我们最熟悉的Office生产力工具中,实现内外数据的自动流转与处理。
2. 核心原理与工具选型解析
2.1 为什么是SeleniumBasic,而不是其他?
在VBA里操作浏览器,并不是只有SeleniumBasic一条路。常见的还有:
- IE对象(InternetExplorer.Application) :这是VBA“祖传”的方法。但IE浏览器本身已被淘汰,对现代网页(大量使用JavaScript、CSS3)的支持极差,速度慢且不稳定。
- WinHttp或XMLHTTP对象 :这类方法直接发送HTTP请求获取网页源码。它的优点是速度快,但缺点是无法处理JavaScript渲染的页面。现在绝大多数网站都是动态加载内容,你用这种方法拿到的经常是一个没有数据的空壳HTML。
- 第三方COM组件或付费工具 :这类工具通常封装较好,但可能收费,且灵活性和社区支持不如开源项目。
SeleniumBasic的优势 就在于它找到了一个完美的平衡点:
- 协议级支持 :它通过WebDriver协议与浏览器通信。这是W3C标准,意味着浏览器厂商(如Google、Microsoft)自己就必须实现并维护这套接口,所以兼容性和稳定性是最好的。
- 真实浏览器环境 :它驱动的是你电脑上真实的Chrome或Firefox。这意味着网页所有的JavaScript、CSS、Cookie、Session都会正常加载和执行,和你手动打开浏览器看到的效果一模一样。
-
VBA原生集成
:SeleniumBasic被封装成一个标准的VBA引用库(
.dll或.tlb)。你在VBA编辑器里添加引用后,就能像使用Excel.Application或Scripting.Dictionary一样,使用New WebDriver来创建浏览器对象,语法非常自然。 - 强大的元素定位能力 :它提供了多达8种元素定位方式(ID、Name、XPath、CSS Selector等),让你能精准地找到页面上的任何一个按钮、输入框或数据表格。
注意 :SeleniumBasic本质上是一个“桥梁”。它本身不包含浏览器驱动(如chromedriver.exe)。你需要根据你电脑上安装的浏览器版本,去下载对应的驱动,并放在SeleniumBasic指定的目录或系统PATH路径下。这是新手最容易卡住的第一步。
2.2 环境搭建与核心对象模型
动手之前,我们需要把环境搭好。整个过程就像组装一台机器,步骤清晰,一步都不能错。
第一步:安装SeleniumBasic
去GitHub上找到
SeleniumBasic
项目,下载最新的发布版本(通常是一个
.exe
安装包)。直接运行安装,它会将必要的库文件注册到你的系统。安装完成后,你的电脑上会多出一个文件夹,里面包含了SeleniumBasic的核心库文件。
第二步:准备浏览器驱动 这是关键。假设我们用Chrome。
-
打开Chrome,在地址栏输入
chrome://settings/help,查看你的Chrome版本号(例如, 120.0.6099.110)。 - 打开ChromeDriver的官方下载网站。 这里有个大坑 :驱动版本必须与你的Chrome主版本号完全一致(比如都是120)。如果版本不匹配,SeleniumBasic将无法启动浏览器。
-
下载对应的
chromedriver.exe。 -
将下载的
chromedriver.exe复制到SeleniumBasic的安装目录下(安装时会有提示),或者复制到C:\Windows\目录下,这样系统就能在任何位置找到它。
第三步:在Excel VBA中引用
-
打开Excel,按
ALT + F11进入VBA编辑器。 -
点击菜单栏的
工具->引用。 -
在弹出的列表中,找到并勾选
SeleniumWrapper Type Library。如果没找到,可以点击浏览,找到SeleniumBasic安装目录下的.tlb文件(通常是SeleniumWrapper.tlb)。 - 点击确定,引用就添加成功了。
现在,我们来认识一下最核心的几个对象,这是你编写所有自动化脚本的基石:
-
WebDriver
:这是总指挥。你通过
Dim driver As New WebDriver来创建一个浏览器实例。后续所有操作,如打开网页、查找元素,都通过这个driver对象进行。 -
WebElement
:代表页面上的一个元素,比如一个按钮 (
<button>)、一个输入框 (<input>)、一段文字 (<span>)。当你使用driver.FindElement方法后,返回的就是这个对象。你可以对它进行点击.Click、输入文本.SendKeys、获取属性.Attribute等操作。 -
By
:这是一个用于辅助定位元素的类。你通常不会直接创建它,而是使用它的静态方法,如
By.ID(“username”),By.CssSelector(“.btn-submit”),来告诉driver你要找什么。
一个最简单的“Hello World”脚本看起来是这样的:
Sub TestSeleniumBasic()
Dim driver As New WebDriver
driver.Start “chrome”, “” ‘启动Chrome浏览器
driver.Get “https://www.baidu.com” ‘导航到百度
‘找到搜索框,输入关键词
driver.FindElement(By.ID(“kw”)).SendKeys “SeleniumBasic”
‘找到“百度一下”按钮并点击
driver.FindElement(By.ID(“su”)).Click
‘等待一下,查看结果
Application.Wait Now + TimeValue(“00:00:05”)
driver.Quit ‘关闭浏览器
End Sub
运行这段代码,你会看到Chrome自动打开,访问百度,搜索关键词,然后关闭。这一刻,你就已经推开了办公自动化新世界的大门。
3. 实战:构建一个网页数据抓取与Excel整合的自动化流程
光说不练假把式。我们来看一个真实的业务场景: 每日自动从某电商后台抓取商品销售数据,并整理到Excel日报模板中 。
3.1 需求分析与流程设计
假设我们公司有一个自研的电商管理后台,每天需要登录后,在“销售报表”页面,选择昨日日期,导出数据。手动操作需要:登录 -> 点击报表菜单 -> 选择日期 -> 点击查询 -> 等待页面加载 -> 复制表格数据 -> 粘贴到Excel -> 调整格式。
我们的自动化目标是将以上步骤全部用VBA脚本完成,最终在Excel里得到一个格式规整的日报表。流程设计如下:
- 启动与导航 :启动浏览器,打开后台登录页。
- 登录认证 :自动输入用户名、密码,点击登录。处理可能的验证码(简单图片验证码可考虑OCR组件,复杂滑块验证码则此方案不适用,需评估)。
- 页面跳转与交互 :跳转到报表页面,使用脚本操作日期选择器,选择“昨天”的日期,点击查询按钮。
-
数据等待与提取
:等待表格数据加载完成,然后逐行、逐列地读取网页表格(
<table>)中的数据。 - 数据写入与格式处理 :将读取到的数据写入Excel工作表的指定位置,并应用预设的单元格格式、字体、边框。
- 清理与报告 :关闭浏览器,在Excel中生成一个简单的日志,记录抓取是否成功、数据行数。
3.2 关键代码实现与难点突破
我们分步拆解其中的关键代码和可能遇到的“坑”。
步骤一:稳健的启动与隐式等待
Dim driver As New WebDriver
driver.Start “chrome”, “” ‘第二个参数可以指定浏览器路径,留空则用系统默认
driver.Timeouts.PageLoad = 30000 ‘设置页面加载超时为30秒
driver.Timeouts.ImplicitWait = 10000 ‘设置隐式等待10秒
实操心得 :
ImplicitWait(隐式等待)非常重要。它告诉driver,在查找元素时,如果立即没找到,不要马上报错,而是轮询查找,最多等10秒。这能有效解决因网络或脚本延迟导致的元素未加载问题。
步骤二:登录与可能存在的验证码处理
driver.Get “https://后台登录地址”
driver.FindElement(By.ID(“userName”)).SendKeys “your_username”
driver.FindElement(By.ID(“password”)).SendKeys “your_password”
‘ — 处理简单验证码(假设验证码图片id为’captchaImg’,输入框id为’captcha’)—
‘ 此方法仅适用于字符型验证码,且需要额外OCR库支持,仅供参考思路
‘ Dim captchaElement As WebElement
‘ Set captchaElement = driver.FindElement(By.ID(“captchaImg”))
‘ captchaElement.CaptureScreenshot “C:\temp\captcha.png”
‘ ‘ 调用本地或云OCR API识别图片中的文字,得到code
‘ Dim captchaCode As String
‘ captchaCode = YourOCRFunction(“C:\temp\captcha.png”) ‘ 假设的OCR函数
‘ driver.FindElement(By.ID(“captcha”)).SendKeys captchaCode
‘ — 处理结束 —
driver.FindElement(By.CssSelector(“.login-btn”)).Click ‘点击登录按钮
重要提示 :如果网站验证码非常复杂(如滑块、点选),纯Selenium方案很难破解。此时应考虑:
- 联系系统管理员,为自动化账号申请免验证码IP白名单或API接口。
- 使用商业验证码识别服务(有成本)。
- 手动干预一次,然后使用浏览器Cookie持久化功能,避免下次登录。SeleniumBasic可以获取和设置Cookie:
driver.GetCookie(“session_id”),driver.AddCookie “name”, “value”, “domain”。
步骤三:操作日期选择器等复杂交互 很多后台的日期选择器是JavaScript组件,直接输入可能无效。我们需要模拟点击操作。
‘ 假设日期输入框点击后会弹出日历层
driver.FindElement(By.ID(“reportDate”)).Click ‘点击输入框,弹出日历
Application.Wait Now + TimeValue(“00:00:01”) ‘短暂等待日历动画
‘ 方法1:如果日历支持直接输入(input框)
‘ driver.FindElement(By.ID(“reportDate”)).Clear
‘ driver.FindElement(By.ID(“reportDate”)).SendKeys “2023-10-26”
‘ 方法2:更通用的,点击日历上的“昨天”按钮
‘ 需要利用浏览器开发者工具(F12)查看“昨天”按钮的CSS选择器或XPath
driver.FindElement(By.CssSelector(“.calendar-container .yesterday”)).Click
‘ 然后点击查询按钮
driver.FindElement(By.XPath(“//button[contains(text(), ‘查询’)]”)).Click
排查技巧 :如何定位元素?在浏览器页面按F12打开开发者工具,使用“元素选择器”(箭头图标)点击页面上的按钮或输入框,就能在代码区看到其HTML结构。优先使用
ID,因为它是唯一的。其次用CSS Selector,它比XPath通常更简洁、性能更好。XPath功能强大但写起来复杂,在页面结构变化时更容易失效。
步骤四:等待数据加载并提取表格 点击查询后,数据通常是异步加载的。我们需要“显式等待”某个标志性元素出现。
‘ 显式等待:直到表格的第一行数据出现(假设数据行的CSS类为’.data-row’)
driver.Wait (By.CssSelector(“.data-table tbody tr.data-row”)).Timeout = 15000
‘ 找到整个表格
Dim table As WebElement
Set table = driver.FindElement(By.CssSelector(“.data-table”))
‘ 获取所有行
Dim rows As WebElements
Set rows = table.FindElements(By.TagName(“tr”))
Dim data() As Variant
ReDim data(1 To rows.Count, 1 To 10) ‘假设有10列
Dim i As Long, j As Long
Dim cells As WebElements
For i = 1 To rows.Count
Set cells = rows(i).FindElements(By.TagName(“td”))
For j = 1 To cells.Count
If j <= 10 Then ‘防止列数超出预设
data(i, j) = cells(j).Text
End If
Next j
Next i
这里我们用
driver.Wait
进行显式等待,它比固定的
Application.Wait
更智能,会在条件满足时立即继续,不会浪费不必要的等待时间。
步骤五:将数据写入Excel并格式化
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets(“销售日报”)
ws.Cells.ClearContents ‘清空旧数据
‘ 将数组数据一次性写入Excel的A1区域,这比逐个单元格写入快得多
ws.Range(“A1”).Resize(UBound(data, 1), UBound(data, 2)).Value = data
‘ 应用格式:设置标题行背景色、字体加粗、添加边框
With ws.Range(“A1:J1”)
.Interior.Color = RGB(200, 220, 240)
.Font.Bold = True
.Borders.LineStyle = xlContinuous
End With
With ws.UsedRange
.Borders.LineStyle = xlContinuous
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
End With
性能技巧 :将网页数据先存储到VBA的二维数组
data()中,然后使用Range.Value = data一次性写入工作表,这是最快的操作方式。避免在循环中使用Cells(i, j).Value = ...,那会非常慢。
4. 高级技巧与稳定性优化
当你的自动化脚本从demo走向生产环境,每天定时执行时,稳定性就成了生命线。下面分享几个让脚本更健壮的经验。
4.1 异常处理与日志记录
脚本不能一遇到错误就崩溃。我们必须用
On Error GoTo
语句捕获异常。
Sub AutomatedReport()
On Error GoTo ErrorHandler
Dim driver As New WebDriver
Dim logMsg As String
logMsg = “开始执行: ” & Now
‘ … 这里是主要的自动化流程代码 …
logMsg = logMsg & vbCrLf & “执行成功: ” & Now
GoTo CleanUp
ErrorHandler:
logMsg = logMsg & vbCrLf & “错误发生在: ” & Now & vbCrLf & _
“错误号: ” & Err.Number & “, 描述: ” & Err.Description
‘ 可以尝试截图保存现场,便于排查
driver.CaptureScreenshot “C:\Logs\error_” & Format(Now, “yyyymmdd_hhmmss”) & “.png”
CleanUp:
‘ 无论成功失败,都尝试关闭浏览器
On Error Resume Next ‘防止quit本身出错导致程序中断
driver.Quit
Set driver = Nothing
On Error GoTo 0
‘ 将日志写入文本文件或Excel的某个隐藏工作表
WriteLogToFile “C:\Logs\automation.log”, logMsg
End Sub
4.2 处理动态元素与iframe
现代网页大量使用动态ID和iframe(内嵌框架),这对自动化是挑战。
-
动态ID :如果元素的ID是类似
“button-12345-random”这样每次刷新都变化的,就不能用By.ID。改用其他相对稳定的属性,如data-testid,name, 或者用XPath根据其文本内容或层级关系定位。‘ 使用包含特定文本的XPath driver.FindElement(By.XPath(“//button[contains(text(), ‘提交订单’)]”)).Click ‘ 使用CSS选择器结合属性 driver.FindElement(By.CssSelector(“button[data-role=’submit’]”)).Click -
iframe :如果目标元素在一个
<iframe>里面,你必须先切换到该iframe上下文,才能找到里面的元素。‘ 通过ID或索引切换到iframe driver.SwitchToFrame “iframe_id” ‘ 通过ID ‘ driver.SwitchToFrame 0 ‘ 通过索引(第一个iframe) ‘ 现在可以操作iframe内的元素了 driver.FindElement(By.ID(“innerElement”)).Click ‘ 操作完成后,切回主页面 driver.SwitchToDefaultContent
4.3 实现无人值守与定时执行
我们希望脚本能在下班后自动运行。有两种主流方法:
-
Windows任务计划程序 :
-
将你的Excel文件另存为一个启用了宏的
.xlsm文件。 -
编写一个VBA宏(比如叫
Main),它包含所有自动化逻辑。 -
创建一个批处理文件
.bat,内容如下:@echo off “C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE” “D:\你的文件.xlsm” /e /x/e和/x参数可以让Excel启动后自动运行一个名为Auto_Open的宏。所以你需要把你的主逻辑放在一个叫Auto_Open的Sub里,或者让Auto_Open去调用你的Main宏。 - 在Windows中搜索“任务计划程序”,创建一个新任务,设置触发器(如每天下午6点),操作为启动这个批处理文件。
-
将你的Excel文件另存为一个启用了宏的
-
在Excel VBA中使用
OnTime方法 :‘ 在ThisWorkbook模块中 Private Sub Workbook_Open() ‘ 设置在当前时间的一分钟后执行RunMyTask过程 Application.OnTime EarliestTime:=Now + TimeValue(“00:01:00”), _ Procedure:=“Module1.RunMyTask”, _ Schedule:=True End Sub ‘ 在标准模块Module1中 Public Sub RunMyTask() ‘ 调用你的主自动化流程 Call AutomatedReport ‘ 设置下一次执行,例如明天同一时间 Application.OnTime EarliestTime:=Date + 1 + TimeValue(“18:00:00”), _ Procedure:=“Module1.RunMyTask”, _ Schedule:=True End Sub这种方法需要Excel一直处于打开状态,适合在专用虚拟机或从不关机的办公电脑上运行。
5. 常见问题排查与性能调优实录
即使代码写得再小心,在实际运行中还是会遇到各种问题。下面是我踩过的一些坑和解决方案。
5.1 浏览器驱动版本不匹配
这是最高频的错误。症状是:运行
driver.Start “chrome”, “”
时,弹出一个错误框,或者代码报错“无法创建WebDriver”。
-
解决方案
:严格按照你Chrome浏览器的版本号,去下载相同主版本号的
chromedriver.exe。更新Chrome后,务必记得重新下载驱动。
5.2 元素找不到(NoSuchElementError)
代码报错,说找不到某个ID或选择器对应的元素。
-
排查清单
:
-
等待不足
:元素还没加载出来代码就去找了。增加隐式等待
driver.Timeouts.ImplicitWait,或在关键操作前使用显式等待driver.Wait。 - 定位器写错 :用开发者工具仔细核对元素的ID、Class或XPath是否正确。注意大小写,注意是否有空格。
- 元素在iframe或shadow DOM内 :如果是,需要先切换上下文。
-
页面有多个匹配项
:
FindElement只返回第一个。如果你需要操作第二个,使用FindElements获取集合,然后按索引操作。 - 页面结构已变更 :网站改版了。这是自动化脚本的“天敌”,需要定期维护更新定位器。
-
等待不足
:元素还没加载出来代码就去找了。增加隐式等待
5.3 脚本执行速度慢
感觉浏览器操作一卡一卡的,整体运行时间很长。
-
性能调优点
:
-
减少不必要的等待
:用智能的
driver.Wait替代固定的Application.Wait。 -
关闭浏览器不必要的功能
:启动浏览器时可以添加参数,提升速度。
driver.Start “chrome”, “–disable-gpu –no-sandbox –disable-dev-shm-usage –disable-extensions”–disable-gpu在无头模式下有用;–no-sandbox在某些Linux环境下需要;–disable-dev-shm-usage解决共享内存问题;–disable-extensions禁用扩展。 -
使用无头模式
:如果不需要看到浏览器界面,使用无头模式可以极大节省资源。
driver.Start “chrome”, “–headless” - 批量操作 :如之前所述,数据写入Excel使用数组一次性操作。
-
减少不必要的等待
:用智能的
5.4 浏览器被检测为自动化工具
一些高级网站(如某些登录门户、反爬严格的站点)会检测浏览器是否被Selenium等工具控制。症状是登录失败,或者跳转到验证页面。
-
应对策略
:
-
使用
chromeOptions排除检测特征 :这是比较有效的方法。SeleniumBasic可以通过AddArgument添加参数。Dim options As New ChromeOptions options.AddArgument “–disable-blink-features=AutomationControlled” options.AddExcludedSwitch “enable-automation” options.AddAdditionalCapability “useAutomationExtension”, False Dim driver As New WebDriver driver.Start “chrome”, “”, options -
更彻底的方法
:使用第三方工具如
undetected-chromedriver,但它与VBA集成较复杂,可能需要通过命令行间接调用。
-
使用
5.5 内存泄漏与浏览器未关闭
脚本跑久了,或者异常退出时,可能会留下浏览器进程和驱动进程,占用内存。
-
健壮性设计
:
可以在脚本开始和结束时,记录系统进程列表,如果发现残留的Sub SafeAutomation() Dim driver As WebDriver On Error GoTo CleanUp Set driver = New WebDriver ‘ … 你的代码 … CleanUp: If Not driver Is Nothing Then On Error Resume Next driver.Quit ‘ 尝试正常退出 ‘ 如果正常退出失败,强制结束进程(Windows API) Dim pid As Long pid = driver.ProcessId ‘ SeleniumBasic 2.0+ 支持 If pid > 0 Then Shell “taskkill /f /pid ” & pid, vbHide End If Set driver = Nothing End If End Subchromedriver.exe或chrome.exe进程,就用taskkill命令强制结束。
将SeleniumBasic引入你的VBA工具箱,绝不是简单地多学一个库。它本质上是扩展了VBA的能力边界,让你能从“办公室”走向“整个互联网”。最初,你可能会花不少时间在调试元素定位器和处理网页异步加载上,这很正常。我的建议是,从一个最小、最确定的任务开始,比如只是打开网页、登录、然后截图。成功了,再一点点增加复杂度。当你成功地将第一个网页数据流自动灌入Excel,并看着它自动生成图表时,那种成就感会让你觉得所有的调试都是值得的。这个技术栈最妙的地方在于,它把强大的Web自动化能力,封装成了你早已熟悉的VBA语法,学习曲线相对平缓,但带来的效率提升是指数级的。

6825

被折叠的 条评论
为什么被折叠?



