讯飞开放平台音频转文字Python实战:从鉴权签名到并发转写
发布时间:2026/9/10 4:17:05 锦皓数字建站

简介面向需要将普通话或英语离线音频快速转为文字的开发者和内容处理人员这份基于讯飞开放平台音频转写API的Python资源提供了一套可直接参考的离线音频转文字实现方案。使用者只需注册讯飞开放平台账号、领取免费时长并创建应用即可按说明接入接口完全适配Python3.7运行环境可用于访谈录音、会议记录、课程录像、网课笔记等离线转写场景。资源压缩包体积仅4KB共包含2个文件一个为核心功能的Python脚本另一个为配套使用说明文本整体结构十分精简既方便快速部署也便于按需二次修改和集成到其他流程中。脚本支持保存完整的整段识别文本同时额外生成按时间分隔、并对说话人进行区分的文本结果方便后期定位片段、整理访谈内容或检索关键信息。目前已有3608人学习/下载整体实操门槛适中适合有一定Python基础、希望借助API实现批量离线音频转文字的使用者参考使用。1. 讯飞开放平台音频转文字python的起点手头有一段录音会议纪要、访谈对话、或者客服电话想把它变成可检索的文字稿。本地搭语音识别模型不是不行但你需要一块还过得去的 GPU、准备训练数据做微调、还要忍受识别速度跟不上音频时长。这时候讯飞开放平台的音频转文字接口就是最省事的一条路只需要 Python 脚本把音频文件按帧丢给 WebSocket等结果回来就能得到带时间戳的文本流。这篇文章记的是我通常处理这类需求的全套思路从创建应用拿鉴权信息开始到写一个能断点续传的 Python 客户端再到并发切分长音频、对照参数调优。标题里的“讯飞开放平台”和“python”是两条主线前者管语音识别的远程服务后者管音频读取、分帧、网络传输和结果解析。适合人群是手上有真实音频文件、想在几小时内打通第一版转写工具的开发者对识别准确率有要求但不想碰模型训练的人也可以照着做。我默认你已经有一个讯飞开放平台的账号没注册的话先去官网完成实名认证这一步大概十分钟。下面所有代码都可以在本地直接跑不依赖特定开发板或云主机。2. 讯飞开放平台应用创建与 Python 依赖准备三把钥匙和鉴权签名2.1 创建应用并获取 APPID、APIKey、APISecret讯飞开放平台的语音转写能力不是把文件上传到网页就行而是通过 API 网关提供实时识别服务所以第一步是创建一个应用拿到三个身份标识APPID、APIKey、APISecret。登录讯飞开放平台控制台在“我的应用”里点击“创建应用”填写应用名称和分类提交后就能看到这几个字符串。注意 APISecret 只在创建时完整展示一次之后只能重置所以拿到就先存到本地配置文件别硬编码进源码。这三把钥匙是调用音频转文字接口的凭证缺一个鉴权都会失败。# config.py 存放讯飞开放平台鉴权信息不要提交到 git APPID your_app_id APIKEY your_api_key APISECRET your_api_secret这段配置是后续所有请求的基础我用单独模块存放是为了方便切换不同应用的配额尤其是当你同时有测试应用和正式应用时改这一份文件就够了。2.2 鉴权签名原理HMAC-SHA256 与 URL 拼接细节讯飞开放平台的实时语音转写 WebSocket 接口需要动态生成一个带签名参数的 URL参数包括当前时间戳、签名有效期和 HMAC-SHA256 加密后的签名串。鉴权逻辑其实不难先从前端鉴权地址拆出 host 和 path然后按格式拼接字符串再用 APISecret 作为密钥加密。import base64 import hashlib import hmac import time from urllib.parse import urlparse, urlencode def build_auth_url(request_url: str, api_key: str, api_secret: str) - str: parsed urlparse(request_url) host parsed.hostname path parsed.path date time.strftime(%a, %d %b %Y %H:%M:%S 0800, time.localtime()) sign_string fhost: {host}\ndate: {date}\nGET {path} HTTP/1.1 digest hmac.new(api_secret.encode(), sign_string.encode(), hashlib.sha256).digest() signature base64.b64encode(digest).decode() auth_header fapi_key{api_key}, algorithmhmac-sha256, headershost date request-line, signature{signature} query urlencode({host: host, date: date, authorization: base64.b64encode(auth_header.encode()).decode()}) return f{request_url}?{query}这里有几个参数需要注意date 必须是 RFC 1123 格式并且要用 UTC8 时区常用变量替换成time.localtime()后会带上本地时区如果你部署的服务器是 UTC 时区生成的时间会偏离标准导致鉴权失败headers 字段固定按host date request-line的顺序拼接最后的 authorization 还需要先拼成一段 json 结构再做一次 base64 编码我代码里直接用了 urlencode 处理等价。2.3 本地依赖安装与最小连接测试我一般用一个独立的虚拟环境来管理讯飞相关的依赖避免污染全局 Python 版本。主要用到websocket-client和pyaudio后者只在需要麦克风实时输入时才安装如果你跟我一样只处理音频文件可以省掉。python3 -m venv xfenv source xfenv/bin/activate pip install websocket-client用websocket-client而不是websockets是因为讯飞接口的握手流程在同步场景下用create_connection更直接而且断线重连时也好写循环。装完依赖后最好先不急着传音频只验证连接能不能建立。import websocket ws_url build_auth_url(wss://iat-api.xfyun.cn/v2/iat, APIKEY, APISECRET) ws websocket.create_connection(ws_url, timeout5) print(连接成功) ws.close()输出“连接成功”说明鉴权签名正确接下来可以进入真正的音频转文字环节。如果这一步抛异常先检查 APISECRET 是否复制完整再检查系统时间是否准确因为签名里的 date 对时间偏差很敏感偏差超过五分钟就会被服务端拒绝。3. 用 Python 调用讯飞开放平台 WebSocket 接口实现音频转文字3.1 识别参数选型音频格式、采样率与讯飞转文字模型的关系讯飞开放平台的实时语音转写服务名为 IAT要求客户端把音频分帧通过 WebSocket 发送服务端流式返回识别结果。音频格式一般支持raw、pcm、wav、mp3和opus但为了识别稳定我通常建议先转成pcm即裸采样数据码率 16k 或 16k 以上单声道。采样率和声道数直接影响识别效果参数在发送首个请求时通过 JSON 传给服务端{ common: { app_id: your_app_id }, business: { language: zh_cn, domain: iat, accent: mandarin, vad_eos: 5000, dwa: wpgs }, data: { status: 0, format: audio/L16;rate16000, encoding: raw } }format里的 rate 要与实际音频采样率完全一致否则识别出来的是乱码encoding填 raw 表示不压缩的裸 PCM 数据。dwa参数设为wpgs后服务端会返回带有开始时间、结束时间和完整句子的动态结果对后续做字幕对齐很有帮助。3.2 发送音频数据与接收识别结果的完整流程连接建立后核心逻辑是一个循环从本地音频文件读取一小块数据通过 WebSocket 发送出去同时接收服务端返回的识别结果。每次发送的数据量没有硬性限制但讯飞官方建议每 40-60ms 发送一次对应 16k 采样率下大约是 1280-1920 字节这样可以模拟实时音频流。下面是一个可直接运行的完整示例读取一个 WAV 文件并实时转写import json import wave import websocket # ws 连接已在前面创建好这里省略鉴权部分 def transcribe_wav(ws, wav_path: str, chunk_size: int 1280): with wave.open(wav_path, rb) as wf: # 校验音频参数是否符合必填要求 if wf.getframerate() ! 16000 or wf.getnchannels() ! 1: raise ValueError(音频必须是16k单声道, 请先转换格式) while True: chunk wf.readframes(chunk_size) if not chunk: break ws.send_binary(chunk) result ws.recv() yield parse_iat_result(result) # 音频发完后发送结束帧 ws.send(json.dumps({data: {status: 2}})) while True: result ws.recv() yield parse_iat_result(result)代码里的chunk_size是帧数而不是字节数readframes返回的是帧数据16k 采样率单声道下一帧两字节所以 1280 帧就是 2560 字节。status2表示数据发送完毕服务端收到后会返回最终识别结果。3.3 解析响应 JSON状态码、文本拼接与动态结果提取讯飞接口返回的数据结构是一个嵌套 JSON核心字段在data.result里。sn是句子编号text是当前片段的识别文本pgs表示处于中间结果还是最终结果rg是词的偏移量列表。def parse_iat_result(raw_message: str) - str: resp json.loads(raw_message) code resp.get(code, 0) if code ! 0: raise RuntimeError(f识别错误: {code}, {resp.get(message)}) data resp.get(data, {}) if data.get(status) 2: return [转写完成] result data.get(result, {}) text for item in result.get(text, []): text item.get(text, ) return textpgs字段有两个值rpl表示这句话被替换过apd表示追加。当收到rpl结果时正确做法是用新的文本替换旧结果中对应sn的句子而不是直接拼接否则你会看到同一句话反复重影。我一般会维护一个last_sn到文本的映射等收到pgsapd再落库。4. 长音频转文字的稳定性分帧上传节奏与并发任务管理4.1 讯飞开放平台实时转写的限制参数不是所有音频都能用同一个姿势跑通。讯飞实时语音转写对单次连接时时长有限制我实际使用下来60 秒以上的音频就要考虑分段或切换方案音频转写 API 与实时 API 区别在于前者需要先上传文件再提交任务但流式接口更适合实时场景所以我这里以流式方案为主。常用限制参数表如下参数推荐值说明采样率16000低于 8k 识别率明显下降声道单声道双声道会混叠导致文字错乱单帧间隔40-60ms模拟实时音频流避免服务端断连VAD 静默时长3000-5000ms服务端判断句子结束的静音阈值单连接时长5 分钟以内超时会强制断开需要重连续传4.2 Python 实现稳定的音频分帧上传循环实践里最常见的错误是一口气把整个文件send出去导致服务端来不及接收而断开。正确的做法是按时间片循环发送并且每次发送后都调用recv读取服务端的状态信息这可以起到流量控制的作用。import time import audioop def upload_loop(ws, pcm_path: str, interval: float 0.05): with open(pcm_path, rb) as f: chunk_size int(16000 * 2 * interval) # 按间隔计算字节数 while True: data f.read(chunk_size) if not data: break ws.send_binary(data) time.sleep(interval) try: ws.settimeout(0.5) while True: msg ws.recv() process_result(json.loads(msg)) except websocket.WebSocketTimeoutException: continue代码里先把chunk_size换算成字节数这样修改interval参数时不用同步改读取逻辑。从文件读取后先休眠再接收返回保证发送速率与音频播放速率基本一致。ws.settimeout(0.5)用来处理服务端可能长时间不返回中间结果的场景避免阻塞发送。4.3 同时转写多个音频的并发机制当你有几十个文件要批量处理逐个串行显然太慢。通常的做法是使用线程池每一条音频单独占一个 WebSocket 连接。from concurrent.futures import ThreadPoolExecutor, as_completed import os def process_one(audio_path: str, output_path: str): ws create_ws_connection() # 每个线程独立的连接 texts [] for partial in transcribe_wav(ws, audio_path): if partial ! [转写完成]: texts.append(partial) with open(output_path, w) as f: f.write(.join(texts)) ws.close() return audio_path files [a.wav, b.wav, c.wav] with ThreadPoolExecutor(max_workers3) as pool: futures [pool.submit(process_one, f, f.replace(.wav, .txt)) for f in files] for future in as_completed(futures): print(f完成: {future.result()})线程数建议限制在 3-5 之间讯飞对并发连接数有配额限制超过会返回 10160 错误所以只开需要的最小线程数而不是盲目开满。5. 排错与调优鉴权失败、识别乱码、静默超时的常见解决路径5.1 鉴权与连接阶段的问题排查鉴权失败一般体现为握手时直接收到401或 WebSocket 连接直接关闭。优先检查三处date的时区是否为 0800这个在本地开发容易忽略因为多数人电脑是北京时间但如果你用 UTC 的云主机跑脚本就会踩坑signature里拼接的request-line是否包含完整的GET /v2/iat HTTP/1.1路径写错一个字符都会失败APPID、APIKey 和 APISecret 是否与创建的应用一致复制时容易混入空格。用trace级别的日志查看实际请求 URL 是个高效手段但别把签名后的 URL 整个打进日志里面包含有效期内的鉴权信息泄露后别人可以借用你的额度。5.2 识别结果乱码、漏字或重复的问题乱码通常不是讯飞服务的问题而是音频参数对不上。我处理过的一个典型场景是录音文件是 48k 采样率直接喂给 16k 的format参数识别出来的文字全是谐音和错字。解决思路是先离线用 FFmpeg 统一转格式ffmpeg -i input.wav -ar 16000 -ac 1 -f s16le -acodec pcm_s16le output.pcm-ar 16000强制重采样-ac 1转为单声道-f s16le输出 PCM。转换后用audioop或librosa检测实际采样率再发起请求。漏字和重复大多与vad_eos有关设置太短会让服务端把一个长句截成多段导致看起来丢字设置太长又会拖慢句尾返回速度一般 3000-5000ms 比较平衡。5.3 长音频中途连接断开与静默超时的处理运行到一半连接断开是流式接口常见问题服务器可能因为长时间没有收到客户端数据或网络波动主动断开。可靠方案是记录已经发送的音频字节位置重连后从断点继续发送而不是重新传整个文件。def with_retry(audio_path: str, max_retries: int 3): offset 0 for attempt in range(max_retries): try: ws create_ws_connection() offset transmit_from(ws, audio_path, start_byteoffset) ws.close() break except websocket.WebSocketConnectionClosedException: print(f连接断开, 第{attempt 1}次重连) time.sleep(2 ** attempt)transmit_from函数里用f.seek(start_byte)跳到上次读取的位置继续发送重连后需要重新发送common和business参数因为服务端不保存上一次会话的上下文。断点续传的代价是重连后的第一段识别文本会缺少上文对单句级别的转写影响不大。6. 用讯飞开放平台做低成本验证先截图出短语再跑完整转写最后分享一个我实际在用的组合技巧不先把整段音频送进讯飞而是先从原始音频里切出 5-10 秒的片段做快速验证确认参数和返回结果符合预期再跑全量转写。这个习惯帮我省了很多次整段跑完发现格式不对的时间。切片段和调用可以放到同一个 Python 脚本里用subprocess调 FFmpeg 切分import subprocess def probe_clip(src: str, start: float, duration: float, out: str): cmd [ ffmpeg, -ss, str(start), -t, str(duration), -i, src, -ar, 16000, -ac, 1, out ] subprocess.run(cmd, checkTrue)验证片段时重点关注三件事时间戳是否合理、数字和英文的识别准确率、多说话人场景下断句位置。如果验证片段表现好全量转写通常不会出大问题。音频转文字不只是把音频送出去收结果Python 脚本里把分帧、鉴权、错误重连这些细节处理好讯飞开放平台才能发挥出接近实时字幕的效果。如果后续想进一步降低高频调用成本可以在本地用 VAD 做一次静音切除把音频里的大段停顿去掉后再送入接口这样按音频时长计费时能省下不少。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。