
简介百度翻译API调用示例代码包面向需要为应用或网站接入多语言翻译功能的开发者也适合刚接触接口调用的初学者边看边练。包内共2个文件分别提供Python与JavaScript两种实现版本覆盖文本翻译场景中从请求构造、URL编码、参数设置到响应解析的完整流程整体仅5KB结构精简便于直接阅读和复用。已有640人学习下载。代码演示了如何获取并使用API Key与Secret Key、构建HTTP请求、解析JSON返回结果以及处理常见错误可帮助读者快速理解百度翻译接口的实际调用方法并迁移到其他API服务中去。需要提醒的是资源描述注明该代码在2023年8月前有效实际使用时应主动核对百度AI开放平台的最新文档、接口版本与免费额度避免因服务策略调整影响项目正常运行。1. 这个翻译接口的完整代码难点不在请求体而在签名参数很多开发者第一次接入这个翻译接口时会拿着别人写的片段改一改结果打开控制台全是Invalid Signature。翻遍文档也没发现哪里拼接错误最后只能把申请到的一串appid发给同事求助。实际上这个接口的完整代码并不长一个MD5签名、一个随机salt、一段UTF-8编码后的待翻译文本就能跑通最小调用。但真正让代码从“能跑”变成“能上线”还需要处理鉴权参数顺序、语言自动检测、批量翻译、错误码映射和超时重试。这篇文章的目标读者是后端开发者、爬虫工程师或做自动化脚本的人。新手照着第三章的代码就能完成第一行翻译熟手可以直接跳到第四、五章看生产环境里的 queue、缓存和踩坑记录。我不会把官方文档抄一遍而是按“选型 → 签名原理 → 完整代码 → 工程化 → 避坑 → 验证”的顺序把这套接口的完整接入路径讲透。2. 调通这个翻译接口前先把接口选型和签名机制弄清楚2.1 标准版与高级版接口怎么选额度、语种和调用速度这个翻译开放平台对外提供两种接入方式一种是面向个人开发者和小流量的标准版另一种是面向企业级应用的高级版。标准版的申请门槛低基本注册实名后就能拿到appid和密钥免费额度对于日调用量几百次的项目完全够用。高级版则需要提交使用场景、预期流量和公司资质审核周期相对长但单次请求长度上限更高并且支持定制术语表、文档翻译等扩展能力。选型时我建议先看两个硬指标一是单次请求支持的文本长度标准版通常限制在 6KB 字节以内按中文 UTF-8 计算大概是 2000 个汉字二是每秒并发上限标准版多在每秒几到几十次之间高级版按套餐可以到百次以上。如果只是做内部工具或爬虫辅助脚本标准版足够如果是给线上用户提供翻译功能一定要提前评估峰值并发否则很容易出现5400之类的限流错误码。另一个容易忽略的点是语种范围。标准版覆盖常见语种比如中英日韩法俄西葡等但一些小语种如斯瓦希里语、库尔德语在标准版里可能返回“不支持的语种”。我一般会在代码里维护一个本地支持语种列表凡是列表之外的自动降级为auto或直接提示用户。这样既避免了运行时错误也让接口调用行为可控。2.2 签名算法MD5 拼接顺序与编码细节决定成败这个接口的签名规则是把请求参数中的appid、原始文本q、随机数salt和开发者密钥按顺序拼接成一个字符串然后对这个字符串计算 MD5得到的 32 位小写十六进制值就是sign。拼接顺序固定为appid q salt secret不是字母序也不需要 URL 编码。但如果q里有中文必须先把文本按 UTF-8 编码成字节串再计算 MD5否则同一个词在不同平台上会得到不同签名。很多初学者在这里栽跟头他们在签名前先对q做了urlencode然后拼接。比如把“你好”变成了%E4%BD%A0%E5%A5%BD但服务端在验签时用的是解码后的原始文本两边的签名就永远对不上。正确的做法是签名阶段用原始字符串发送 HTTP 请求时让requests或fetch自动替你做 URL 编码。salt参数用来标记每次请求官方将其描述为随机数。我一般用当前时间的毫秒时间戳这样即使同一秒内发送相同文本两个请求也有不同的签名。如果偷懒把salt固定成 0除了会被服务端识别为重复请求还可能触发防重放机制导致后续所有请求都被拒绝。2.3 鉴权参数表每次调用必须携带什么下面这张表列出了这个翻译接口请求中的核心参数以及我在代码中实际设置的取值方式照着做可以避开八成签名错误。参数类型必填取值与说明qstring是待翻译文本UTF-8 原始字符串请求发送时自动编码fromstring是源语言代码如zh、en用auto表示自动识别tostring是目标语言代码如en、zhappidstring是申请得到的应用标识拼接进签名saltstring是随机数或时间戳防止重放signstring是MD5(appid 原始q salt 密钥) 的小写结果额外注意from参数。如果使用auto自动识别服务端会根据q的内容推断语种但签名时不包含from和to。因此你完全可以在签名前后随意修改语种参数不必重新计算 MD5只要保证appid、q、salt和密钥没变就行。不过我还是建议在调试阶段固定from比如先传fromzhtoen等跑通了再切换成auto这样能更快定位是语种识别问题还是翻译质量问题。3. 从 0 实现这个翻译接口的完整代码Python 可运行版3.1 最小可用代码单句翻译的请求与返回解析我先把最容易跑通的最小版本写出来。它不需要任何第三方依赖以外的库只需要requests、hashlib和random。这一步的意义是让你看到签名和请求的完整关系而不是把精力花在业务封装上。import hashlib import random import requests # 申请到的配置替换成你在控制台获取的真实值 APP_ID 你的appid SECRET_KEY 你的密钥 API_URL https://api.example.com/trans/v1/translate # 请替换为官方提供的接口地址 def translate_text(text, from_langauto, to_langzh): # 生成随机 salt这里使用时间戳可以保证大概率不同 salt str(random.randint(100000, 999999)) # 签名原文appid 原始文本 salt 密钥 raw APP_ID text salt SECRET_KEY sign hashlib.md5(raw.encode(utf-8)).hexdigest() params { q: text, from: from_lang, to: to_lang, appid: APP_ID, salt: salt, sign: sign, } resp requests.get(API_URL, paramsparams, timeout5) data resp.json() if data.get(error_code) 0: return data[trans_result][0][dst] else: raise RuntimeError(f翻译失败: {data.get(error_code)} {data.get(error_msg)})这段代码有三个关键点。第一raw APP_ID text salt SECRET_KEY里的text是原始字符串不能用quote或urlencode处理第二hashlib.md5(raw.encode(utf-8))将拼接结果按 UTF-8 编码中文文本必须这么做第三params字典交给requests.get后库会自动把q里的中文编码为百分号形式服务端在验签时会先解码再比对所以签名和传输互不干扰。这里有一个非常容易踩的坑如果你在写params前提前把q编码成了%E4%BD%A0那么拼接进签名的text也和编码后的值相同服务端验签时得到的是解码前的值结果就完全不一样了。我在刚接触这个接口时正是被这个细节拌倒后来才发现签名用的q必须跟原始输入一致。3.2 让代码支持自动语言检测和批量文本翻译把单句翻译封装好后批量翻译和自动检测只是换参数的问题。对于批量我一般会维护一个循环逐句调用translate_text而不是尝试构造数组参数。原因很简单这个接口的设计就是一次请求返回一个结果单次请求内传多句话通常不被支持要么报参数错误要么只翻译第一个所以循环是最可靠的方式。自动检测就是from_langauto服务端返回时会在trans_result里包含src字段告诉我它识别出的原语言。下面我给出一个同时支持批量与自动检测的版本并加入简单的并发控制。import hashlib import random import requests from concurrent.futures import ThreadPoolExecutor, as_completed def translate(text, to_langzh, from_langauto): # 省略签名逻辑同上文一致 pass def batch_translate(texts, to_langzh, from_langauto, max_workers3): results {} with ThreadPoolExecutor(max_workersmax_workers) as pool: future_map {pool.submit(translate, t, to_lang, from_lang): idx for idx, t in enumerate(texts)} for future in as_completed(future_map): idx future_map[future] try: results[idx] future.result() except Exception as exc: results[idx] f失败: {exc} # 按原顺序返回避免并发完成顺序影响业务 return [results[i] for i in range(len(texts))]max_workers直接决定了并发上限。我通常不会设超过 5因为这个平台对单 IP 的每秒请求数有限制max_workers3时并发请求能跑满 3 路已经能应付大多数个人工具场景。如果你发现响应码变成5400说明并发太高被限流了此时应该降低max_workers或者在每次请求之间加time.sleep。批量翻译最大的收益不是缩短单次延迟而是把多条小文本的请求时间重叠起来适合句子列表不超过几百条的离线翻译任务。3.3 请求超时、重试与错误码映射网络请求和 IO 一样必须考虑超时和重试。单次请求最理智的超时我设置为 5 秒原因是翻译接口依赖服务端内部资源如果 5 秒还没返回大概率是网络抖动或服务端排队再多等很可能浪费资源。重试可以使用指数退避策略第一次失败后等 1 秒第二次等 2 秒第三次等 4 秒。我把这段逻辑整合进一个更健壮的调用函数import time def request_with_retry(api_url, params, max_retries3, timeout5): for attempt in range(max_retries): try: resp requests.get(api_url, paramsparams, timeouttimeout) resp.raise_for_status() data resp.json() if data.get(error_code) 0: return data # 遇到限流或服务端错误时重试 if data.get(error_code) in (5400, 5401, 5403, 52001): time.sleep(2 ** attempt) continue # 其他错误码直接抛出不再重试 raise RuntimeError(f接口错误: {data.get(error_code)} - {data.get(error_msg)}) except requests.exceptions.Timeout: time.sleep(2 ** attempt) except requests.exceptions.ConnectionError: time.sleep(2 ** attempt) raise RuntimeError(f请求在 {max_retries} 次后仍失败)这里我列出几个常见错误码5400表示请求频率超过限额5401是接口未开通或欠费5403是请求 IP 不在白名单内52001是服务端内部错误。对于5403这种配置类错误重试没有意义但很多新手会一遍遍点发送不仅浪费时间还可能因为频率过高连带触发5400。所以在重试逻辑里我只对限流、服务端超时和网络异常做退避重试其余错误码直接抛异常让上层业务尽快感知。4. 面向生产环境的补充多语言版本与复杂场景适配4.1 用 Node.js 或 Java 重写签名与调用如果我们的项目不是 Python而是 Node.js 或 Java签名逻辑并不需要重写只要注意字符串编码。Node.js 里计算 MD5 时要确保待签名字符串使用utf8Java 中需要把拼接后的字符串getBytes(StandardCharsets.UTF_8)后再交给MessageDigest。下面分别给出核心代码片段。Node.js 侧核心签名函数const crypto require(crypto); function translateText(text, appid, secret, to zh) { const salt Date.now().toString(); const raw appid text salt secret; const sign crypto.createHash(md5).update(raw, utf8).digest(hex); const params new URLSearchParams({ q: text, from: auto, to, appid, salt, sign }); return fetch(https://api.example.com/trans/v1/translate? params) .then(r r.json()) .then(data { if (data.error_code 0) return data.trans_result[0].dst; throw new Error(${data.error_code}: ${data.error_msg}); }); }Java 侧核心签名函数import java.security.MessageDigest; import java.nio.charset.StandardCharsets; public static String translate(String text, String appid, String secret, String to) throws Exception { String salt String.valueOf(System.currentTimeMillis()); String raw appid text salt secret; MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(raw.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } String sign sb.toString(); // 用 URL 拼接参数并发送请求 String url https://api.example.com/trans/v1/translate?q URLEncoder.encode(text, UTF-8) fromautoto to appid appid salt salt sign sign; // ... 发起 HTTP GET解析 JSON }Node 和 Java 示例中的https://api.example.com也是占位符你需要替换成自己申请接入时拿到的真实域名。两种语言里最需要注意的同样是“签名前不要编码q”。Java 的URLEncoder.encode只用于拼接请求地址绝不参与raw拼接。很多从 Python 转 Java 的开发者习惯了 Python 的requests自动编码写 Java 时容易在这里多调一次encode签名就错。4.2 高频调用时的限流、队列与缓存策略生产环境不同于脚本调用频率会秒级上升。我见过最典型的翻车场景一个导购小程序每分钟要翻译近千条商品名称结果并发直接砸到接口上限服务端连返回5400都来不及先断开连接。解决方案是给调用层加一个本地队列和令牌桶。常见的做法是维护一个全局数组作为任务队列由一个单线程消费者每隔固定间隔取出若干任务发送请求。间隔的计算方法是如果你申请到的并发限制是每秒 10 次那就让消费者每 100 毫秒取一次任务每次取 1 条。如果一批任务有 100 条最坏情况下需要 10 秒完成但不会触发限流。缓存的意义更大。翻译接口返回的结果对于相同输入文本是基本稳定的命中缓存后除了省下请求额度还让高频词汇的翻译延迟从几百毫秒降到几毫秒。我习惯用 LRU 策略键为from_lang : to_lang : text值为翻译结果。注意键里要包含语种方向因为同一句中文翻译成英文和日文结果不同。对于商品名称这种重复率高的场景缓存命中率能超过 60%极大降低接口压力。4.3 长文本分片与保留格式翻译的思路单个接口对q的长度有上限比如 6KB 字节这会导致一篇很长的文章无法直接提交。常规拆法是把文本按段落或句子切分并限制每片不超过 1000 字节然后逐片翻译最后把结果按原顺序拼接。这样做保证了每片都有边距不会因为汉字 UTF-8 的 3 字节特性被拦腰截断。保留格式翻译需要一点点额外处理。常见做法是在切分时把\n、\t、Markdown 标记等特殊符号抽离成占位符比如先把文本按正则(?!\n)\n(?!\n)分段翻译完再把占位符还原。这个方案不算完美但能保住大多数换行和缩进。如果对格式要求极高建议直接使用平台提供的文档翻译扩展接口而不是在通用翻译接口上自己造轮子。5. 这个翻译接口的避坑指南签名、限流、超时与语种识别5.1 一直报Invalid Signature但代码看起来没错现象请求返回error_code10001提示签名无效。检查了appid、密钥和拼接顺序都没问题换一个单词就能成功换成中文就失败。原因签名用的q被提前 URL 编码了。很多开发者使用浏览器复制参数看到发送的请求里q%E4%BD%A0%E5%A5%BD就误以为签名也要用这个值拼接。实际上服务端验签时会将q先解码回原始文本再拼接appid q salt secret。解决把签名变量的值绑定到用户输入的原始字符串上永远不要在签名前对q做quote。如果你是在调试工具里构造请求也要先验证sign是基于原始值计算的而不是基于编码后的值。最简单的办法是先用纯英文字符跑通再逐步加入中文。5.2 请求成功但返回的语种识别结果总是en现象调用时传fromauto输入一段中文返回结果里的src是en翻译出来的内容也不对。原因服务端自动识别依赖文本足够长太短的文本如“嗯”“走”等短语容易被误判。另一个原因是请求参数里把from拼错成了fr导致实际传入了法语。但签名里的from不参与拼接所以编译期不会发现。解决对极短文本建议强制指定fromzh或fromen不要依赖auto。同时在代码中打印实际发出的请求地址检查from参数是否与预期一致。我一般会在业务层拦截长度小于 2 个字符的文本直接让用户选择源语言。5.3 批量翻译到第 20 条时开始返回5400限流现象脚本前 20 条全部成功之后连续报5400而且这个错误一直持续了好几分钟才恢复。原因标准版接口的瞬时并发有限制但很多开发者误以为限流是按“每天总量”计算的。批量脚本开满线程前 20 条把瞬时额度打满后续请求全部被拒。更糟糕的是没有写退避逻辑脚本会立即重试形成持续高压。解决在request_with_retry中加入对5400的指数退避重试同时在消费者例子中控制每秒钟的请求数。另一个有效做法是记录每次连续失败的时间戳如果一分钟内超过 10 次5400就停止该批次 30 秒后再继续。5.4 超时时间对英文短句够用翻译长文本时总是超时现象同样的代码翻译hello稳定返回翻译一段 500 字的英文就偶发timeout异常。原因翻译请求处理时长和文本长度正相关尤其是带术语表或使用高级模型时服务端处理时间可能超过 2 秒。而你把requests.get的timeout设成了 2 秒网络传输和排队稍微抖动一下就会超时。解决把超时拆成“连接超时”和“读取超时”连接超时保持 3 秒读取超时设成 10 秒甚至更长因为服务端只要在读取超时内返回任何数据都算有效。注意如果是用短连接批量发送最稳妥的方式是按文本长度的分位数动态设置超时比如每个字加 50 毫秒。5.5 错误码五花八门日志里全是数字没法定位现象线上系统调用失败后只记录了error_code52001运维不知道是接口挂了还是业务参数问题。原因很多代码只在raise RuntimeError时传入了错误码没有附带错误信息、请求参数和重试次数。而且raise之后堆栈只显示到封装层没有打印params内容。解决在所有异常抛出前用日志模块记录JSON.stringify({appid, q, from, to, error_code, error_msg, attempt})其中q只截取前 100 个字符避免日志膨胀。还要区分两类错误5400这类可以自动重试58002之类写死无法恢复的就需要人工介入。好的日志能让你从报错到定位原因只花半分钟如果你是夜班开发这半分钟决定你能继续睡觉还是被叫起来处理。6. 验证你的翻译接口代码从单测到线上压测的实用技巧代码写完并不代表能用至少要用三种方式验证。第一层是单元测试用mock拦截requests.get固定输入和输出断言签名计算是否正确。这里的技巧是把签名函数单独暴露出来不联网直接测sign值。你可以预计算一条已知数据appid123、qhello、salt1、secretabc用任何 MD5 工具算出基准签名做成回归测试。以后无论谁改代码只要签名结果不符测试就会失败。第二层是控制台自测在真实申请到密钥后先用curl构造一条最简单的请求校验响应。curl的好处是你能看到完整的 URL避免程序里隐式编码干扰判断。我常用的命令是这样的curl -G https://api.example.com/trans/v1/translate \ --data-urlencode qhello \ --data fromen \ --data tozh \ --data appid123 \ --data salt1 \ --data sign签名值注意q使用--data-urlencode其他参数用--data这样curl会正确编码中文而sign是固定的十六进制串不需要编码。如果这条请求返回成功说明你的密钥和签名流程没问题如果失败先检查sign是否基于未编码的q计算出。这一步能过滤掉“程序里的编码问题”和“签名算法问题”两种情况。第三层是压测脚本。不要一上来就并发几百先以 5 并发持续压 60 秒观察错误率和平均耗时。如果期间出现5400就把并发降为 2直到找到稳定的最大值。这个临界并发值就是你生产环境必须绑定的上限。我习惯把这个配置放到环境变量里比如TRANSLATE_MAX_WORKERS3防止不同环境用不同并发把接口打爆。压测还有一个附加收益你能直观感受到响应时间的分布。比如 200 字节的文本平均延迟 300ms但尾部 5% 的请求能到 800ms。如果业务要求亚秒级体验就需要把翻译结果预先写入缓存或者改用异步化用户先看到原文本翻译完成后通过推送展示。这类优化都来自压测数据而不是猜。最后分享一个我自己的习惯每次上线翻译相关功能前我会拿着生产密钥在测试环境跑一遍中英互译的例子并故意输入一个超长文本确认它能正确返回参数错误而不是直接超时。这个简单动作已经帮我拦截过三四次密钥过期、白名单 IP 变更等问题。这个接口的坑并不多但每个坑都很深。希望这篇带有完整代码和踩坑记录的文章能帮你在接入路上少走一段弯路。本文还有配套的精品资源点击获取