
最近帮一位当英语老师的朋友整理考纲词汇表600多个单词要配上音标和中文翻译。我第一反应是手工查词典查了三十来个就有点怀疑人生第二反应是写Python脚本但对方电脑上根本没装Python最后还得导回Excel。于是我把目光放回Excel本身——用VBA直接调用在线API把单词丢给音标接口和翻译接口返回结果自动填进单元格。这篇就把完整的实现思路、踩坑记录和完整代码发出来给同样需要批量生成音标和翻译的朋友参考。整个方案只需要一台装了Excel或WPS的电脑不需要额外插件把代码复制到VBA编辑器里跑一遍就能拿到一个长期可用的批处理工具。1. 这一篇解决谁的痛点词汇表批处理与手工对照的差距1.1 我最初的需求场景事情是这样的朋友发来一张Excel表里面是下学期的考纲词汇A列是英文单词B列和C列空着需要填音标和中文解释。词汇量不大也就六百多个但手动查词典的经历谁试谁知道——查一个词平均几十秒遇到派生词还要想半天查两百个基本就头昏眼花统计下来一晚上全搭进去。其实这种“批量给英文单词配音标和翻译”的需求很常见不只是老师备考学生在整理自己的生词本时希望把做真题时摘出来的生词自动补全音标和释义。做海外市场运营的同学经常要整理产品词表、竞品关键词表需要中英对照。编辑和译者处理双语稿件时偶尔需要批量核对术语的翻译和发音标注。这些场景的共同点是数据来源就是一张普通的Excel表最终交付物也必须是Excel表。中间过程越少人工干预越好。1.2 为什么不用Python而用VBA我平时写脚本优先Python但这个需求放到VBA里有很现实的理由。最关键的一点是交付门槛。我用Python写好脚本同事拿到手要装解释器、装requests库、处理编码问题这对非技术背景的人来说几乎是天堑。就算我用PyInstaller打成exe杀毒软件可能误报公司电脑可能没有权限运行。而VBA是Excel自带的文件保存成.xlsm对方打开Excel就能用按钮一按就出结果零成本。其次是数据处理闭环。单词已经在Excel里了VBA直接读单元格、写单元格不需要导入导出。有些人会说Power Query或者Office Script也能做但Office Script只在网页版Excel里跑本地Excel用户接触不到Power Query处理网络请求和JSON的能力又很有限。VBA在这个场景下反而是最顺手的。当然VBA也有痛点JSON解析没有原生支持网络请求的编码处理容易踩坑错误信息也不够直观。所以本文的方案不是把VBA吹成万能工具而是用一套“轻量级、够用”的方式把这些痛点绕过去。1.3 最终效果预览与A/B/C三列布局工具最终做出来是这样用的在Sheet1的A列从A2开始填入英文单词每个单元格一个单词。点击宏按钮或直接运行AddPhoneticAndTranslation过程。B列自动填充音标C列自动填充中文翻译。布局就是这样A列B列C列hello/həˈloʊ/你好喂world/wɜːld/世界algorithm/ˈælɡərɪðəm/算法处理过程中Excel左下角状态栏会显示“进度12/600 当前algorithm”方便观察运行情况。全部跑完后弹一个消息框告诉你一共处理了多少行、失败了多少行。失败的行会留空方便事后单独检查不会中断整个流程。2. 免费API选型哪些能用、哪些会坑你2.1 音标接口DictionaryAPI.dev怎么返回数据音标数据我用的是一个开源词典接口DictionaryAPI.dev完整的请求地址是https://api.dictionaryapi.dev/api/v2/entries/en/{word}把{word}替换成要查的单词就行比如https://api.dictionaryapi.dev/api/v2/entries/en/hello这个接口不需要注册不需要API Key直接GET请求就能拿数据。返回的是JSON数组一个词条对应一个对象典型的返回结构长这样[ { word: hello, phonetic: /həˈloʊ/, phonetics: [ { text: /həˈloʊ/, audio: https://api.dictionaryapi.dev/media/pronunciations/en/hello-uk.mp3 } ], meanings: [] } ]需要注意一个细节phonetic字段不保证一定存在。有些词根部的phonetic是空的但phonetics数组里的元素有text字段。所以解析的时候要做两层兼容——先取根部的phonetic取不到就去phonetics数组里找第一个以斜杠包裹的text值。还有一点这个接口的服务器在国外国内网络访问有时会慢。如果响应太慢或超时影响的是整体处理速度后面会讲怎么加重试和限速。2.2 翻译接口MyMemory与备用方案翻译接口我用的是MyMemory一个免费的机器翻译服务。请求地址格式https://api.mymemory.translated.net/get?qhellolangpairen|zh-CNq是待翻译文本langpair是语言对英语到简体中文就是en|zh-CN。返回的JSON也很简洁{ responseData: { translatedText: 你好, match: 1.0 }, responseStatus: 200 }我们只需要从responseData.translatedText里取值。这个接口的优点是不需要Key、不需要签名缺点是匿名请求有配额限制单IP每天大约5000字符级别日常处理几百个单词完全够用。翻译质量方面单词和常用短语的翻译都比较准确专业术语偶尔会翻得生硬但对词汇表来说已经可以接受。如果你的词汇表有特殊要求比如必须要用百度翻译的质量或者要对接公司内部的翻译平台可以替换成百度翻译开放平台的通用文本翻译API。那个接口需要注册后拿到APP ID和密钥请求时要计算MD5签名sign MD5(appid q salt secretKey)VBA里计算MD5一般通过CreateObject(System.Security.Cryptography.MD5CryptoServiceProvider)调.NET的库实现逻辑不复杂但绕。我的建议是先用MyMemory跑通整套流程如果你确实需要换成百度翻译只需要改GetTranslation函数里的URL拼装方式和解析规则主流程完全不用动。2.3 为什么没用“现成的VBA插件”网上其实有现成的Excel翻译插件、词典插件装完点按钮也能出结果。我之所以没选第一是很多插件是收费的或者免费版有次数限制第二是插件的数据源是写死的你换不了接口、改不了解析规则第三是公司电脑的Excel加载项经常被安全策略禁用你还要去改注册表、改信任中心设置折腾半天还不如自己写几十行代码。自己写的好处是接口可控、逻辑透明。哪天MyMemory配额不够了换接口只动一个函数哪天同事说音标格式想带重音符号而不是IPA改一行正则就行。工具是自己的才能随需求调整。3. VBA发起HTTP请求与编码处理最容易被乱码打败的地方3.1 XMLHTTP与WinHttpRequest的选择VBA里发起HTTP请求最常见的两种COM对象一个是MSXML2.XMLHTTP一个是WinHttp.WinHttpRequest.5.1。MSXML2.XMLHTTPExcel和WPS里基本都能用创建简单同步请求写起来方便。WinHttp.WinHttpRequest.5.1支持SetTimeouts方法可以显式设置连接超时、发送超时、接收超时。本文主方案用MSXML2.XMLHTTP因为兼容性最好。如果你在批量处理时经常碰到请求卡死的情况再把对象换成WinHttp.WinHttpRequest.5.1调用SetTimeouts(5000, 5000, 5000, 5000)设置四类超时其余代码逻辑不用改。3.2 用ADODB.Stream强制按UTF-8解码响应这是整个方案里最容易翻车的环节我一开始直接取responseText结果所有中文翻译全部变成乱码类似“浣犲ソ”这样的东西。原因在于MSXML2.XMLHTTP的responseText属性在把响应字节转换成字符串时默认按系统当前代码页去解码。中文Windows系统默认的是GBK而接口返回的是UTF-8编码的文本两边对不上自然乱码。解决办法是绕开responseText直接取二进制响应体responseBody然后用ADODB.Stream强制按UTF-8解码Function HttpGet(ByVal url As String) As String On Error GoTo errHandle Dim http As Object Set http CreateObject(MSXML2.XMLHTTP) http.Open GET, url, False http.setRequestHeader User-Agent, Mozilla/5.0 http.Send If http.Status 200 Then Dim stream As Object Set stream CreateObject(ADODB.Stream) stream.Type 1 adTypeBinary stream.Open stream.Write http.responseBody stream.Position 0 stream.Type 2 adTypeText stream.Charset UTF-8 HttpGet stream.ReadText stream.Close Else HttpGet End If Exit Function errHandle: HttpGet End Function注意最后On Error GoTo errHandle的处理网络请求失败时函数直接返回空字符串由调用方决定怎么重试这样就不会因为单个单词的超时而中断整个批处理。3.3 请求URL编码英文单词也要留个心眼英文单词平时不需要编码字母和数字在URL里都是安全的。但有两种情况必须处理一是短语翻译比如“give up”中间的空格必须编码成%20二是万一你的词汇表里有带撇号、连字符、特殊符号的单词直接拼进URL会导致请求失败。VBA没有现成的encodeURIComponent函数自己写一个通用的UTF-8百分号编码函数即可。写法是利用ADODB.Stream把字符串转成UTF-8字节再逐个字节转成%XX格式Function Utf8PercentEncode(ByVal s As String) As String Dim stream As Object Dim bytes() As Byte Dim i As Long Set stream CreateObject(ADODB.Stream) stream.Type 2 stream.Charset UTF-8 stream.Open stream.WriteText s stream.Position 0 stream.Type 1 bytes stream.Read stream.Close For i 0 To UBound(bytes) Utf8PercentEncode Utf8PercentEncode % Right(0 Hex(bytes(i)), 2) Next i End Function这个函数对纯英文单词也安全直接套用就行。我在主流程里统一对查询词做一次编码省得后续要支持中文词条时再回来改。4. 音标与翻译的提取正则还是JSON解析器4.1 先给结论两个接口都用正则够用这一步很多人会纠结VBA解析JSON是不是必须引入第三方模块其实要看接口的返回结构。我用的两个接口返回JSON都非常规整字段也很简单用正则提取完全够用不需要往工程里塞模块也不需要考虑64位Office下第三方库的兼容性问题。原则是简单的接口用正则复杂的再上JSON解析器。下面先给出不用任何模块的版本最后再补充说明引入JSONConverter的进阶玩法。4.2 正则提取音标DictionaryAPI.dev的返回里音标通常出现在phonetic字段或者phonetics数组里元素的text字段。我写一个GetPhonetic函数先匹配根部的phonetic找不到就匹配以斜杠开头的text值Function GetPhonetic(ByVal word As String) As String Dim resp As String resp HttpGet(https://api.dictionaryapi.dev/api/v2/entries/en/ Utf8PercentEncode(word)) If resp Then Exit Function Dim reg As Object Set reg CreateObject(VBScript.RegExp) reg.Global True 先找根部 phonetic 字段 reg.Pattern phonetic:([^]*) If reg.Execute(resp).Count 0 Then GetPhonetic reg.Execute(resp)(0).SubMatches(0) Exit Function End If 再找 phonetics 数组里以斜杠包裹的 text 值 reg.Pattern text:(/[^]*/) If reg.Execute(resp).Count 0 Then GetPhonetic reg.Execute(resp)(0).SubMatches(0) End If End Function这里要提醒一下VBA字符串里的双引号写法VBA字符串内部的双引号要写成两个连续的双引号所以正则里的phonetic:在代码里表现为phonetic:直接复制代码即可别自己手敲容易错。4.3 正则提取翻译 Unicode解码MyMemory的返回里translatedText的值是我们要的翻译。但有个坑JSON里的中文通常不是直接显示成“你好”而是以Unicode转义序列\u4f60\u597d的形式存在。VBA的字符串解析不会自动解码这种转义所以要写一个DecodeUnicode函数手动还原。先看翻译提取函数Function GetTranslation(ByVal word As String) As String Dim url As String url https://api.mymemory.translated.net/get?q Utf8PercentEncode(word) langpairen|zh-CN Dim resp As String resp HttpGet(url) If resp Then Exit Function Dim reg As Object Set reg CreateObject(VBScript.RegExp) reg.Pattern translatedText:(.*?),match If reg.Execute(resp).Count 0 Then GetTranslation DecodeUnicode(reg.Execute(resp)(0).SubMatches(0)) End If End Function注意正则里我加了后面的,match作为锚点这样匹配到的是真正想要的translatedText字段不会错误匹配到其他含有相似结构的字段。再看Unicode解码函数Function DecodeUnicode(ByVal s As String) As String Dim reg As Object Dim mt As Object Dim chrCode As Long Set reg CreateObject(VBScript.RegExp) reg.Global True reg.Pattern \\u([0-9a-fA-F]{4}) 循环把第一个 \uXXXX 替换成对应字符 Do While reg.Test(s) Set mt reg.Execute(s) chrCode CLng(H mt(0).SubMatches(0)) s Replace(s, mt(0).Value, ChrW(chrCode), 1, 1) Loop DecodeUnicode s End Function这里的原理是H4f60这样的写法在VBA里代表一个十六进制数ChrW函数把Unicode码位转换成对应的字符。如果接口返回的翻译本来就不是转义形式正则匹配不到\uXXXX函数会原样返回不会破坏数据。4.4 进阶引入JSONConverter处理复杂接口如果你的需求升级了例如想接有道词典接口同时取词性和例句或者想接百度翻译解析多层嵌套的JSON正则方案就不够看了。这时候建议在VBA工程里引入VBA-JSON这个开源库也就是社区里常说的JSONConverter模块。用法三步在GitHub上搜索VBA-JSON下载JsonConverter.bas文件。在VBA编辑器里菜单栏“文件-导入文件”选这个模块。在“工具-引用”里勾选Microsoft Scripting Runtime。之后就可以这样解析DictionaryAPI.dev的返回Dim json As Object Set json JsonConverter.ParseJson(resp) 第一个词条对象的 phonetic 字段 GetPhonetic json(1)(phonetic)JsonConverter会把JSON解析成VBA的Collection、Dictionary嵌套结构访问起来直观很多也不怕字段顺序变化。缺点是模块本身的依赖稍微多了一点首次配置时间会比纯正则长。5. 批处理主流程缓存去重、进度提示、单元格写入5.1 用Scripting.Dictionary做词典缓存批量处理时最容易被忽略的就是重复请求。一张600行的词汇表里完全可能有重复的单词比如“run”出现三次“ability”出现两次。每次都去请求接口既浪费配额又拖慢速度。用Scripting.Dictionary做一个缓存键是单词值是“音标|翻译”的拼接字符串。处理每一行时先查缓存命中就直接用没命中才发请求并把结果存进缓存Dim cache As Object Set cache CreateObject(Scripting.Dictionary) 查缓存 If cache.Exists(word) Then cacheKey cache(word) Else 请求接口结果存入缓存 phon GetPhonetic(word) Sleep 300 trans GetTranslation(word) Sleep 300 cache.Add word, phon | trans cacheKey cache(word) End If这个缓存还有一个额外好处处理到一半网络断了你修正后重新运行脚本已经查过的词不会重复请求直接从缓存取效率提升非常明显。5.2 主循环与Sleep限速Sleep的作用是控制请求频率。免费接口对并发和频率都有限制你一秒发十个请求很容易被服务端拒绝返回429或503。实测下来两次请求之间至少间隔300毫秒比较安全。VBA里声明Sleep函数要分32位和64位Office用条件编译处理#If VBA7 Then Private Declare PtrSafe Sub Sleep Lib kernel32 (ByVal ms As LongPtr) #Else Private Declare Sub Sleep Lib kernel32 (ByVal ms As Long) #End If完整的主循环过程Sub AddPhoneticAndTranslation() Dim ws As Worksheet Set ws ThisWorkbook.Worksheets(Sheet1) Dim lastRow As Long lastRow ws.Cells(ws.Rows.Count, 1).End(xlUp).Row If lastRow 2 Then MsgBox A列还没有单词请从A2开始填写。 Exit Sub End If Dim cache As Object Set cache CreateObject(Scripting.Dictionary) Dim i As Long Dim word As String Dim phon As String Dim trans As String Dim cacheKey As String Dim failed As Long For i 2 To lastRow word Trim(CStr(ws.Cells(i, 1).Value)) If Len(word) 0 Then GoTo nextRow If cache.Exists(word) Then cacheKey cache(word) Else phon GetPhonetic(word) Sleep 300 trans GetTranslation(word) Sleep 300 If phon And trans Then failed failed 1 cache.Add word, phon | trans cacheKey cache(word) End If 写入B列音标、C列翻译 ws.Cells(i, 2).Value Split(cacheKey, |)(0) ws.Cells(i, 3).Value Split(cacheKey, |)(1) Application.StatusBar 进度 i / lastRow 当前 word nextRow: DoEvents Next i Application.StatusBar False MsgBox 处理完成共 lastRow - 1 行失败 failed 行。 End Sub速度方面因为每个单词至少有两次网络请求加上Sleep间隔大约一两秒处理一个词。600个单词大概十几分钟跑完可以接受。如果你想更快可以把两次Sleep去掉或改短但被限流的风险会增加自己权衡。5.3 状态栏进度与DoEvents的意义Application.StatusBar会在Excel左下角动态显示当前进度比弹窗提示更不打扰人。循环结束后记得把它设成False否则状态栏会一直显示你设置的文本。DoEvents的作用是让出CPU控制权让Excel在循环期间能处理系统消息。不加的话按钮点击、窗体绘制都会显得卡死别人以为你的Excel崩了。实测在几百行的循环里加了DoEvents体验会好很多。6. 实测避坑四类常见问题的排查链路6.1 中文翻译乱码/变成问号如果翻译列出现乱码排查顺序是先去掉HttpGet里的ADODB.Stream解码直接用responseText测试看是否出现乱码。出现说明就是编码问题。确认HttpGet函数里stream.Charset UTF-8没有写错Charset属性对大小写不敏感。在Excel里检查单元格字体换成宋体、微软雅黑等中文字体避免用了不支持中文的字体导致显示成问号。我自己遇到乱码的原因99%都是直接在VBA里取了responseText而不是用了responseBody加Stream的方案。这个坑只要掉过一次之后就条件反射了。6.2 接口返回429/503导致脚本中断批量处理时最怕的不是慢而是请求被限流后整个脚本报错中断。429是请求太频繁503是服务端临时过载。这两个状态码下HttpGet返回空字符串主流程会跳过当前单词最后统计为“失败”。网络错误有时候是临时的重试一次就能成功。所以建议把HttpGet包一层带重试逻辑的函数Function HttpGetRetry(ByVal url As String, Optional ByVal retries As Long 3) As String Dim i As Long For i 1 To retries HttpGetRetry HttpGet(url) If HttpGetRetry Then Exit Function Sleep 1000 Next i End Function把主流程里的GetPhonetic和GetTranslation内部调用的HttpGet替换成HttpGetRetry单个单词的网络抖动基本都能扛过去。如果重试三次还是失败那就是接口服务真的出问题了或者你的IP被临时封了这时候停下来歇一会再跑。6.3 宏明明写了却不执行/被禁用代码写好了按下按钮没反应最常见的原因有三个文件没保存成宏文件格式检查一下扩展名是不是.xlsm。Excel信任中心禁用了宏路径在“文件-选项-信任中心-信任中心设置-宏设置”改成“启用所有宏”或对你自己的文件做数字签名。你运行的是受保护的视图文件从网上下载的Excel表经常有“受保护的视图”需要点“启用编辑”和“启用内容”。顺带说一句如果自己写的宏在别人的电脑上运行不了多半就是第二条。给文件做VBA数字签名或者把信任中心设置截图发给对方都可以解决。6.4 WPS与64位Office的兼容性差异WPS Office里运行VBA宏前提是安装了VBA组件。很多人的WPS默认不带VBA引擎“开发工具”选项卡里根本找不到“Visual Basic”按钮需要单独下载安装WPS VBA组件。装好之后MSXML2.XMLHTTP和ADODB.Stream这两个COM对象在WPS里都能正常使用。64位Office和32位Office的差异主要在API声明上。我在5.2里已经用#If VBA7 Then ... #Else ... #End If做了条件编译32位和64位都能跑。如果你抄别人的代码遇到了Declare语句报错优先检查是不是PtrSafe关键字没写。还有一个小坑WPS的自定义功能区按钮需要另做配置在WPS里运行宏最稳妥的方式是AltF8打开宏列表直接运行或者在工作表里插入一个“窗体控件”按钮并指定宏。7. 扩展方向从单词表到生词本、双向翻译与词典维护7.1 中译英与短语翻译的写法调整这个方案稍微改一改就能做反向翻译。需要中文翻译成英文时把MyMemory请求里的langpairen|zh-CN改成zh-CN|en翻译结果就会变成英文。至于音标英文单词才有音标所以中译英时直接跳过B列或者把B列留空只在C列写翻译结果。短语翻译也简单直接把“give up”这样的文本作为查询词传进去Utf8PercentEncode会自动把空格编码成%20接口能够正常识别。实测这类动词短语的翻译质量还不错而且MyMemory会返回多个候选结果我们可以只取第一个。7.2 把翻译结果回写为“自定义词典”供其他表使用跑完第一张词汇表之后你就拥有一张“单词-音标-翻译”三列的对照表。这张表本身就是一份小词典可以在其他工作簿里用VLOOKUP直接引用VLOOKUP(A2, 词典表!$A:$C, 3, FALSE)这样后续处理新词汇表时完全重复的词可以直接从旧表里带过来不需要再走接口。但对新词VLOOKUP返回#N/A就得再跑一次宏。所以我实际使用时的流程是新词汇表先跟旧词典表做匹配匹配不上的单词才交给宏处理跑完后再把新结果追加到词典表里形成持续积累。7.3 合并到个人模板做成一键工具最后分享一个我自己的使用习惯这个方案最终被我做成了一个日常模板A列粘贴单词、点按钮自动输出B列音标和C列翻译。模板里还做了两件事让实际体验更顺滑。一是对B列音标设置了格式字体用Times New Roman加粗斜体因为IPA音标字符搭配衬线字体显示最清晰。二是加了一个简单的前置检查选中的单元格区域如果已经有内容会弹出确认框避免误覆盖已经填好的数据。实际用下来这种自动化工具最需要注意的反而不是代码本身而是“先小批量跑通再上全量”。第一次跑的时候先选10个单词验证音标格式、翻译质量、单元格写入位置都正常再放开跑全表。磨刀不误砍柴工这个习惯帮我避开了好几次“跑完600行发现B列音标全挤在一个单元格里”的惨剧。