🚀

用 Python 搭本地语音转文字笔记:开会录音秒变文字,隐私全在本地

本地运行语音识别,录音文件自动转文字,支持中英文混合识别。不上传云端,隐私安全,开箱即用。

👁️ - 次阅读

项目背景

你是不是也这样:开会时狂录音,会后翻到崩溃——音频两小时,整理要三小时。用讯飞、百度这些在线工具转写速度快,但有个问题:会议内容全上传到对方服务器,敏感信息根本不放心。

本地跑语音识别,文件不出门,转录完直接删录音,安全感拉满。

这个项目的目标是:一个 Python 脚本,输入音频文件,输出文字记录。支持中文、英文、中英文混合,离线运行,零依赖云端。

技术选型

组件选型理由
语言Python 3.10+语音生态成熟
语音识别faster-whisper基于 Whisper,速度快3-4倍,内存占用低
音频预处理ffmpeg / librosa格式转换、降噪
输出格式Markdown + JSON结构清晰,方便后续处理
批量处理pathlib + multiprocessing多文件并发转写

不需要数据库,不需要后端服务。一个脚本跑完就完事,适合个人和小型团队。

实现步骤

第一步:安装依赖

pip install faster-whisper librosa ffmpeg-python

faster-whisper 是 OpenAI Whisper 的高效实现,用的是 CTranslate2 引擎,CPU 上也能跑出不错的速度。librosa 负责音频格式转换和降噪。

第二步:创建项目结构

mkdir -p voice_to_text/{input,output,temp}
cd voice_to_text
touch transcribe.py config.yaml

把音频文件丢进 input 文件夹,转写结果自动输出到 output。

第三步:写转写脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import json
import yaml
import librosa
from pathlib import Path
from datetime import datetime
from faster_whisper import WhisperModel

# 加载配置
with open("config.yaml") as f:
    cfg = yaml.safe_load(f)

INPUT_DIR = Path(cfg["input_dir"])
OUTPUT_DIR = Path(cfg["output_dir"])
TEMP_DIR = Path(cfg["temp_dir"])
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
TEMP_DIR.mkdir(parents=True, exist_ok=True)

# 初始化模型(base 模型约140MB,medium约450MB)
MODEL_SIZE = cfg.get("model_size", "base")
DEVICE = cfg.get("device", "cpu")
compute_type = cfg.get("compute_type", "int8")

print(f"🔄 加载模型: {MODEL_SIZE} ({DEVICE}/{compute_type})")
model = WhisperModel(MODEL_SIZE, device=DEVICE, compute_type=compute_type)
print("✅ 模型加载完成\n")


def normalize_audio(audio_path: Path) -> Path:
    """将音频转为单声道 16kHz WAV,降低识别难度"""
    temp_path = TEMP_DIR / f"{audio_path.stem}_norm.wav"
    os.system(
        f"ffmpeg -y -i '{audio_path}' "
        "-ar 16000 -ac 1 -codec:a pcm_s16le "
        f"'{temp_path}' 2>/dev/null"
    )
    return temp_path


def transcribe(audio_path: Path) -> dict:
    """转写单个音频文件"""
    print(f"🎙️ 处理: {audio_path.name}")

    # 音频预处理
    norm_path = normalize_audio(audio_path)

    # faster-whisper 分段识别,自带标点
    segments, info = model.transcribe(
        str(norm_path),
        language=cfg.get("language", "zh"),
        beam_size=5,
        vad_filter=True,  # 自动过滤静音段
    )

    text_parts = []
    for seg in segments:
        text_parts.append(seg.text.strip())
        # 可选:记录时间戳用于后续校对
        # print(f"[{seg.start:.2f}s -> {seg.end:.2f}s] {seg.text}")

    full_text = "\n".join(text_parts)

    # 清理临时文件
    os.remove(norm_path)

    return {
        "filename": audio_path.name,
        "language": info.language,
        "language_probability": info.language_probability,
        "duration": info.duration,
        "text": full_text,
        "timestamp": datetime.now().isoformat(),
    }


def save_result(result: dict):
    """保存为 Markdown 和 JSON"""
    stem = result["filename"].rsplit(".", 1)[0]

    # Markdown 格式
    md_path = OUTPUT_DIR / f"{stem}.md"
    md_content = f"""# 语音转文字记录

**文件**: {result['filename']}
**时长**: {result['duration']:.1f} 秒
**识别语言**: {result['language']} (置信度: {result['language_probability']:.2f})
**转录时间**: {result['timestamp']}

---

{result['text']}
"""
    md_path.write_text(md_content, encoding="utf-8")

    # JSON 格式(方便程序处理)
    json_path = OUTPUT_DIR / f"{stem}.json"
    json_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")

    print(f"  ✅ 已保存: {md_path.name}")


def main():
    audio_extensions = {".wav", ".mp3", ".m4a", ".flac", ".aac", ".ogg"}
    audio_files = [
        f for f in INPUT_DIR.iterdir()
        if f.suffix.lower() in audio_extensions
    ]

    if not audio_files:
        print("⚠️  input/ 目录下没有找到音频文件")
        return

    print(f"📁 发现 {len(audio_files)} 个音频文件\n")

    for audio_path in audio_files:
        try:
            result = transcribe(audio_path)
            save_result(result)
        except Exception as e:
            print(f"  ❌ {audio_path.name}: 处理失败 - {e}")

    print("\n🎉 全部完成!")


if __name__ == "__main__":
    main()

第四步:配置文件 config.yaml

# 目录配置
input_dir: "input"
output_dir: "output"
temp_dir: "temp"

# 模型配置
model_size: "base"        # 可选: tiny / base / small / medium / large
device: "cpu"            # 有GPU可改为 "cuda"
compute_type: "int8"     # int8 更快更省内存,float32 精度更高

# 识别语言
language: "zh"           # 自动检测可设为 null,或固定为 "zh" / "en"

第五步:运行

python transcribe.py

输出示例:

📁 发现 2 个音频文件

🎙️ 处理: meeting_20260805.mp3
  ✅ 已保存: meeting_20260805.md
🎙️ 处理: interview_zhang.mp3
  ✅ 已保存: interview_zhang.md

🎉 全部完成!

生成的 Markdown 文件里包含完整文字内容和元数据,可以直接用来做会议纪要。

运行效果

转写后的 Markdown 长这样:

# 语音转文字记录

**文件**: meeting_20260805.mp3
**时长**: 1823.5 秒
**识别语言**: zh (置信度: 0.97)
**转录时间**: 2026-08-08T10:15:32.441023

---

大家好,今天我们开一个产品评审会。
首先请张经理汇报一下 Q3 的数据表现。
...

JSON 文件包含结构化数据,适合后续做统计或接入其他工具:

{
  "filename": "meeting_20260805.mp3",
  "language": "zh",
  "language_probability": 0.97,
  "duration": 1823.5,
  "text": "大家好,今天我们开一个产品评审会。...",
  "timestamp": "2026-08-08T10:15:32.441023"
}

优化方向

  1. 多 speaker 分离:接入 PyAnnote 做说话人分离,区分不同发言人的内容,自动生成带角色标注的会议纪要

  2. 关键词高亮:识别结果里自动标出会议主题词、待办事项、决策项,一键生成行动清单

  3. GPU 加速:有 NVIDIA 显卡的话把 device 改成 cuda,compute_type 改成 float16,速度提升明显

  4. 实时转写:接入麦克风输入,边说边出文字,适合线上会议记录

  5. 摘要生成:把转写文本喂给本地 LLM(比如用 Ollama 跑 Llama 3),自动生成会议摘要和行动项

  6. 集成飞书/钉钉:转写完成后自动把文字推送到群组,同事可以直接在文档里编辑

  7. 多语言混合场景:中英文混杂的会议(比如技术评审),把 language 设为 null 让模型自动检测每段语言

核心思路:先让模型跑起来,再把每一步拆细。语音识别这事,模型选对了,剩下的都是工程优化。

你的电脑有 GPU 吗?没 GPU 用 base 模型也能跑,就是慢一点。试试把一段录音丢进去,看看转写质量怎么样?

这篇文章对你有帮助吗?