跳转至

下載 YouTube Shorts 轉換成 MP3,進行語音辨識轉文本,v1

介紹

```bash showLineNumbers 作業環境:Windows + WSL 服務管理: 主要應用:ytdlp + ffmpeg + openai-whisper 功能重點: a. 下載 YouTube Shorts 並轉換成 MP3 b. 語音辨識 (Speech-to-Text)

## 安裝 

需先安裝:

```bash
pip install openai-whisper
sudo apt install ffmpeg yt-dlp -y

1. openai-whisper

Whisper 會在第一次載入時下載語音模型:

tiny → 約 74 MB base → 約 142 MB small → 約 466 MB medium → 約 1.5 GB large → 約 1.9 GB

openai-whisper 本身 Python 套件不大,只有幾 MB,但它會依賴 PyTorch(torch、torchaudio、torchvision),這些套件通常就有 1GB ~ 2GB 以上

本地版這個會安裝超級久,如果只是為了短影片語音辨識可以考慮雲端版

pip install -U openai-whisper
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

目前 WSL 的 Python 環境被 Ubuntu “受管理”(externally managed)”。

這種情況常發生在 Ubuntu 22.04/23.10 以上自帶 Python 3.12, 系統為了安全,不允許你直接用 pip install 安裝到系統 Python, 會建議你用 apt install python3-package

2. 用 Python 虛擬環境 (推薦) 安裝必要的插件

這是最乾淨、最安全的方法,不會破壞系統 Python:

先測試:建立虛擬環境

python3 -m venv venv

在 WSL (Ubuntu) 上,建立虛擬環境需要 venv 模組 如果出現錯誤像 No module named venv,就需要安裝:

sudo apt install python3-venv -y
# 進入你要放專案的資料夾
cd ~/myproject

# 建立虛擬環境
python3 -m venv venv

# 啟動虛擬環境
source venv/bin/activate

# 升級 pip
pip install --upgrade pip

# 安裝 openai-whisper 
pip install openai-whisper


# 安裝 ffmpeg (系統套件)
sudo apt install ffmpeg -y

腳本

1. Python 腳本說明

  1. 從 YouTube Shorts 下載影片(yt-dlp)
  2. 自動 user-agent 避免 403
  3. 自動 fallback mp4 → mp3
  4. 自動抓取影片標題
  5. 使用 Whisper 本地模型轉成文字
  6. 支援 --model 調整 Whisper 模型
  7. 如果沒傳 output_folder,直接用影片標題命名 .mp3 和 .txt,存在當前路徑

2. transcribe.py 腳本內容

#!/usr/bin/env python3
import subprocess
import whisper
import sys
import os
import shlex
import argparse
import glob

# ---------------------------
# 參數設定
# ---------------------------
parser = argparse.ArgumentParser(description="下載 YouTube Shorts,轉 mp3 並用 Whisper 轉文字")
parser.add_argument("url", help="YouTube Shorts 影片 URL")
parser.add_argument("output_folder", nargs="?", default=None, help="輸出資料夾(可選)")
parser.add_argument("--model", default="base", help="Whisper 模型 (tiny/base/small/medium/large)")
parser.add_argument("--cookies", default=None, help="YouTube cookies.txt 檔案(可選)")
args = parser.parse_args()

yt_url = args.url
output_folder = args.output_folder
model_name = args.model
cookies_file = args.cookies

# ---------------------------
# 輸出路徑設定
# ---------------------------
if output_folder:
    os.makedirs(output_folder, exist_ok=True)
    out_template = os.path.join(output_folder, "%(title)s.%(ext)s")
else:
    out_template = "%(title)s.%(ext)s"  # 當前目錄

# ---------------------------
# yt-dlp 下載函式
# ---------------------------
def download_audio(url):
    # 嘗試直接下載 mp3
    cmd = f'yt-dlp -x --audio-format mp3 -o "{out_template}" --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"'
    if cookies_file:
        cmd += f' --cookies {shlex.quote(cookies_file)}'
    cmd += f' {shlex.quote(url)}'

    try:
        subprocess.run(cmd, shell=True, check=True)
    except subprocess.CalledProcessError:
        print("直接下載 mp3 失敗,嘗試先下載 mp4 再轉 mp3...")
        # 下載 mp4
        mp4_template = os.path.splitext(out_template)[0] + ".mp4"
        cmd_mp4 = f'yt-dlp -f bestvideo+bestaudio -o "{mp4_template}" --merge-output-format mp4 --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"'
        if cookies_file:
            cmd_mp4 += f' --cookies {shlex.quote(cookies_file)}'
        subprocess.run(cmd_mp4, shell=True, check=True)
        # 轉 mp3
        mp3_file = os.path.splitext(mp4_template)[0] + ".mp3"
        subprocess.run(f'ffmpeg -y -i "{mp4_template}" "{mp3_file}"', shell=True, check=True)
        return mp3_file

    # 找最新 mp3
    search_dir = output_folder if output_folder else "."
    mp3_files = glob.glob(os.path.join(search_dir, "*.mp3"))
    if not mp3_files:
        raise FileNotFoundError("未找到 mp3 檔案")
    latest_mp3 = max(mp3_files, key=os.path.getmtime)
    return latest_mp3

# ---------------------------
# 執行下載
# ---------------------------
try:
    mp3_file = download_audio(yt_url)
except Exception as e:
    print("下載失敗:", e)
    sys.exit(1)

