如何用 VBA 宏将 Excel 联系人推送到 Brevo(以及 Office Scripts 替代方案)
一个可工作的 VBA 宏,一键将 Excel 表格中的联系人发布到 Brevo 的 API。同时介绍何时改用 Office Scripts + Power Automate,以及与 Google Apps Script 方案的取舍。
你在 Excel 中有联系人,希望把它们放进 Brevo。最快粗糙的答案是把文件另存为 .csv 然后导入,做一次没问题。对于任何重复进行的事情(每周销售交接、团队每天更新的工作簿、定期刷新的合作伙伴名单),你想要的是 Excel 内部就有一个按钮来执行同步。
本指南介绍真正有意义的两条路径:
- 嵌入工作簿的 VBA 宏:无许可证、无云、可离线工作,用户点击按钮的瞬间就运行。约 80% 的”Excel 到 Brevo”场景的正确答案。
- Office Scripts + Power Automate:用 TypeScript 而不是 VBA,运行在云端,支持定时触发器。如果工作簿在 OneDrive/SharePoint 中,并且你想要无人值守的同步,这是正确答案,但要注意 Power Automate 的许可证。
如果你在找 Google Sheets 等价方案,请见 Apps Script 配套文章。如果你只想从笔记本上的脚本一次性导入 CSV,CSV 导入指南 有 Python、Node.js 和 cURL 版本。
这个宏做什么
当用户点击表格上的”Sync to Brevo”按钮时:
- 读取活动工作表的每一行(先读表头行,每行一个联系人)。
- 构建一个符合 Brevo
jsonBody参数形状的 JSON 数组。 - 用工作簿存储的 API 密钥将其 POST 到
https://api.brevo.com/v3/contacts/import。 - 用消息框显示结果。
就这样,约 120 行 VBA。下面是完整可工作的模块。
宏期望的表格布局
| firstName | lastName | company | city | |
|---|---|---|---|---|
| [email protected] | Jane | Doe | Acme | Berlin |
| [email protected] | John | Smith | Globex | Paris |
email 是必需的。其他每一列都成为 Brevo 联系人属性,按列表头(转为大写)映射到属性名。所以 firstName → FIRSTNAME,company → COMPANY。自定义属性(标准集之外的任何属性)需要先在你的 Brevo 账户中存在。在 Contacts → Settings → Contact attributes 中定义它们。
第 1 步:打开 VBA 编辑器
在 Excel 中:按 Alt + F11。VBA 编辑器打开。在左侧的 Project 窗格中,右键点击你的工作簿并选择 Insert → Module。一个空的 Module1 出现。
第 2 步:粘贴完整宏
将 Module1 的内容替换为以下内容:
' ===========================================================================' Brevo contact sync for Excel' Reads the active sheet's rows and POSTs them to Brevo's import API.' ===========================================================================Option Explicit
Private Const BREVO_API_BASE As String = "https://api.brevo.com/v3"Private Const BREVO_LIST_ID As Long = 42 ' <- your Brevo list IDPrivate Const BATCH_SIZE As Long = 1000
' --- Public entry points (the ones you assign to ribbon buttons) -----------
Public Sub SyncSheetToBrevo() Dim apiKey As String apiKey = GetApiKey() If apiKey = "" Then MsgBox "No API key configured. Run ConfigureApiKey first.", _ vbExclamation, "Brevo Sync" Exit Sub End If
Dim ws As Worksheet Set ws = ActiveSheet
Dim emailCol As Long emailCol = FindEmailColumn(ws) If emailCol = 0 Then MsgBox "Sheet must have an 'email' column in row 1.", _ vbExclamation, "Brevo Sync" Exit Sub End If
Dim lastRow As Long, lastCol As Long lastRow = ws.Cells(ws.Rows.Count, emailCol).End(xlUp).Row lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column If lastRow < 2 Then MsgBox "No contact rows found.", vbInformation, "Brevo Sync" Exit Sub End If
Dim contacts As Collection Set contacts = New Collection
Dim r As Long, c As Long For r = 2 To lastRow Dim email As String email = LCase(Trim(CStr(ws.Cells(r, emailCol).Value))) If email <> "" And InStr(email, "@") > 0 Then Dim json As String json = "{""email"":""" & EscapeJson(email) & """,""attributes"":{"
Dim attrFirst As Boolean attrFirst = True For c = 1 To lastCol If c <> emailCol Then Dim val As String val = CStr(ws.Cells(r, c).Value) If val <> "" Then If Not attrFirst Then json = json & "," Dim attrName As String attrName = UCase(Trim(CStr(ws.Cells(1, c).Value))) json = json & """" & attrName & """:""" & EscapeJson(val) & """" attrFirst = False End If End If Next c
json = json & "}}" contacts.Add json End If Next r
If contacts.Count = 0 Then MsgBox "No valid contact rows found.", vbInformation, "Brevo Sync" Exit Sub End If
Dim totalSent As Long Dim batchNum As Long Dim okCount As Long, failCount As Long Dim batchStart As Long
For batchStart = 1 To contacts.Count Step BATCH_SIZE batchNum = batchNum + 1 Dim batchEnd As Long batchEnd = batchStart + BATCH_SIZE - 1 If batchEnd > contacts.Count Then batchEnd = contacts.Count
Dim payload As String payload = "{""jsonBody"":[" Dim i As Long For i = batchStart To batchEnd If i > batchStart Then payload = payload & "," payload = payload & contacts(i) Next i payload = payload & "],""listIds"":[" & BREVO_LIST_ID & _ "],""updateExistingContacts"":true,""emptyContactsAttributes"":false}"
Dim ok As Boolean ok = PostToBrevo(apiKey, payload) If ok Then okCount = okCount + 1 totalSent = totalSent + (batchEnd - batchStart + 1) Else failCount = failCount + 1 End If Next batchStart
MsgBox "Sent " & totalSent & " contact(s) in " & batchNum & " batch(es)." _ & vbCrLf & "Successful batches: " & okCount _ & vbCrLf & "Failed batches: " & failCount, _ vbInformation, "Brevo Sync"End Sub
Public Sub ConfigureApiKey() Dim key As String key = InputBox("Paste your Brevo API key (xkeysib-...):", "Brevo API Key") If key = "" Then Exit Sub key = Trim(key) If Left(key, 8) <> "xkeysib-" Then MsgBox "That doesn't look like a Brevo API key (should start with xkeysib-).", _ vbExclamation, "Brevo API Key" Exit Sub End If
On Error Resume Next ThisWorkbook.CustomDocumentProperties("BrevoApiKey").Delete On Error GoTo 0
ThisWorkbook.CustomDocumentProperties.Add _ Name:="BrevoApiKey", _ LinkToContent:=False, _ Type:=msoPropertyTypeString, _ Value:=key
ThisWorkbook.Save MsgBox "API key saved inside the workbook.", vbInformation, "Brevo API Key"End Sub
' --- Private helpers --------------------------------------------------------
Private Function GetApiKey() As String On Error Resume Next GetApiKey = ThisWorkbook.CustomDocumentProperties("BrevoApiKey").Value On Error GoTo 0End Function
Private Function FindEmailColumn(ws As Worksheet) As Long Dim lastCol As Long, c As Long lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column For c = 1 To lastCol If LCase(Trim(CStr(ws.Cells(1, c).Value))) = "email" Then FindEmailColumn = c Exit Function End If Next c FindEmailColumn = 0End Function
Private Function PostToBrevo(apiKey As String, payload As String) As Boolean Dim http As Object Set http = CreateObject("MSXML2.XMLHTTP") http.Open "POST", BREVO_API_BASE & "/contacts/import", False http.SetRequestHeader "api-key", apiKey http.SetRequestHeader "Content-Type", "application/json" http.SetRequestHeader "Accept", "application/json" http.Send payload PostToBrevo = (http.Status = 202) If Not PostToBrevo Then Debug.Print "Brevo error " & http.Status & ": " & http.responseText End IfEnd Function
Private Function EscapeJson(s As String) As String Dim r As String r = Replace(s, "\", "\\") r = Replace(r, """", "\""") r = Replace(r, vbCrLf, "\n") r = Replace(r, vbLf, "\n") r = Replace(r, vbCr, "\n") r = Replace(r, vbTab, "\t") EscapeJson = rEnd Function第 3 步:将工作簿另存为 .xlsm
VBA 宏只在启用宏的工作簿中持久化。Save As → 选择 Excel Macro-Enabled Workbook (.xlsm)。普通的 .xlsx 格式会静默剥离宏。许多人第一次就这样丢失代码。
第 4 步:配置你的 API 密钥
运行 ConfigureApiKey 一次。可以:
- 在 VBA 编辑器中,点击
ConfigureApiKey子过程内任意位置并按F5,或者 - 在 Excel 中,Developer → Macros,选择
ConfigureApiKey,Run。
粘贴你的 xkeysib-... 密钥。宏将其作为自定义文档属性存储在工作簿内部,它不在源代码中,不在注册表中,并随文件移动(请注意:如果你把 .xlsm 邮件发送给某人,API 密钥也跟着走)。
如果你更愿意把密钥放在工作簿之外,可以把存储改到 Windows 注册表:
' Replace the body of ConfigureApiKey with:SaveSetting "Brevo", "Sync", "ApiKey", key
' And GetApiKey with:GetApiKey = GetSetting("Brevo", "Sync", "ApiKey", "")SaveSetting/GetSetting 写入 HKCU\Software\VB and VBA Program Settings\Brevo\Sync(按用户,而非按工作簿)。如果多个工作簿应共享一个密钥,或你不希望密钥在文件中,使用此方法。
第 5 步:在表格上添加按钮
这把它变成对非技术用户的一键体验。
Insert → Shapes → Rectangle,把一个矩形拖到表格上,标签为”Sync to Brevo”。右键点击形状 → Assign Macro → 选择 SyncSheetToBrevo。完成。
或者,为了更精致的 UI,通过 Office Custom UI Editor 添加自定义功能区选项卡。但对大多数内部工具来说,形状作为按钮已经足够。
第 6 步:运行它
点击按钮。宏读取行、批处理它们、把每个批次发送到 Brevo,并显示一个汇总消息框。Brevo 的导入是异步的,所以成功消息意味着”Brevo 接受了批次”,实际的联系人创建在接下来的几秒内在服务器端完成。完成后你会收到 Brevo 的邮件汇总(除非你设置了 disableNotification: true)。
常见陷阱
按钮没反应也没有错误。 宏被禁用了。看一下表格顶部的黄色安全栏,点击 Enable Content。如果你的组织阻止宏,请参见下面的信任中心 / 代码签名路径。
Compile error: User-defined type not defined。 你在 Mac Excel 上,没有 MSXML2.XMLHTTP。Mac VBA 不能直接发起 HTTPS 请求;改用下面的 Office Scripts 路径。
没有明显原因的 400 Bad Request 来自 Brevo。 几乎总是以下之一:(a)你表格中的某个自定义属性在 Brevo 中尚未存在(先创建它);(b)JSON 转义错误(单元格值中未转义的引号或反斜杠)。代码中的 EscapeJson 函数处理标准情况;如果你的数据有奇怪字符,把 payload 记录到立即窗口(Debug.Print payload)并检查。
401 Unauthorized。 错误的 header。是 api-key(小写、连字符),不是 Authorization。宏使用了正确的,但如果你从别处复制了片段,请再次检查。
Excel 在大导入时冻结。 宏在 UI 线程上同步运行。对于 50,000+ 行,你会看到 Excel 在构建 JSON 并等待 Brevo 时挂起 10 到 30 秒。要么接受它,要么把 MSXML2.XMLHTTP 切换为它的异步变体,但在那种规模下,最好用 Power Automate(下一节)。
当 VBA 不够用时:Office Scripts + Power Automate
VBA 不能做云端定时同步。如果你需要:
- 工作簿每小时同步到 Brevo,无需任何人打开它
- 工作簿在 OneDrive/SharePoint 中,从网页编辑
- 一个禁止桌面宏的 IT 部门
…那你需要 Office Scripts(Microsoft 的 Apps Script 云端等价物)加上 Power Automate(他们的调度和 HTTP 层)。
分工:Office Scripts 读取表格并返回联系人数据。Power Automate 接收该数据,并在触发器上把它 POST 到 Brevo。
Office Script(网页版 Excel → Automate → New Script):
function main(workbook: ExcelScript.Workbook): {email: string, attributes: Record<string, string>}[] { const sheet = workbook.getActiveWorksheet(); const range = sheet.getUsedRange(); if (!range) return [];
const values = range.getValues() as string[][]; if (values.length < 2) return [];
const headers = values[0].map(h => String(h).trim()); const emailIdx = headers.findIndex(h => h.toLowerCase() === "email"); if (emailIdx === -1) throw new Error("Sheet must have an 'email' column");
const contacts: {email: string, attributes: Record<string, string>}[] = []; for (let r = 1; r < values.length; r++) { const row = values[r]; const email = String(row[emailIdx] ?? "").trim().toLowerCase(); if (!email || !email.includes("@")) continue;
const attributes: Record<string, string> = {}; for (let c = 0; c < headers.length; c++) { if (c === emailIdx) continue; const v = row[c]; if (v === null || v === "") continue; attributes[headers[c].toUpperCase()] = String(v); } contacts.push({ email, attributes }); } return contacts;}Power Automate 流:
- Trigger:Recurrence(每 1 小时),或手动按钮,或者如果你想要变更驱动的同步,使用”When a row is modified”。
- Action:Excel Online → Run script:指向你的工作簿和上面的脚本。把它的返回值保存为
contacts。 - Action:HTTP(这是 Premium 连接器,请见下方的许可证说明)。
- Method:
POST - URI:
https://api.brevo.com/v3/contacts/import - Headers:
api-key: xkeysib-...,Content-Type: application/json - Body:
{"jsonBody": @{outputs('Run_script')?['body/result']},"listIds": [42],"updateExistingContacts": true}
- Method:
- Action:Condition → 如果状态码不是 202,发送 Teams/邮件警报。
许可证现实检查:HTTP 操作是 Power Automate Premium 连接器。在 Microsoft 365 Business Basic/Standard 计划中,你获得标准连接器但没有 Premium。最便宜的变通是 Power Automate Premium 附加组件(撰写时约 15 美元/用户/月),或者把 HTTP 移到一个标准流可以调用的小型 Azure Function。如果你已经在包含 Premium 的 E3/E5 上,你已经准备好了。
这就是 Apps Script 故事更干净的主要原因:Apps Script 的 UrlFetchApp 免费且不受限,而 Microsoft 的等价物把网络调用放在付费连接器层之后。
VBA、Office Scripts、Apps Script:何时选哪个
| 需求 | 最佳选择 |
|---|---|
| 在团队每天都打开的工作簿中的一键按钮 | VBA 宏(本指南上半部分) |
| 工作簿在 OneDrive/SharePoint 中,每小时自动同步 | Office Scripts + Power Automate(HTTP 需要 Premium) |
| 仅 Mac Excel,无法使用 VBA | Office Scripts + Power Automate |
| 数据存放于 Google Sheets,而非 Excel | Apps Script(免费,内置定时触发器) |
| 一次性导入,永远不会再需要 | Save As → CSV 并使用 CSV 导入脚本 |
| 从 >10 MB 的文件批量导入 | 带 fileUrl 的 CSV:见 CSV 指南 |
这为什么胜过 Zapier / 无代码平台
对于重复的 Excel 到 Brevo 任务,第三方自动化工具(Zapier、Make、n8n)按任务计费,并在你的数据和 Brevo 之间放一个第三方。VBA 方法没有持续成本,没有第三方数据流,并且生活在文件内部(工作簿移动时,集成跟着移动)。Office Scripts + Power Automate 类似,但 Microsoft 是第三方(如果你在 M365 上,它已经在你的技术栈中)。
Brevo POST /v3/contacts/import 端点的全部意义在于你不需要粘合平台,你的工具已经知道如何发起 HTTP 请求。