YouTube動画をMarkdown形式で要約するPythonスクリプト

## はじめに

このスクリプトは、YouTube動画のURLを入力すると、動画の情報、字幕（トランスクリプト）、そしてAIによる要約をMarkdown形式で出力します。これにより、動画の内容をテキストデータとして活用し、再利用や分析を効率的に行うことができます。

出力されるMarkdownファイルは、例えばVisual Studio Codeの拡張機能であるMarkmapでマインドマップのように表示したり、Obsidianなどのノートアプリでハッシュタグ検索をしたりするのに便利です。

## 準備

### 必要なライブラリのインストール

以下のコマンドを実行して、必要なライブラリをインストールしてください。

```bash
pip install google-api-python-client
pip install requests
pip install youtube-transcript-api
pip install google-generativeai
```

### APIキーの取得

このスクリプトでは、以下の2つのAPIキーが必要です。

#### Google AI APIキー

1.  [Google Cloud Platform](https://console.cloud.google.com/) にアクセスし、Googleアカウントでログインします。
2.  新しいプロジェクトを作成します。
3.  「APIとサービス」>「ライブラリ」で「Google AI」を検索し、APIを有効にします。
4.  「APIとサービス」>「認証情報」でAPIキーを作成し、安全な場所に保管します。

#### YouTube Data API v3キー

1.  [Google Cloud Platform](https://console.cloud.google.com/) にアクセスし、Googleアカウントでログインします。
2.  新しいプロジェクトを作成します。
3.  「APIとサービス」>「ライブラリ」で「YouTube Data API v3」を検索し、APIを有効にします。
4.  「APIとサービス」>「認証情報」でAPIキーを作成し、安全な場所に保管します。

**重要:** APIキーは機密情報です。GitHubなどの公開リポジトリにコミットせず、環境変数に設定するなど安全な方法で管理してください。

#### 環境変数への設定方法

APIキーを環境変数に設定する方法は、オペレーティングシステムによって異なります。

**Windowsの場合:**

1.  **システム環境変数**を設定する方法:
    *   スタートメニューを右クリックし、「システム」>「詳細設定」>「環境変数」を選択します。
    *   「システム環境変数」の「新規」をクリックし、変数名に `YOUTUBE_API_KEY` または `GOOGLE_API_KEY` を、変数値に取得したAPIキーを入力します。
    *   設定後、コマンドプロンプトまたはPowerShellを再起動して変更を反映させます。
2.  **PowerShell** で一時的に設定する方法:
    ```powershell
    $env:YOUTUBE_API_KEY="YOUR_YOUTUBE_API_KEY"
    $env:GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
    ```
    この方法は現在のPowerShellセッションでのみ有効です。

**Ubuntu (Linux)の場合:**

1.  `~/.bashrc` または `~/.zshrc` ファイルを編集する方法:
    ```bash
    echo 'export YOUTUBE_API_KEY="YOUR_YOUTUBE_API_KEY"' >> ~/.bashrc
    echo 'export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"' >> ~/.bashrc
    source ~/.bashrc
    ```
    または
    ```bash
    echo 'export YOUTUBE_API_KEY="YOUR_YOUTUBE_API_KEY"' >> ~/.zshrc
    echo 'export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"' >> ~/.zshrc
    source ~/.zshrc
    ```
    `YOUR_YOUTUBE_API_KEY` と `YOUR_GOOGLE_API_KEY` は、それぞれのAPIキーに置き換えてください。
    設定後、新しいターミナルを開くか、`source ~/.bashrc` または `source ~/.zshrc` を実行して変更を反映させます。
2.  **ターミナル** で一時的に設定する方法:
    ```bash
    export YOUTUBE_API_KEY="YOUR_YOUTUBE_API_KEY"
    export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
    ```
    この方法は現在のターミナルセッションでのみ有効です。

**注意:**
*   APIキーを環境変数に設定する際は、`YOUR_YOUTUBE_API_KEY` と `YOUR_GOOGLE_API_KEY` を、実際に取得したAPIキーに置き換えてください。
*   環境変数の設定は、スクリプトを実行する前に完了させてください。

## スクリプトの概要

このスクリプトは、`YoutubeToMarkdown` クラスとして実装されています。主な機能は以下の通りです。

1.  **APIクライアントの初期化:** YouTube Data API v3とGoogle Gemini APIのクライアントを初期化します。APIキーは環境変数から取得します。
2.  **動画情報の取得:** YouTube Data APIを用いて、動画のタイトル、説明、公開日時などの情報を取得します。
3.  **トランスクリプトの取得:** `youtube-transcript-api` ライブラリを用いて、動画のトランスクリプト（字幕）を取得します。
4.  **AIによる要約:** Gemini APIを用いて、トランスクリプトの内容を要約します。
5.  **Markdown形式での出力:** 取得した動画情報、トランスクリプト、要約をMarkdown形式で整形し、ファイルに保存します。

## コードの詳細

以下に、`YoutubeToMarkdown` クラスのコードを示します。

````python
import os
from urllib.parse import parse_qs, urlparse
from datetime import datetime
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from youtube_transcript_api import YouTubeTranscriptApi
import re
import google.generativeai as genai

class YoutubeToMarkdown:
    """
    YouTube動画の情報を取得し、Markdown形式で出力するクラス。
    """
    def __init__(self):
        """
        初期化処理。YouTube APIとGemini APIの初期化を行う。
        """
        self._init_youtube_api()
        self._init_gemini_api()

    def _init_youtube_api(self):
        """
        YouTube APIクライアントを初期化する。
        """
        self.api_key = os.getenv('YOUTUBE_API_KEY')
        if not self.api_key:
            raise ValueError("YOUTUBE_API_KEY 環境変数が設定されていません。")
        try:
            self.youtube = build('youtube', 'v3', developerKey=self.api_key)
        except Exception as e:
            print(f"YouTube API の初期化エラー: {e}")
            self.youtube = None

    def _init_gemini_api(self):
        """
        Gemini APIクライアントを初期化する。
        """
        self.gemini_api_key = os.getenv('GOOGLE_API_KEY')
        if not self.gemini_api_key:
            raise ValueError("GOOGLE_API_KEY 環境変数が設定されていません。")
        genai.configure(api_key=self.gemini_api_key)
        self.model = genai.GenerativeModel('gemini-1.5-flash')

    def get_video_info(self, video_id):
        """
        YouTube動画の情報を取得する。

        Args:
            video_id (str): YouTube動画のID。

        Returns:
            dict: YouTube動画の情報。

        Raises:
            RuntimeError: YouTube APIが初期化されていない場合、または動画情報の取得に失敗した場合。
        """
        if not self.youtube:
            raise RuntimeError("YouTube API が初期化されていません。")
        try:
            request = self.youtube.videos().list(
                part="snippet,statistics,contentDetails",
                id=video_id
            )
            return request.execute()
        except Exception as e:
            raise RuntimeError(f"動画情報の取得に失敗: {e}")

    def get_transcript(self, video_id):
        """
        YouTube動画のトランスクリプトを取得する。

        Args:
            video_id (str): YouTube動画のID。

        Returns:
            list: トランスクリプトのリスト。取得できない場合はNone。
        """
        try:
            transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)
            transcript = transcript_list.find_generated_transcript(['ja'])
            return transcript.fetch()
        except Exception:
            try:
                transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)
                transcript = transcript_list.find_generated_transcript(['en'])
                return transcript.fetch()
            except Exception as e:
                print(f"トランスクリプト取得エラー: {e}")
                return None

    def create_markdown(self, video_info, transcript, summary):
        """
        動画情報、トランスクリプト、要約からMarkdown形式の文字列を生成する。

        Args:
            video_info (dict): YouTube動画の情報。
            transcript (list): トランスクリプトのリスト。
            summary (str): 動画の要約。

        Returns:
            str: Markdown形式の文字列。
        """
        video = video_info['items'][0]
        snippet = video['snippet']
        stats = video['statistics']
        
        publish_date = datetime.fromisoformat(
            snippet['publishedAt'].replace('Z', '+00:00')
        ).strftime('%Y-%m-%d %H:%M:%S')

        markdown = f"""# {snippet['title']}

## 基本情報
- チャンネル: {snippet['channelTitle']}
- 公開日時: {publish_date}
- 再生回数: {stats.get('viewCount', '不明')}
- URL: https://www.youtube.com/watch?v={video['id']}

## 動画説明
{snippet['description']}

## AI要約
{summary}
"""
        return markdown

    def summarize_content(self, video_info, transcript):
        """
        動画のトランスクリプトを要約する。

        Args:
            video_info (dict): YouTube動画の情報。
            transcript (list): トランスクリプトのリスト。

        Returns:
            str: 要約されたテキスト。トランスクリプトがない場合はエラーメッセージ。
        """
        if not transcript:
            return "トランスクリプトが利用できません。"

        video = video_info['items'][0]
        prompt = f"""
以下のYouTube動画コンテンツを階層的に要約してください：

タイトル: {video['snippet']['title']}
説明: {video['snippet']['description']}

トランスクリプト:
{' '.join([t['text'] for t in transcript])}

要約の際は以下の点に注意してください：
1. Markdown形式で出力
2. # (h1)では出力せず、"タイトル: *動画のタイトル*"とする
2. 主要なトピックは### (h3)で区切る（セクションの判定ができるのであればセクションで区切る）
3. サブトピックは箇条書きで表現
4. 重要なキーワードは**太字**で強調
5. 説明文中のURLやハッシュタグは保持
6. 誤認識や変換ミスの可能性があるので全体の文脈を見て修正
7. 言い間違いや事実誤認の可能性があるのでファクトチェックを実施
"""

        try:
            response = self.model.generate_content(prompt)
            return response.text
        except Exception as e:
            return f"要約生成エラー: {e}"

    def analyze_video(self, url):
        """
        YouTube動画のURLから情報を取得し、Markdown形式でファイルに保存する。

        Args:
            url (str): YouTube動画のURL。

        Returns:
            str: 出力ファイル名。

        Raises:
            ValueError: 無効なYouTube URLの場合。
        """
        video_id = parse_qs(urlparse(url).query).get('v', [None])[0]
        if not video_id:
            raise ValueError("無効なYouTube URL")

        video_info = self.get_video_info(video_id)
        transcript = self.get_transcript(video_id)
        summary = self.summarize_content(video_info, transcript)
        markdown = self.create_markdown(video_info, transcript, summary)

        # ファイル名生成
        safe_title = re.sub(r'[\\/:*?"<>|]+', '', video_info['items'][0]['snippet']['title'])[:30]
        timestamp = datetime.now().strftime('%Y%m%d_%H%M')
        output_file = f"{timestamp}-summary-{safe_title}-youtube.md"

        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(markdown)
        
        return output_file

if __name__ == '__main__':
    import sys
    try:
        # テスト用URLを設定
        test_url = "https://www.youtube.com/watch?v=xxxxxxxxxxx"  # テスト用URLを設定。リリース時にはコメントアウト
        
        if 'ipykernel' in sys.modules:
            # Jupyter Notebook 環境の場合、テストURLを即使用
            url = test_url
            print("Jupyter Notebook環境を検出。テストURLを使用します。")
        elif len(sys.argv) > 1:
            # コマンドライン引数がある場合、それを使用
            url = sys.argv[1]
        else:
            # コマンドライン引数がない場合、プロンプトで入力を求める
            url = input("YouTube URLを入力: ")
        
        analyzer = YoutubeToMarkdown()
        output_file = analyzer.analyze_video(url)
        print(f"分析結果を {output_file} に保存しました。")
    except Exception as e:
        print(f"エラーが発生しました: {e}")
````

### クラスのメソッド

-   `__init__()`: クラスの初期化を行います。YouTube APIとGemini APIのクライアントを初期化します。
-   `_init_youtube_api()`: YouTube APIクライアントを初期化します。
-   `_init_gemini_api()`: Gemini APIクライアントを初期化します。
-   `get_video_info(video_id)`: YouTube動画の情報を取得します。
-   `get_transcript(video_id)`: YouTube動画のトランスクリプトを取得します。
-   `create_markdown(video_info, transcript, summary)`: 動画情報、トランスクリプト、要約からMarkdown形式の文字列を生成します。
-   `summarize_content(video_info, transcript)`: 動画のトランスクリプトを要約します。
-   `analyze_video(url)`: 動画URLを受け取り、動画情報、トランスクリプトを取得し、Markdown形式でファイルに保存します。

## スクリプトの使い方

1.  上記のコードを `youtube_to_markdown.py` という名前で保存します。
2.  環境変数 `YOUTUBE_API_KEY` と `GOOGLE_API_KEY` に、それぞれ取得したAPIキーを設定します。
3.  ターミナルで以下のコマンドを実行し、スクリプトを実行します。

    ```bash
    python youtube_to_markdown.py <YouTube動画URL>
    ```

    または、Jupyter Notebook環境で実行する場合は、以下のコードを実行します。

    ```python
    from youtube_to_markdown import YoutubeToMarkdown

    youtube_to_markdown = YoutubeToMarkdown()
    video_url = "https://www.youtube.com/watch?v=xxxxxxxxxxx"  # 例として、このURLを使用
    output_file = youtube_to_markdown.analyze_video(video_url)
    print(f"分析結果を {output_file} に保存しました。")
    ```

    `<YouTube動画URL>` には、分析したいYouTube動画のURLを指定してください。

## 実行結果

スクリプトを実行すると、動画の情報、トランスクリプト、AIによる要約がMarkdown形式でファイルに保存されます。ファイル名は `YYYYMMDD_HHMM-summary-<動画タイトル>-youtube.md` の形式になります。

## まとめ

このスクリプトを使うことで、YouTube動画の内容をテキスト形式で簡単に取得し、様々な用途に活用できます。APIキーの管理には十分注意し、安全に利用してください。