# ---------------------------
# Whisper 語音辨識
# ---------------------------
model = whisper.load_model(model_name)
result = model.transcribe(mp3_file)

# ---------------------------
# 輸出文字檔(同名 mp3 + .txt)
# ---------------------------
txt_file = os.path.splitext(mp3_file)[0] + ".txt"
with open(txt_file, "w", encoding="utf-8") as f:
    f.write(result["text"])

print(f"轉寫完成: {txt_file}")

3. 使用範例

指定模型和資料夾:

python transcribe.py "https://www.youtube.com/shorts/XXXXXXXXXXX" "/home/user/output" --model tiny

指定模型,但不給資料夾 → 會存到當前路徑,檔名用影片標題:

python transcribe.py "https://www.youtube.com/shorts/XXXXXXXXXXX" --model medium

不指定模型 → 預設 base:

python transcribe.py "https://www.youtube.com/shorts/XXXXXXXXXXX"

python3 transcribe.py "https://www.youtube.com/shorts/sKntYRHWMN0" --model tiny

其他狀況

1. yt-dlp 在 window 本地執行 ok 但切到 WSL 環境就不行?

yt-dlp -x --audio-format mp3 --cookies cookies.txt -o "%(title)s.%(ext)s"  https://www.youtube.com/shorts/sKntYRHWMN0

本地可以正常下載 shorts

但WSL 加 user-agent 和 使用 cookies.txt(登入 YouTube)

還是 ERROR: unable to download video data: HTTP Error 403: Forbidden

WSL 的網路環境與 Windows 不完全一樣,導致 yt-dlp 在 WSL 下載 YouTube Shorts 可能會被拒絕或 403。

# 測試網路
curl -I https://www.youtube.com/shorts/XXXXXXXXXXX
# HTTP/2 200 表示 WSL 網路其實是暢通的

2. ytdlp 403/400

即使能連線,yt-dlp 下載時仍可能被拒絕,原因主要是 YouTube 動態檢查下載請求:

User-Agent 或 headers 不對

yt-dlp 在 WSL 執行,可能沒有模擬瀏覽器 headers,YouTube 會判斷為「非正常瀏覽器」,直接拒絕。

Shorts API 改版

YouTube 不定期更新 Shorts 播放器/簽名方式

yt-dlp 需要最新版才能抓到有效下載 URL

Cookie / 登入限制

若影片受地區或年齡限制,沒登入 cookie 就會 403

Windows 可能自動有 cookie 或瀏覽器 session,WSL 沒有

3. 比較本地與 WSL 的 yt-dlp 版本

本地 yt-dlp 版本

yt-dlp --version
2025.03.31

WSL yt-dlp 版本

yt-dlp --version
2024.04.09

可以看出WSL 版本是比較舊的,可能無法抓到有效下載 URL

4. 更新 yt-dlp 方式

用 apt 更新(可能還是不最新)

yt-dlp -U
sudo apt install yt-dlp -y
直接用官方腳本安裝最新 yt-dlp(推薦)

那得先移除 apt 版:

sudo apt remove yt-dlp -y

建議 先退出 venv 再操作系統全域的安裝

# 退出 venv
deactivate

# 下載官方最新 yt-dlp
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
sudo chmod a+rx /usr/local/bin/yt-dlp

# 確認版本
yt-dlp --version   # 應該顯示最新版本

# 回到你的 venv
source /path/to/venv/bin/activate

vnev 會優先使用虛擬環境內的 PATH

如果在 venv 裡用 sudo curl ... -o /usr/local/bin/yt-dlp,雖然檔案放到系統路徑 /usr/local/bin,但是 venv 的 PATH 可能把 /usr/local/bin 放在後面或被 venv 覆蓋,導致 Python 找不到 yt-dlp 或仍然用舊版。

系統安裝是全域的

更新 /usr/local/bin/yt-dlp 是針對整個系統,而 venv 只是 Python 套件隔離。

退出 venv 安裝完後,確認全域可執行檔可用,再回 venv 呼叫即可。

# 在 WSL 重新執行 yt-dlp
yt-dlp -x --audio-format mp3 --cookies cookies.txt -o "%(title)s.%(ext)s"  https://www.youtube.com/shorts/sKntYRHWMN0

結果

雖然順利進行了語音辨識轉文本,但文本的精準度一般

python3 transcribe.py "https://www.youtube.com/shorts/sKntYRHWMN0" --model tiny
[youtube] Extracting URL: https://www.youtube.com/shorts/sKntYRHWMN0
[youtube] sKntYRHWMN0: Downloading webpage
[youtube] sKntYRHWMN0: Downloading tv simply player API JSON
[youtube] sKntYRHWMN0: Downloading tv client config
[youtube] sKntYRHWMN0: Downloading tv player API JSON
[info] sKntYRHWMN0: Downloading 1 format(s): 251
[download] 那些只有前半句的名句!  #cheems小剧场 mp4.mp3 has already been downloaded
[ExtractAudio] Not converting audio 那些只有前半句的名句!  #cheems小剧场 mp4.mp3; file is already in target format mp3
100%|█████████████████████████████████████| 72.1M/72.1M [00:28<00:00, 2.69MiB/s]
/mnt/d/new-start-2025-09/ytdlp/download/whisper_test/venv/lib/python3.12/site-packages/whisper/transcribe.py:132: UserWarning: FP16 is not supported on CPU; using FP32 instead
  warnings.warn("FP16 is not supported on CPU; using FP32 instead")
轉寫完成: ./1.txt