简介:百度翻译接口完整代码示例,面向需要在应用或网站中集成多语言翻译功能的开发者,尤其适合刚接触API调用、身份验证及HTTP请求流程的初中级程序员,用于快速解决从零接入百度翻译API的落地问题。压缩包内共2个文件,分别为Python脚本与JavaScript文件,覆盖两种主流技术栈下的调用方式,整体仅5KB,轻量易读,便于直接修改复用,也适合作为代码阅读练习。已有640人学习浏览,资源清晰展示了请求URL的构建、请求头设置、参数URL编码、JSON响应解析以及常见错误处理等关键环节,可作为理解API交互全流程的入门参考。代码在2023年8月前有效,能帮助读者快速完成翻译功能从零到一的落地实践,并在此基础上进一步扩展缓存策略、批量翻译与异步处理等优化思路。
1. 百度翻译接口完整代码:先从签名算法说起
翻译接口的门槛从来不在 HTTP 请求,而在签名。我最早接百度翻译开放平台时,照着网上的旧教程抄代码,结果第一条请求就撞上 52003——认证失败。后来才发现,2020 年之后接入方式改成了 token 鉴权,老代码里的「appid + 密钥 + 拼接字符串 + MD5」还停留在上一代标准。这套接口的真实价值在于:一次搞定正确的鉴权流程、拿到可直接改用的完整代码,并且把长文本分批、错误码排查这些后端才踩的坑提前排掉。适合要在软件/插件里集成多语言翻译、又不想反复调试签名逻辑的开发者。本文给出 Python 与 JavaScript 两套可运行实现,按步骤替换参数即可上线。
2. 调用前必须吃透的参数体系:appid、密钥、salt 和签名
2.1 新版鉴权流程:先换 token,再做签名
百度翻译开放平台从 2020 年 10 月起,标准版接口全面切换为 token 鉴权。旧版的「MD5 拼接签名」并没有被废弃,而是被包了一层——你得先用 appid 和密钥换一个 24 小时有效的 token,再用这个 token 去替换请求中的 secret 字段。
这张表的参数含义务必记清楚:
| 参数 | 说明 | 是否必填 |
|---|---|---|
| appid | 创建应用时分配的 ID,形如 2025010100123456 | 是 |
| key | 创建应用时的密钥明文,仅用于换取 token | 是(换取 token 时) |
| token | 换取到的临时凭证,24 小时有效 | 是(请求翻译时) |
| salt | 随机整数,防重放,每次请求重新生成 | 是 |
| sign | 签名串,具体算法见下文 2.2 | 是 |
| q | 待翻译文本,UTF-8 编码 | 是 |
| from / to | 源语言 / 目标语言代码,如 zh、en、auto | 是 |
换取 token 的接口是https://api.fanyi.baidu.com/api/trans/v3/apitrans/token,请求方式为 GET,需要带上grant_type=client_credentials、appid、key三个参数。响应是一个 JSON,结构如下:
{ "access_token": "1234567890abcdef", "expires_in": 86400 }请求翻译时,把access_token当作密钥去生成签名。换句话说,旧版拼接是「appid + q + salt + key」,新版是「appid + q + salt + token」。很多老教程只改了请求地址,没改签名拼接,所以永远报 52003。
2.2 签名算法:MD5 拼接顺序一个字符都不能错
如果你只是想在插件里快速跑通,不要自己实现签名——直接在服务端放一份现成代码改参数。但理解算法仍然重要,因为调试时你得能判断是签名错还是网络错。
签名原文的拼接规则是:
sign = MD5(appid + q + salt + token)四个字段按顺序直接拼接,中间没有分隔符。q 必须和实际请求体里传的完全一致,包括大小写、空格、换行符。常见翻车点:请求时用了encodeURIComponent(q),签名时用了原始字符串。
MD5 之后转小写十六进制字符串。每一步的中间结果可以打印出来,和官方调试工具比对。我一般会建议先写死一个 salt 值(比如 123456),这样签名结果可复现,方便排查。线上再改成随机数。
2.3 开发前准备:申请密钥与测试配额
到翻译开放平台控制台创建应用时,需要选择「标准版」还是「尊享版」。标准版免费额度是单次请求最长 6000 字符,每月 100 万字符;尊享版计费。个人插件开发选标准版完全够用。
创建应用后,页面会给出一个 appid 和一个密钥,密钥只显示一次,务必复制到安全的地方。常见操作失误是把密钥写死在插件包里,导致打包发布后密钥泄露,被他人盗用配额。正确做法是:插件端只保留 appid,密钥和 token 换发放在你自己的服务端中转。
3. 完整代码实现:Python 版与脚本插件版各来一份
3.1 Python 版:一次性给你可直接改用的完整请求函数
我用 requests 库写了一个最小可用实现,复制后替换三个常量即可跑通:
# -*- coding: utf-8 -*- import hashlib import json import random import time import requests APPID = "你的appid" TOKEN = "你的token" # 先用 curl 换到,再填进来 def translate_text(q, from_lang="auto", to_lang="zh"): # 1. 生成 salt salt = str(random.randint(32768, 65536)) # 2. 计算签名:appid + q + salt + token 直接拼接 sign_str = APPID + q + salt + TOKEN sign = hashlib.md5(sign_str.encode("utf-8")).hexdigest() # 3. 构造请求参数 payload = { "q": q, "from": from_lang, "to": to_lang, "appid": APPID, "salt": salt, "sign": sign, "token": TOKEN, } # 4. 发起 POST 请求 url = "https://api.fanyi.baidu.com/api/trans/v3/apitrans/text" resp = requests.post(url, data=payload, timeout=5) result = resp.json() # 5. 解析结果 if "trans_result" in result: dst = result["trans_result"][0]["dst"] return dst else: raise RuntimeError(f"翻译失败: {result}")逻辑说明:第 2 步的签名拼接必须用原始字符串 q,不能用 URL 编码后的值,这是最容易出错的地方。第 4 步用的是data参数而非json参数,因为接口要求application/x-www-form-urlencoded格式。第 5 步只取了第一个翻译结果,生产环境建议循环取出所有段落。
参数说明:from_lang和to_lang支持语言代码。auto表示自动识别源语言;翻译成繁体中文用cht,不是zh-tw;日文是jp,不是ja。这个简写体系是老用户最容易踩的命名坑。
调用方式:
print(translate_text("Hello, world.", "auto", "zh")) # 输出:你好,世界。3.2 JavaScript 版:插件脚本里怎么安全地携带签名
如果你是在浏览器插件或本地脚本里调用,复用同一个签名逻辑即可。注意:MD5 需要引库,生产环境建议用crypto-js或服务端中转。
const crypto = require("crypto"); function translate(q, fromLang = "auto", toLang = "zh") { const appid = "你的appid"; const token = "你的token"; const salt = Math.floor(Math.random() * 10000 + 1000).toString(); // 用 Node.js 自带 crypto 计算 MD5 const sign = crypto .createHash("md5") .update(appid + q + salt + token) .digest("hex"); const params = new URLSearchParams(); params.append("q", q); params.append("from", fromLang); params.append("to", toLang); params.append("appid", appid); params.append("salt", salt); params.append("sign", sign); params.append("token", token); return fetch("https://api.fanyi.baidu.com/api/trans/v3/apitrans/text", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params.toString(), }) .then((res) => res.json()) .then((data) => { if (data.trans_result) { return data.trans_result.map((item) => item.dst).join("\n"); } throw new Error(JSON.stringify(data)); }); }这段代码和 Python 版逻辑完全对称,核心差异只在 URLSearchParams 构造请求体。map那一步把所有分段结果合并成多行字符串,适合插件里直接渲染到弹窗。如果你的插件环境是浏览器而非 Node,crypto 库不可用,需要引入crypto-js,或者干脆把签名计算放到服务端,前端只拿结果。
3.3 服务端中转:防止 token 泄露的标准做法
把 token 直接写在前端代码里,等于把钥匙交给每个用户。标准做法是在自己的后端加一个代理接口:
# 先手动换取 token 保存到环境变量 curl "https://api.fanyi.baidu.com/api/trans/v3/apitrans/token?grant_type=client_credentials&appid=你的appid&key=你的key" # 输出中的 access_token 存入环境变量 BAIDU_TOKEN后端接口每收到一次翻译请求,从环境变量读 token,签名后转发给百度翻译。这样一来,插件端只暴露你自己的服务地址,token 永远不出站。代价是多一跳网络延迟,但换来的是配额安全,这笔账值得。
4. 响应解析、错误码与长文本分批策略
4.1 响应 JSON 结构:主段与逐句翻译的关系
接口响应格式是稳定的,但新手经常把trans_result和result搞混。正常响应长这样:
{ "from": "en", "to": "zh", "trans_result": [ { "src": "Hello world.", "dst": "你好,世界。" }, { "src": "How are you?", "dst": "你好吗?" } ] }如果请求时 q 传了多段文本(用换行符\n分隔),返回的trans_result是一个数组。数组下标与输入的段落顺序一一对应。如果你的原始文本没有换行,只会有数组里一个元素。
解析时注意两点:一是不要用浮点 JSON 解析,百度翻译偶尔会返回非标准 Unihan 字符;二是src字段可能为空,表示自动检测语种失败。更稳妥的解析策略是只取dst,不依赖src。
4.2 错误码速查表:52003 与 54004 的区别
以下是高频错误码的排查路径,我根据实际调用经验整理:
| 错误码 | 含义 | 常见原因 | 解决方案 |
|---|---|---|---|
| 52001 | 请求超时 | 网络波动或 q 过长 | 缩短文本,设置 5 秒超时重试一次 |
| 52003 | 认证失败 | 签名错误或 token 失效 | 检查签名拼接顺序,重新换 token |
| 54003 | 请求频率受限 | 并发过高或单 QPS 超标 | 加延时队列,控制 1 QPS |
| 58001 | 语言方向不支持 | from和to组合无效 | 核对语言代码表,注意jp不是ja |
| 54004 | 余额不足 | 免费额度耗尽或未开通计费 | 控制台查额度,切换付费或降级 |
排查顺序我一般这样走:先看错误码;如果 52003,先打印签名原文,确认q没被 URL 编码,然后确认 token 是否超过 24 小时;如果这两个都没问题,再看时间戳是否和服务器差距过大。
注意:错误码 52003 和 54003 只有一字之差,前者是签名错,后者是频率受限。很多老开发者遇到 54003 习惯性去检查签名,结果方向完全错了。
4.3 长文本分批:6000 字符上限的拆法
标准版单次请求最大 6000 字符,这是 ASCII 字符数还是 UTF-8 字节数?答案是字符数。一个中文占一个字符,一个 emoji 可能占两个。写了个粗暴但实用的切分函数:
def split_text(text, max_len=5000): """按最大长度切分,优先断在句子边界""" if len(text) <= max_len: return [text] chunks = [] cursor = 0 while cursor < len(text): end = min(cursor + max_len, len(text)) # 往回找最近的句号或换行 last_period = text.rfind("。", cursor, end) last_newline = text.rfind("\n", cursor, end) break_pos = max(last_period, last_newline) if break_pos > cursor + max_len // 2: # 至少切掉一半才算有效边界 end = break_pos + 1 chunks.append(text[cursor:end]) cursor = end return chunks切分逻辑说明:max_len=5000留 1000 字符余量,避免响应拼接后超过接口隐式限制。rfind从后往前找.与换行,尽量不砍断语义单元。如果找不到自然边界,直接硬切到max_len,保证程序不死循环。
分批之后,每个 chunk 单独调用翻译接口,最后按顺序拼接。这里有个隐蔽问题:不同 chunk 翻译出的结果可能存在术语不一致。要处理这个问题,得用第 6 章的术语表方案。
5. 避坑手册:签名失败、限流、乱码的五个真实翻车现场
5.1 现象:52003 认证失败,但 appid 和密钥明明是对的
原因:签名用的 token 是旧版,已经过期。token 有效期 24 小时,你在上午换的 token,晚上就会失效。更隐蔽的是,如果你用 curl 手动换 token 测试,然后把 token 复制到了代码里,代码跑了一天后必然挂。
解决:换 token 的流程单独封装成函数,每次启动时检查本地缓存文件的时间戳,超过 20 小时自动刷新。不要手动复制 token 字符串。
5.2 现象:翻译出来的结果是乱码,英文变成中文再传回去又变英文
原因:请求参数q在签名时是 UTF-8,但 POST 时被某些语言框架自动做了 URL 编码解码。Python 的requests.post(data=payload)会自动编码,不会出问题;但如果是自己构造的 HTTP 请求,容易漏掉Content-Type头,导致服务端按 ISO-8859-1 解码。
解决:构造data前显式把字符串编码为 UTF-8,响应解码统一用resp.content.decode("utf-8"),不要用resp.text隐式解码。排查时打印返回体的原始字节,看\\u转义是否符合预期。
5.3 现象:长文本翻译经常漏段落,或者最后一段消失
原因:分块逻辑里cursor递增时,如果break_pos恰好在切分点的下一行,end = break_pos + 1会把换行符吞进去,导致下一 chunk 拼接后多出半个空行或缺失字符。
解决:切分函数把换行符当作边界的一部分保留下来,分块之间用空字符串分隔而不是尝试精细化切割。翻译结果拼接时,用"\n".join([c.strip() for c in chunks])过滤多余空白。
5.4 现象:调用频率一高就返回 54003,并发请求全部失败
原因:免费版 QPS 限制是 1,也就是每秒最多一个请求。插件批量翻译时并发发起了多个请求,瞬间超限。返回的频率限制不是每个请求独立的,而是滚动窗口统计。
解决:加一个简单的单机限流队列:
import time class RateLimiter: def __init__(self, qps=1): self.qps = qps self.last = 0.0 def wait(self): now = time.time() gap = now - self.last if gap < 1.0 / self.qps: time.sleep(1.0 / self.qps - gap) self.last = time.time()用法是在每次调用translate_text之前执行limiter.wait()。如果并发量很大,建议挪到服务端做全局限流,配合队列逐条处理。
5.5 现象:部分生僻字翻译结果与网站不一致
原因:接口的语种识别倾向不同。网站版会先识别from再匹配专业词库,API 版默认走通用模型。比如医疗术语、法律条款这类专业文本,API 翻译结果质量明显低于网页端。
解决:不要依赖自动识别。明确指定from为en或具体语种,不要填auto。如果要提升术语准确率,进入第 6 章的术语表方案。
6. 进阶技巧:自定义术语表与接口状态自检
这一章讲两个真正提升使用体验的习惯,一个是术语表,一个是上线前的自检用例。
先说话拔术表。百度翻译开放平台支持在控制台为应用添加自定义术语,但很多人不知道这个功能对 API 请求同样生效。设置方式是:在控制台打开你的应用,进入「术语表」页签,添加「原文→译文」的固定映射,例如:
browser -> 浏览器(默认译法:浏览器) 函数 -> function(默认译法:功能)配置生效时间约 5 分钟。此后 API 请求中如果命中了术语,会优先返回你在术语表里的定义,而不是模型默认结果。术语表同一语言方向最多 500 条,对垂直领域的软件/插件翻译,这比任何 prompt 调优都可靠。如果你在做一个专业工具,强烈建议把核心 UI 文案的常见误译全部录入术语表,让每次翻译的结果稳定。
第二条习惯是写一个接口自检脚本,把调试期翻过的车固化成用例。我在每次部署前都会跑一遍下面的检查:
# 1. 基础翻译 curl "接口地址" --data "q=hello&from=en&to=zh&appid=xxx&salt=123&sign=xxx&token=xxx" # 2. 超长文本 5000 字符,确认不报 52001 # 3. 特殊字符:引号、换行符、emoji,确认签名一致 # 4. 连续请求 3 次,确认不触发 54003 # 5. 请求一个不存在的语言方向,确认返回 58001 而不是 52003特别说一下第 3 条。我踩过最深的一个坑是:待翻译文本里包含英文字符串长引号(")时,签名阶段用原始字符串,但 POST 阶段被requests自动做了urlencode,导致服务器拿到的q和签名里的q不一致。解决办法是在签名前先做一次urllib.parse.quote(q, safe=""),保证签名和传输完全同一个编码状态。从那以后,我每次接翻译接口都会在测试用例里加入特殊字符组合,先把边界跑完再谈上线。
顺带提一句调试技巧:在代码里加一个环境变量DEBUG_SIGN=1,开启后打印签名原文和 MD5 结果。上线时关掉。这比逐行断点快得多——签名这类问题,肉眼比对长度和字符就能定位。
希望这些踩坑记录帮你节约一个下午的调试时间。接口本身不难,难的是所有细节都恰好被处理好。一个能稳定跑半年的翻译服务,胜过一个偶尔翻车但指标漂亮的项目。
本文还有配套的精品资源,点击获取