WhisperとClaude APIとPythonでYouTube動画の自動要約システムを構築する

Artificial Intelligence tutorial - IT technology blog
Artificial Intelligence tutorial - IT technology blog

先週、AIに関するYouTubeプレイリスト(15本)を視聴してメモを取るのに3時間近くかかりました。乱雑なメモを見返して、ふと思いました。なぜこれをPythonスクリプトで自動化しないのか?2晩かけて、動画を自動的に文字起こしし、内容を要約し、完全なブログ記事まで書いてくれるシステムを作りました。

この記事では、環境構築から最初の結果が出るまで、実際にやったことをそのまま共有します。

5分で動かす — まず試してみよう

Python 3.9+とAnthropicのAPIキーが必要です。まず動かしてみて、理解は後から。

ステップ1:ライブラリのインストール

pip install openai-whisper yt-dlp anthropic
  • openai-whisper:OpenAIの音声認識モデル。完全ローカルで動作し、無料で使えます
  • yt-dlp:YouTubeから音声をダウンロードするツール(youtube-dlの積極的にメンテされているフォーク)
  • anthropic:Claude APIを呼び出すためのPython SDK

ステップ2:動作確認用の完全スクリプト

import subprocess
import whisper
import anthropic
import sys

def download_audio(url: str, output: str = "audio") -> str:
    subprocess.run([
        "yt-dlp", "-x", "--audio-format", "mp3",
        "-o", f"{output}.%(ext)s", url
    ], check=True)
    return f"{output}.mp3"

def transcribe(audio_path: str) -> str:
    model = whisper.load_model("base")  # base: 高速; small/medium: より精度が高い
    result = model.transcribe(audio_path)
    return result["text"]

def summarize_to_article(transcript: str, topic: str) -> str:
    client = anthropic.Anthropic()  # 環境変数からANTHROPIC_API_KEYを読み込む
    message = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=2048,
        messages=[{
            "role": "user",
            "content": f"""YouTubeの動画「{topic}」のトランスクリプトをもとに、
H2/H3構造が明確で、必要であればコード例も含んだ、完全な日本語ブログ記事を書いてください。
前置きなしで本文から入ってください。

TRANSCRIPT:
{transcript[:8000]}"""
        }]
    )
    return message.content[0].text

if __name__ == "__main__":
    url = sys.argv[1]
    topic = sys.argv[2] if len(sys.argv) > 2 else "テクノロジー"

    print("音声をダウンロード中...")
    audio = download_audio(url)

    print("文字起こし中(10〜15分の動画で1〜2分かかります)...")
    transcript = transcribe(audio)

    print("Claudeで記事を執筆中...")
    article = summarize_to_article(transcript, topic)

    with open("output.md", "w") as f:
        f.write(article)
    print("完了!記事はoutput.mdに保存されました")

動作確認

export ANTHROPIC_API_KEY="sk-ant-..."
python main.py "https://youtube.com/watch?v=VIDEO_ID" "Docker container"

output.mdファイルがブログ記事の内容で生成されていれば、最初のステップはクリアです。

詳細解説 — YouTubeの字幕ではなくWhisperを使う理由

これはよく聞かれる質問です。YouTubeの自動字幕はスペルミスや句読点の欠落が多く、技術系の動画では日本語字幕がないものも多いです。Whisperは音声を直接処理するため精度が大幅に高く、特に日本語のアクセントやIT専門用語に強いです。

適切なWhisperモデルを選ぶ

Whisperには5つのサイズがあります。30分以内の動画にはsmallをよく使っています:

  • tiny:約39Mパラメータ — 最速、精度は低め
  • base:約74Mパラメータ — デモやテストに最適なバランス
  • small:約244Mパラメータ — 最もコスパが良い(日常使いしています)
  • medium:約769Mパラメータ — 高精度、安定したGPUが必要
  • large:約1.5Bパラメータ — 最高精度だがCPUでは遅い

NVIDIA GPUがあれば、WhisperはCUDAを自動検出して5〜10倍高速に動作します。GPUがなくてもCPUで動きますが、時間はかかります。

長い動画のコンテキスト制限問題

1時間の動画は15,000〜20,000語のトランスクリプトになることがあります。Claude Sonnetは最大200Kトークンを受け付けますが、コスト削減のため、トランスクリプトを6,000語のチャンクに分割し、各チャンクを要約してから最終的な記事にまとめるようにしています。

