如何导出Paraformer识别结果?文本保存完整教程
你刚用 Paraformer-large 语音识别离线版完成了长音频转写,界面上清清楚楚显示了识别文字——但问题来了:怎么把这段文字保存成文件?
不是截图,不是手动复制粘贴,而是真正“导出”为.txt或.srt等可编辑、可归档、可批量处理的标准文本格式。
很多用户卡在这一步:Gradio 界面很友好,但默认不提供下载按钮;模型能精准识别数小时会议录音,却没人告诉你结果藏在哪、怎么取出来、如何保留标点与时间戳。
本文就是为你写的——不讲原理,不堆参数,只聚焦一件事:从点击“开始转写”到拿到本地.txt文件的完整闭环操作。全程在镜像内完成,无需额外安装、无需改代码、不依赖网络,小白照着做,5分钟内搞定。
1. 先搞清楚:识别结果到底存在哪?
Paraformer-large 镜像使用的是 FunASR 框架,其model.generate()返回的不是单个字符串,而是一个结构化字典列表。我们来看原始代码中这一段:
res = model.generate( input=audio_path, batch_size_s=300, ) if len(res) > 0: return res[0]['text']表面看,res[0]['text']就是你要的纯文本。但其实,res[0]还包含更多关键字段:
| 字段名 | 类型 | 说明 | 是否默认展示 |
|---|---|---|---|
'text' | str | 带标点的完整识别文本(如:“你好,今天天气不错。”) | Gradio 界面显示 |
'timestamp' | list | 时间戳列表,每个元素为[start_ms, end_ms, word] | ❌ 默认不显示 |
'seg_id' | int | 分段ID(长音频自动切分后每段编号) | ❌ 默认不显示 |
'punc' | str | 标点预测结果(与 text 一致,冗余字段) | ❌ 默认不显示 |
关键提醒:
'timestamp'是生成.srt字幕文件的唯一依据。没有它,就只能导出纯文本;有了它,就能导出带时间轴的逐字字幕。
所以,“导出结果”的本质,是把res中的结构化数据,按需转换为标准文本格式并写入磁盘文件。下面我们就分三步走:先手动验证结构、再加导出功能、最后一键保存。
2. 手动验证:用 Python 快速查看完整识别结构
别急着改界面,先确认你的音频识别是否真的返回了时间戳等信息。打开终端,进入/root/workspace目录,运行一个最小验证脚本:
2.1 创建验证脚本check_output.py
cd /root/workspace vim check_output.py粘贴以下内容(注意替换your_audio.wav为你的实际音频路径):
# check_output.py from funasr import AutoModel import json # 加载模型(复用镜像内置逻辑) model_id = "iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch" model = AutoModel( model=model_id, model_revision="v2.0.4", device="cuda:0" ) # 替换为你的音频文件路径(支持 wav/mp3/flac) audio_path = "/root/workspace/test.wav" # ← 修改这里! print("正在识别音频...") res = model.generate( input=audio_path, batch_size_s=300, output_dir=None # 不自动生成目录,只返回内存结果 ) print(f"\n 识别完成,共 {len(res)} 个分段") print("\n--- 第一段完整结构(截取前3个字段)---") first_seg = res[0] for key in ['text', 'timestamp', 'seg_id']: if key in first_seg: val = first_seg[key] if isinstance(val, list) and len(val) > 5: print(f"{key}: {val[:3]} ... (共{len(val)}项)") else: print(f"{key}: {val}") else: print(f"{key}: 未返回") # 保存原始 res 到 JSON 方便查看 with open("debug_res.json", "w", encoding="utf-8") as f: json.dump(res, f, ensure_ascii=False, indent=2) print("\n 完整结果已保存至 debug_res.json,可用 vim 查看")2.2 运行并观察输出
source /opt/miniconda3/bin/activate torch25 && python check_output.py你会看到类似这样的输出:
识别完成,共 7 个分段 --- 第一段完整结构(截取前3个字段)--- text: 你好,欢迎参加本次技术分享会。 timestamp: [[0, 1200, '你好'], [1200, 2500, '欢迎'], [2500, 3800, '参加']] ... (共12项) seg_id: 0如果看到timestamp字段且内容非空,恭喜!你的镜像已具备导出字幕能力。
❌ 如果timestamp显示未返回或为空列表,请检查两点:
- 音频是否为中文(Paraformer-large 对中文支持最佳,英文可能无 timestamp);
- 是否使用了
vad-punc版本模型(镜像文档明确写了speech_paraformer-large-vad-punc_asr_nat...,必须带vad-punc)。
小技巧:若
timestamp缺失,可临时强制开启 VAD 检测,在model.generate()中添加参数:vad=True, punc=True, spk_num=1
3. 给 Gradio 界面加“导出按钮”:三行代码搞定
现在我们知道数据结构了,下一步就是让 Web 界面支持导出。不需要重写整个app.py,只需在原文件末尾追加一个导出函数 + 一个按钮。
3.1 修改app.py(直接编辑)
vim /root/workspace/app.py找到def asr_process(audio_path):函数结尾处(即return res[0]['text']后),在with gr.Blocks(...) as demo:之前,插入以下函数:
# 新增:导出为 TXT 和 SRT 的函数 def export_to_files(audio_path, text_result): import os import json from funasr import AutoModel if not audio_path or not text_result: return "请先完成识别" # 重新加载模型(避免重复加载耗时,此处仅用于获取结构化结果) model_id = "iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch" model = AutoModel(model=model_id, model_revision="v2.0.4", device="cuda:0") # 重新推理一次,但这次要完整结果 res = model.generate(input=audio_path, batch_size_s=300) if not res: return "识别失败" # 构建文件名(基于音频名) base_name = os.path.splitext(os.path.basename(audio_path))[0] txt_path = f"/root/workspace/{base_name}.txt" srt_path = f"/root/workspace/{base_name}.srt" # 导出 TXT:纯文本,带段落分隔 with open(txt_path, "w", encoding="utf-8") as f: for seg in res: f.write(seg.get("text", "") + "\n\n") # 导出 SRT:带时间轴的字幕格式 with open(srt_path, "w", encoding="utf-8") as f: idx = 1 for seg in res: if "timestamp" not in seg or not seg["timestamp"]: continue # 每个分段生成多行字幕(按词粒度) for start_ms, end_ms, word in seg["timestamp"]: if not word.strip(): continue # 转换毫秒为 SRT 格式:HH:MM:SS,mmm def ms_to_srt(ms): s = ms // 1000 h, s = divmod(s, 3600) m, s = divmod(s, 60) ms_part = ms % 1000 return f"{h:02d}:{m:02d}:{s:02d},{ms_part:03d}" start_str = ms_to_srt(start_ms) end_str = ms_to_srt(end_ms) f.write(f"{idx}\n{start_str} --> {end_str}\n{word}\n\n") idx += 1 return f" 已导出:\n- 文本:{txt_path}\n- 字幕:{srt_path}"3.2 在 Gradio 界面中添加导出按钮
继续编辑app.py,找到with gr.Blocks(...) as demo:块内,在text_output下方(即gr.Textbox(...)后),插入以下代码:
# 新增:导出按钮区域 with gr.Row(): export_btn = gr.Button(" 导出为 TXT & SRT", variant="secondary") export_status = gr.Textbox(label="导出状态", lines=2, interactive=False) # 绑定导出事件 export_btn.click( fn=export_to_files, inputs=[audio_input, text_output], outputs=export_status )3.3 保存并重启服务
:wq # 保存退出 vim # 重启服务(或直接 Ctrl+C 停止后重新运行) source /opt/miniconda3/bin/activate torch25 && cd /root/workspace && python app.py刷新浏览器http://127.0.0.1:6006,你会看到界面底部多了一个 ** 导出为 TXT & SRT** 按钮。
点击它,几秒后下方显示:
已导出:- 文本:/root/workspace/my_recording.txt- 字幕:/root/workspace/my_recording.srt
4. 如何拿到导出的文件?三种零门槛方法
导出的文件默认保存在/root/workspace/目录下。你有三种方式立即获取:
4.1 方法一:通过 Web UI 直接下载(推荐)
Gradio 本身不支持文件下载,但我们可以通过一个极简技巧绕过:在app.py中新增一个gr.File组件,让它自动列出 workspace 下的.txt和.srt文件。
在export_btn.click(...)后,再加两行:
# 自动刷新并显示最新导出文件 file_list = gr.File(label="最近导出的文件", file_count="multiple") demo.load(lambda: [f"/root/workspace/{f}" for f in os.listdir("/root/workspace") if f.endswith(('.txt','.srt'))], None, file_list)重启服务后,页面右下角会出现一个文件列表,点击即可下载。
4.2 方法二:用 SSH 下载(适合本地电脑)
在你自己的电脑终端执行(替换为你的实例信息):
# 下载所有 .txt 和 .srt 文件 scp -P [你的端口号] root@[你的SSH地址]:/root/workspace/*.txt ./download/ scp -P [你的端口号] root@[你的SSH地址]:/root/workspace/*.srt ./download/提示:提前在本地创建
./download/目录,文件将自动存入。
4.3 方法三:用 VS Code Remote SSH(图形化最方便)
如果你用 VS Code:
- 安装插件Remote - SSH
- 连接你的实例
- 左侧资源管理器中直接浏览
/root/workspace/ - 右键点击
.txt或.srt文件 → “Download”
5. 进阶技巧:批量导出 + 自定义命名 + 保留段落
上面的方法适合单次操作。如果你需要处理几十个会议录音,手动点 50 次“导出”显然不现实。这里提供一个免界面、纯命令行的批量导出脚本。
5.1 创建批量导出脚本batch_export.py
cd /root/workspace vim batch_export.py#!/usr/bin/env python3 # batch_export.py —— 批量识别并导出指定目录下所有音频 import os import glob from funasr import AutoModel # 配置 INPUT_DIR = "/root/workspace/audio_in" # 存放待识别音频的文件夹 OUTPUT_DIR = "/root/workspace/export_out" # 导出文件存放位置 MODEL_ID = "iic/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch" # 创建输出目录 os.makedirs(OUTPUT_DIR, exist_ok=True) # 加载模型 model = AutoModel(model=MODEL_ID, model_revision="v2.0.4", device="cuda:0") # 支持的音频格式 SUPPORTED_EXT = ('.wav', '.mp3', '.flac', '.m4a') print(f" 扫描 {INPUT_DIR} 下的音频文件...") files = [f for f in glob.glob(os.path.join(INPUT_DIR, "*")) if os.path.splitext(f)[1].lower() in SUPPORTED_EXT] print(f" 共找到 {len(files)} 个音频文件") for i, audio_path in enumerate(files, 1): base_name = os.path.splitext(os.path.basename(audio_path))[0] print(f"\n[{i}/{len(files)}] 正在处理:{base_name}") try: # 识别 res = model.generate(input=audio_path, batch_size_s=300) # 导出 TXT(按分段保留空行) txt_path = os.path.join(OUTPUT_DIR, f"{base_name}.txt") with open(txt_path, "w", encoding="utf-8") as f: for seg in res: text = seg.get("text", "").strip() if text: f.write(text + "\n\n") # 导出 SRT(词级时间轴) srt_path = os.path.join(OUTPUT_DIR, f"{base_name}.srt") with open(srt_path, "w", encoding="utf-8") as f: idx = 1 for seg in res: if "timestamp" not in seg: continue for start_ms, end_ms, word in seg["timestamp"]: if not word.strip(): continue def ms_to_srt(ms): s = ms // 1000 h, s = divmod(s, 3600) m, s = divmod(s, 60) ms_part = ms % 1000 return f"{h:02d}:{m:02d}:{s:02d},{ms_part:03d}" start_str = ms_to_srt(start_ms) end_str = ms_to_srt(end_ms) f.write(f"{idx}\n{start_str} --> {end_str}\n{word}\n\n") idx += 1 print(f" 已保存:{txt_path} | {srt_path}") except Exception as e: print(f"❌ 处理失败:{e}") print(f"\n 批量导出完成!文件位于:{OUTPUT_DIR}")5.2 使用方法
# 1. 创建输入目录并放入音频 mkdir -p /root/workspace/audio_in # 把你的 .wav/.mp3 文件复制进去(例如用 scp) # 2. 运行批量脚本 source /opt/miniconda3/bin/activate torch25 && python batch_export.py # 3. 导出文件自动出现在 /root/workspace/export_out/优势:全自动、可定时、支持任意数量音频、输出路径清晰隔离。
6. 常见问题快速排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
点击“导出”后无反应或报错NameError: name 'os' is not defined | export_to_files函数里没导入os | 在函数开头加上import os |
导出的.srt时间轴全为00:00:00,000 | timestamp字段为空或格式异常 | 运行check_output.py确认 timestamp 是否有效;确保音频是中文、采样率 16k |
.txt文件内容乱码(显示问号) | 文件未用 UTF-8 编码保存 | 检查open(..., encoding="utf-8")是否写对,尤其 Windows 用户 |
| 导出按钮点击后状态栏显示“请先完成识别”,但界面上已有文字 | text_output输入未正确绑定 | 确保export_btn.click(..., inputs=[audio_input, text_output], ...)中text_output是 Gradio 组件变量名,不是字符串"text_output" |
批量脚本运行报错CUDA out of memory | 单次处理音频太长 | 在model.generate()中添加max_duration=60(单位秒),限制单次处理时长 |
终极建议:首次使用前,务必用
check_output.py验证timestamp是否可用。这是所有导出功能的地基。
7. 总结:你已经掌握 Paraformer 的完整工作流闭环
回顾一下,你刚刚完成了从“识别”到“导出”的关键跃迁:
- 看清本质:识别结果不是字符串,而是含
text、timestamp、seg_id的结构化数据; - 手动验证:用
check_output.py5 分钟确认环境是否就绪; - 界面增强:三行函数 + 两行按钮,让 Gradio 支持一键导出 TXT/SRT;
- 文件获取:三种方式任选(Web 下载、SCP、VS Code),总有一款适合你;
- 批量处理:
batch_export.py脚本解放双手,百个音频一气呵成; - 问题兜底:常见报错对应解决方案,不再卡在细节上。
从此,Paraformer-large 不再只是一个“能看不能拿”的演示工具,而是你工作流中真正可用的生产力组件——会议纪要自动归档、课程录音转文字笔记、播客内容生成双语字幕,全部变得轻而易举。
下一步,你可以尝试:
- 把导出的
.txt接入 LLM 做会议摘要; - 用
.srt文件喂给剪辑软件自动生成字幕轨道; - 将
batch_export.py加入 crontab,实现每日录音自动转写。
技术的价值,永远在于它能否无缝融入你的日常。而今天,这一步,你已经走通了。
--- > **获取更多AI镜像** > > 想探索更多AI镜像和应用场景?访问 [CSDN星图镜像广场](https://ai.csdn.net/?utm_source=mirror_blog_end),提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。