公司动态

DeepSeek API实现SRT字幕自动英译中教程

📅 2026/9/2 20:07:59
DeepSeek API实现SRT字幕自动英译中教程
前阵子整理一批上世纪 80 年代的老动画资源比如 1984 年的《梦战士银翼超人》Wingman发现很多外挂字幕都是英文版。网上中文字幕要么残缺要么时间轴对不上手动逐条翻译又完全不现实。后来我直接把 DeepSeek API 接进来做了一条自动化字幕翻译链路输入一个英文 SRT 文件输出一个保留时间轴、风格统一的中文 SRT 文件。整个过程不涉及语音识别也不需要重新压制视频成本很低适合个人整理收藏和老番字幕补全。本文会把这条链路完整拆开从 DeepSeek API 的基础调用方法到 SRT 字幕解析、提示词设计、批量翻译脚本再到常见报错和工程化建议。如果你也想给老番、纪录片或课程视频做“英转中字幕”可以直接照着操作。1. 背景与核心概念1.1 字幕翻译与视频翻译的差别很多刚接触字幕处理的朋友会把“字幕翻译”和“视频翻译”混在一起。实际上两者差别很大视频翻译通常包含语音识别ASR、文本翻译、语音合成TTS、时间轴对齐甚至还要考虑人声分离和字幕压制链路很长。字幕翻译只处理已有字幕文本输入是 SRT、ASS、SSA 或 VTT 文件输出仍是同格式的字幕文件。它不改变视频画面也不重新生成音频只把文字内容从一种语言换成另一种语言。本文讨论的是第二种场景也是最容易用大模型 API 自动化的场景。你只需要保证字幕文件本身存在且时间轴正确剩下的事情就是把文本提取出来交给 DeepSeek 翻译再按原顺序写回文件。1.2 为什么选择 DeepSeek 做字幕翻译字幕翻译看起来只是“英译中”但实际要求并不低。长句要拆分口语要自然人名要统一遇到双关语还得适当意译。DeepSeek 在这个过程中有几个明显优势中文翻译质量稳定尤其在口语化和长句理解上优于很多通用机器翻译引擎。API 兼容 OpenAI 协议你既可以用官网 SDK也可以直接用 requests 调用代码迁移成本很低。支持一次传入多条字幕通过 JSON 结构化返回正好适合批量处理。有 deepseek-chat 和 deepseek-reasoner 两个模型方向前者适合日常翻译后者适合需要推理分析的复杂场景。另外字幕里经常会出现人名、技能名和世界观专有名词DeepSeek 对“按术语表翻译”这类指令的理解能力比较强。你可以把术语表直接放进系统提示词让它在翻译时统一遵循。1.3 本文适合哪些读者如果你属于以下情况之一这篇教程会很有帮助手上有大量英文 SRT 字幕希望批量转成中文。正在学习 DeepSeek API 调用想找一个有真实业务场景的练手项目。使用过网页版在线翻译但发现它无法保留 SRT 时间轴想用代码解决。对字幕翻译的工程化、缓存、重试和成本控制感兴趣。2. 环境准备与版本说明2.1 运行环境本文示例以 Python 3 为基础推荐 3.9 及以上版本。操作系统方面Windows、macOS、Linux 都可以只要终端能正常执行 Python 命令即可。你需要准备Python 3.9。一个 DeepSeek 开放平台账号并创建 API Key。一个英文 SRT 字幕文件。IDE 不强求VS Code、PyCharm 甚至系统自带编辑器都行。我自己习惯用 VS Code方便直接对比输入输出文件。2.2 获取 DeepSeek API KeyDeepSeek 开放平台的使用流程和其他大模型平台类似登录 DeepSeek 开放平台。在控制台找到 API Keys 管理页面。创建一个新的 API Key创建后只显示一次需要立即复制保存。确认账户有足够的余额。字幕翻译虽然是文本任务但批量处理时仍会消耗 token建议先充少量金额测试。需要注意API Key 是敏感信息不要提交到 Git 仓库也不要直接硬编码在线上代码里。本文示例用环境变量读取。2.3 安装 Python 依赖字幕翻译脚本主要依赖 OpenAI SDK因为 DeepSeek 的接口兼容 OpenAI。pip install openai如果你想先跑一个最小请求测试也可以只安装requests。但完整脚本里我用的是 OpenAI SDK所以建议直接安装pip install openai requests版本方面OpenAI SDK 的 1.x 版本都支持自定义base_url这就是接入 DeepSeek 的关键。具体版本号不需要固定以 pip 当前解析到的最新稳定版为准。3. DeepSeek API 调用核心知识3.1 OpenAI 兼容接口与 Base URLDeepSeek API 最大的特点是兼容 OpenAI Chat Completions 协议。也就是说你在 OpenAI SDK 里只需把base_url改成 DeepSeek 的地址就能把请求发到 DeepSeek 模型上。常见的两个参数如下BASE_URL https://api.deepseek.com MODEL deepseek-chatdeepseek-chat适合通用的对话、翻译、文本生成任务。deepseek-reasoner适合需要复杂推理、逻辑分析的任务但翻译任务通常不需要每次都做深度推理使用deepseek-chat性价比更高。需要说明的是模型名称和接口地址可能会随官方迭代调整实际使用前建议以官方文档为准。本文代码中的地址是长期可用的基准示例。3.2 使用 curl 快速测试在写完整 Python 脚本之前建议先用 curl 验证 API Key 是否有效。下面是一个最小请求示例curl https://api.deepseek.com/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的APIKey \ -d { model: deepseek-chat, messages: [ {role: system, content: You are a helpful assistant.}, {role: user, content: Translate this into Chinese: I am Wingman!} ], stream: false }如果 Key 有效且余额充足会返回一段 JSON里面包含模型回复内容。你可以在返回结果中看到choices[0].message.content字段这就是翻译后的文本。3.3 使用 OpenAI SDK 调用 DeepSeek用 Python 调用时只需要把OpenAI客户端的base_url参数指向 DeepSeekfrom openai import OpenAI client OpenAI( api_keysk-你的APIKey, base_urlhttps://api.deepseek.com ) resp client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: 你是专业字幕翻译。}, {role: user, content: 翻译这句话Lets protect this world.} ], temperature0.3 ) print(resp.choices[0].message.content)这段代码可以独立运行也是后续批量翻译脚本的基础。3.4 关键参数说明在字幕翻译场景中以下参数比较重要参数作用字幕翻译建议model选择模型优先deepseek-chatmessages构造对话上下文System 放翻译规则User 放字幕 JSONtemperature控制随机性0.3 左右保证术语稳定max_tokens限制生成长度根据批次大小设置 4096 左右response_format要求 JSON 输出设为{type: json_object}stream是否流式输出批量场景建议 False温度参数值得多说一句。如果你希望翻译结果稳定尤其是人名和专有名词不要每批都不一样temperature不要设太高。0.3 是比较合适的起点。如果发现翻译太“死板”可以适当提高到 0.5。3.5 本地部署与第三方封装工具除了官方 APIDeepSeek 也有开源模型权重可以在本地部署。社区里已经有不少封装好的桌面工具、插件和客户端比如你在网上可能看到的 DeepSeek Harness、Hermes 等它们本质上还是对官方 API 或本地模型做了一层界面封装。如果只是个人整理字幕直接用官方 API 最省事。如果字幕内容比较敏感或者公司要求数据不能出内网可以考虑本地部署再让脚本通过http://localhost:8000/v1这一类的 OpenAI 兼容地址接入。本文不展开本地部署的完整步骤因为依赖 GPU 和模型权重环境差异太大。只要记住本地部署后调用方式仍然可以复用本文的 Python 脚本只需改BASE_URL和MODEL。4. 完整实战DeepSeek 批量翻译 SRT 字幕4.1 理解 SRT 字幕格式SRT 是最常见的字幕格式之一。一个标准 SRT 文件由多条字幕组成每条字幕包含序号、时间轴和文本中间用空行分隔。例如1 00:00:01,000 -- 00:00:04,000 I am Wingman! 2 00:00:05,000 -- 00:00:08,000 Lets protect this world.翻译时最核心的原则是时间轴和序号不能动只替换文本内容。如果把时间轴也交给模型处理很容易出现格式错误。所以我建议在脚本中先解析 SRT把文本内容提取成结构化 JSON翻译完成后再把时间轴拼回去。4.2 提示词设计字幕翻译的提示词和普通“帮我翻译一句话”完全不同。你需要告诉模型几条约束保持口语化、自然。不要合并或拆分字幕条目。人名和专有名词保留原文除非有公认译名。只输出 JSON不输出多余解释。原文本为空时译文也返回空字符串。下面是我常用的系统提示词你是专业的字幕翻译负责将英文字幕翻译为简体中文。 要求 1. 翻译口语化、自然保留角色语气。 2. 人名字名等专有名词保留原文除非有公认中文译名。 3. 不要合并/拆分字幕条目必须保持原 id 一一对应。 4. 只输出 JSON不要输出解释。 可接受格式为 [{id:1,translation:...}] 或 {data:[{id:1,translation:...}]} 5. 原文本为空时translation 返回空字符串。把翻译规则放在 System 提示词里把待翻译内容放在 User 提示词里。这样模型每次都能按照同一套规则工作。4.3 编写完整脚本下面是一个可以直接运行的 Python 脚本。它支持批量翻译、自动缓存进度、失败重试适合处理一整集甚至一整季的字幕。# -*- coding: utf-8 -*- DeepSeek 英转中字幕批处理脚本 用法: python translate_srt.py 输入.srt 输出.srt import json import os import re import sys import time from openai import OpenAI API_KEY os.getenv(DEEPSEEK_API_KEY, ) BASE_URL https://api.deepseek.com MODEL deepseek-chat BATCH_SIZE 10 MAX_RETRY 3 CACHE_SUFFIX .trans_cache.json SYSTEM_PROMPT 你是专业的字幕翻译负责将英文字幕翻译为简体中文。 要求 1. 翻译口语化、自然保留角色语气。 2. 人名字名等专有名词保留原文除非有公认中文译名。 3. 不要合并/拆分字幕条目必须保持原 id 一一对应。 4. 只输出 JSON不要输出解释。 可接受格式为 [{id:1,translation:...}] 或 {data:[{id:1,translation:...}]} 5. 原文本为空时translation 返回空字符串。 def parse_srt(content): 解析 SRT 字幕文件内容返回包含 id、start、end、text 的列表。 blocks [] pattern re.compile( r(\d)\s*\n(\d{2}:\d{2}:\d{2},\d{3})\s*--\s*(\d{2}:\d{2}:\d{2},\d{3})\s*\n(.*?)(?\n\s*\d\s*\n|\Z), re.S, ) for m in pattern.finditer(content): blocks.append({ id: int(m.group(1)), start: m.group(2), end: m.group(3), text: m.group(4).strip(), }) return blocks def load_cache(cache_path): 加载断点续传缓存。 if os.path.exists(cache_path): with open(cache_path, encodingutf-8) as f: return json.load(f) return {} def save_cache(cache_path, cache): 保存翻译进度缓存。 with open(cache_path, w, encodingutf-8) as f: json.dump(cache, f, ensure_asciiFalse, indent2) def translate_batch(client, batch, cache, cache_path): 翻译一批字幕并把结果写入缓存。 to_translate [b for b in batch if str(b[id]) not in cache] if not to_translate: return payload [{id: b[id], text: b[text]} for b in to_translate] user_content json.dumps(payload, ensure_asciiFalse) for attempt in range(MAX_RETRY): try: resp client.chat.completions.create( modelMODEL, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_content}, ], temperature0.3, max_tokens4096, response_format{type: json_object}, ) content resp.choices[0].message.content data json.loads(content) if isinstance(data, dict): data data.get(data) or data.get(translations) or [] for item in data: if isinstance(item, dict) and id in item and translation in item: cache[str(item[id])] item[translation] save_cache(cache_path, cache) return except Exception as e: print(f[WARN] 批次重试 {attempt 1}/{MAX_RETRY}: {e}) time.sleep(2 ** attempt) raise RuntimeError(翻译批次失败: user_content[:100]) def merge_srt(blocks, cache): 把翻译结果写回 SRT 格式。 out_lines [] for b in blocks: translated cache.get(str(b[id]), b[text]) out_lines.append(f{b[id]}\n{b[start]} -- {b[end]}\n{translated}\n) return \n.join(out_lines) def main(): if len(sys.argv) 3: print(用法: python translate_srt.py 输入.srt 输出.srt) return in_path, out_path sys.argv[1], sys.argv[2] cache_path out_path CACHE_SUFFIX with open(in_path, encodingutf-8) as f: content f.read() blocks parse_srt(content) print(f共解析到 {len(blocks)} 条字幕) client OpenAI(api_keyAPI_KEY, base_urlBASE_URL) cache load_cache(cache_path) for i in range(0, len(blocks), BATCH_SIZE): batch blocks[i:i BATCH_SIZE] translate_batch(client, batch, cache, cache_path) print(f进度: {min(i BATCH_SIZE, len(blocks))}/{len(blocks)}) with open(out_path, w, encodingutf-8) as f: f.write(merge_srt(blocks, cache)) print(f翻译完成: {out_path}) if __name__ __main__: main()这段脚本有几个设计点值得说明parse_srt负责解析时间轴翻译过程从不修改 start 和 end。缓存文件保存的是“字幕 id 到译文”的映射。如果脚本中途断了再次运行时会跳过已经翻译过的条目只处理剩余部分。translate_batch每次传入多条字幕减少请求次数也降低费用。重试逻辑使用指数退避遇到网络抖动或临时限流时可以自动恢复。4.4 运行与验证首先设置环境变量export DEEPSEEK_API_KEYsk-你的APIKey然后运行脚本python translate_srt.py wingman_ep29.en.srt wingman_ep29.zh.srt假设输入文件内容如下1 00:00:01,000 -- 00:00:04,000 I am Wingman! 2 00:00:05,000 -- 00:00:08,000 Lets protect this world.正常运行后输出文件应类似1 00:00:01,000 -- 00:00:04,000 我是银翼超人 2 00:00:05,000 -- 00:00:08,000 让我们守护这个世界。你需要在播放器里载入原始视频挂上这个中文字幕重点检查两点时间轴是否和原来的英文字幕一致人名和关键术语是否符合预期。4.5 进阶如何避免上下文割裂字幕是按条翻译的但台词之间存在上下文。比如角色前面说“我要去那里”后面才说“那里就是 Wingman 的基地”。如果模型只看单条字幕可能会把“那里”翻译得不够准确。一个简单的改进思路是在 User Prompt 里把当前批次的前几条字幕也带进去但只要求模型对目标 id 生成译文。这样模型能看到上下文又不会误解输出范围。例如user_content json.dumps({ context: previous_last_5_texts, to_translate: payload }, ensure_asciiFalse)对应 Prompt 里再加一句用户输入格式为 JSON其中 context 是上下文to_translate 是待翻译列表。 你只需要翻译 to_translate 中的条目。这个方案适合剧情连贯性强的老番效果比完全独立翻译更自然。5. 常见问题与排查思路5.1 常见报错对照表问题现象常见原因解决思路返回 401 Authentication FailsAPI Key 错误或未设置环境变量检查 Key 是否复制完整重新 export返回 402 Insufficient Balance账户余额不足到 DeepSeek 平台充值后重试提示 model 不存在模型名称写错使用deepseek-chat或查询官方文档请求超时网络不稳定或请求体过大增加超时时间减小 BATCH_SIZEJSON 解析失败模型输出被截断或格式混乱增加 max_tokens检查 response_format译文与字幕 id 不对应Prompt 约束不够明确强调保持 id 一一对应调整返回 JSON 结构输出内容只有英文模型没有理解翻译要求在 System Prompt 中增加“必须输出简体中文”5.2 翻译质量不理想怎么办如果你发现翻译结果太直译、术语不统一不要急着换模型先调整 Prompt在 System Prompt 中加入术语表。把temperature调低到 0.2 左右。对同一批字幕多跑几次对比结果。如果单条字幕太长先按句号切分再翻译避免长句截断。5.3 网络超时与限流批量任务经常遇到“偶尔一次请求超时”的情况。本文脚本已经包含重试逻辑但如果你在别的脚本中复制代码建议也加上指数退避。注意运行环境需要能正常访问api.deepseek.com如果公司网络有白名单限制需要联系网络管理员放行。5.4 字幕时间轴丢失问题很多在线网页翻译工具会把 SRT 当普通文本处理输出后时间轴全没了。本文脚本通过先解析、后回写的方式彻底规避这个问题。前提是输入的 SRT 文件本身时间轴格式正确。如果你的输入文件是 ASS 或 SSA建议先用pysubs2这类库转换成 SRT再走本文流程。6. 最佳实践与工程建议6.1 推荐项目结构处理多集字幕时建议用统一的目录结构subtitle_project/ ├── input/ │ ├── wingman_ep01.en.srt │ └── wingman_ep29.en.srt ├── output/ │ ├── wingman_ep01.zh.srt │ └── wingman_ep29.zh.srt ├── glossary.json ├── translate_srt.py └── requirements.txt这样输入输出分离缓存文件也可以统一放在 output 目录不会污染原始字幕。6.2 术语表与风格统一翻译一个系列作品最重要的就是术语统一。你可以把专用名词整理成 JSON 文件{ Wingman: 银翼超人, Aoi: 葵, Dream Fighter: 梦战士 }然后在系统提示词中追加翻译时参考以下术语表 Wingman - 银翼超人 Aoi - 葵这样即使分多批翻译也能保证后续校验时不会出现“第一集叫银翼超人第二集叫翼人”这种不一致。6.3 成本控制与缓存策略字幕翻译的 token 消耗取决于字幕条数和文本长度。要控制成本可以从三方面入手单次请求尽量合并多条字幕减少请求次数。使用缓存文件断点续传避免失败后重新消耗 token。先拿一集测试统计消耗再决定是否批量处理整季。DeepSeek 的定价会随官方策略变化具体费用以平台账单为准。但思路是一样的批处理比逐条请求便宜得多缓存比重复翻译便宜得多。6.4 API Key 安全与生产环境注意不管是在本地脚本还是服务器任务中使用API Key 都不要硬编码。推荐的做法是export DEEPSEEK_API_KEYsk-你的APIKey脚本中从环境变量读取。如果使用 CI/CD 或定时任务可以把 Key 放到密钥管理服务中并配置最小权限。字幕内容如果涉及版权素材建议只用于个人学习和备份不要公开发布翻译后的字幕文件更不要用于商业传播。技术本身是工具合规使用才能长久。6.5 从单集脚本到批量工具当你觉得单集翻译脚本稳定之后可以再加一层循环批量处理整个文件夹import glob for srt_path in sorted(glob.glob(input/*.en.srt)): out_path srt_path.replace(input/, output/).replace(.en.srt, .zh.srt) # 在这里复用 translate_srt 的核心函数做好缓存、日志和失败告警后这套流程基本可以无人值守跑完一整季老番。老番字幕补全是一件很耗耐心的事情但用 DeepSeek API 把“翻译”这步自动化后剩下的主要工作就是术语表维护和质量抽检。建议你从一集开始跑通流程记录消耗和效果再决定要不要扩大到整个系列。如果你手头也有积压的英文 SRT不妨照着这份教程试一次。