応用編 — 長い動画とプレイリストの処理

トランスクリプトのスマートなチャンク分割

def chunk_transcript(text: str, chunk_size: int = 6000) -> list[str]:
    words = text.split()
    return [" ".join(words[i:i + chunk_size]) for i in range(0, len(words), chunk_size)]

def summarize_long_video(transcript: str, topic: str) -> str:
    client = anthropic.Anthropic()
    chunks = chunk_transcript(transcript)
    summaries = []

    for i, chunk in enumerate(chunks):
        print(f"パート{i+1}/{len(chunks)}を要約中...")
        # 中間ステップにHaikuを使用 — Sonnetより10倍安い
        resp = client.messages.create(
            model="claude-haiku-4-5-20251001",
            max_tokens=1024,
            messages=[{"role": "user", "content": f"この部分を簡潔に要約してください(100〜200語):\n\n{chunk}"}]
        )
        summaries.append(resp.content[0].text)

    # Sonnetで完全な記事に仕上げる
    combined = "\n\n".join(summaries)
    final = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=3000,
        messages=[{"role": "user", "content": f"「{topic}」に関する要約をもとに、完全な日本語ブログ記事を書いてください:\n\n{combined}"}]
    )
    return final.content[0].text

プレイリスト全体の処理

# プレイリストからすべてのURLを取得
yt-dlp --flat-playlist --print url "https://youtube.com/playlist?list=PLAYLIST_ID" > urls.txt
with open("urls.txt") as f:
    urls = f.read().splitlines()

for url in urls:
    print(f"\n処理中: {url}")
    audio = download_audio(url, output=f"audio_{abs(hash(url))}")
    transcript = transcribe(audio)
    article = summarize_to_article(transcript, "AIと機械学習")
    with open(f"article_{abs(hash(url))}.md", "w") as f:
        f.write(article)

日常的に使って気づいた実践的なヒント

実際に使っていると、これは習得すべき重要なスキルの一つだと感じています。聞こえが良いからではなく、動画資料を大量にリサーチする際に週に数時間を本当に節約できるからです。

1. 再文字起こしを避けるためのトランスクリプトキャッシュ

Whisperは長い動画だとかなり時間がかかります。トランスクリプトをファイルに保存して再利用しましょう:

import os

def get_or_transcribe(audio_path: str) -> str:
    cache_file = audio_path.replace(".mp3", "_transcript.txt")
    if os.path.exists(cache_file):
        with open(cache_file) as f:
            return f.read()
    transcript = transcribe(audio_path)
    with open(cache_file, "w") as f:
        f.write(transcript)
    return transcript

2. APIコストの節約

中間の要約ステップにはHaikuを使い、最終的な記事執筆にのみSonnetを使います。コストが60〜70%削減される一方、最終的な記事の品質にほとんど影響しません。月に50〜100本動画を処理するなら、この差は無視できません。

3. 必要に応じて言語を強制指定

result = model.transcribe(audio_path, language="vi")  # ベトナム語を強制指定
result = model.transcribe(audio_path, language="ja")  # 日本語を強制指定

4. ユースケースに合わせたプロンプト

PROMPTS = {
    "blog": "H2/H3構造と実践的なコード例を含む、完全な技術ブログ記事を書いてください...",
    "notes": "箇条書きで要約し、要点とアクションアイテムを強調してください...",
    "linkedin": "実践的なインサイトを含む、プロフェッショナルなLinkedInポスト(200〜300語)を書いてください...",
    "quiz": "この動画の内容を理解できているか確認する5択問題を5問作成してください...",
}

5. 法的事項に関する注意

このシステムは、使用権を持つコンテンツに適しています。自分の動画、Creative Commonsライセンスのカンファレンス講演、または購入したコースなどです。商業コンテンツのコピーには使用しないでください。ほとんどのプラットフォームのToSには、コンテンツの自動ダウンロードに関する明確な規定があります。

このシステムは元の記事を読む代わりにはなりません — 80%完成した下書きを提供してくれますが、公開前に手を加える必要はあります。しかし、毎回白紙から始める代わりに、明確な出発点ができます。私にとって、それは「週に3〜4記事書ける」と「記事を書く時間が永遠にない」の違いです。

Share: