本ページはプロモーションが含まれています

AI Python

PythonでYouTube自動要約!Gemini APIでメモ化する全手順

トム

・都内自社開発IT企業勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

「見たいYouTubeチャンネルが多すぎて、時間が足りない」と感じていませんか。

私も情報収集のために多くのチャンネルを登録していますが、毎日すべてを視聴するのは困難でした。

そこで、PythonとGemini APIを使い、お気に入りのチャンネルが更新されたら自動で動画を要約し、Markdownファイルとして出力する仕組みを作りました。この方法で、動画の概要を素早く把握し、視聴時間を大幅に短縮できています。

この記事では、「PythonとGeminiAPIを使ってYouTubeの情報収集を効率化したい」「特定チャンネルの動画を自動で要約させたい」と考えている方に向けて、私が実践している具体的なスクリプトと、その仕組みを解説します。

PythonとGemini APIでYouTubeを自動要約してみた

まずは、今回構築するシステムで何ができるのか、どのような技術を使うのかを整理します。

この記事でできること

この記事で紹介するPythonスクリプトを実行すると、以下の4つが自動で可能になります。

  1. 指定したYouTubeチャンネルの最新動画を定期的にチェックします。
  2. 新しい動画がアップロードされていたら、その動画の字幕データを取得します。
  3. 取得した字幕と動画情報をGemini APIに送信し、日本語の要約を生成させます。
  4. 生成された要約を、動画情報と共にMarkdownファイルとして指定フォルダに保存します。

使う技術(Python/Gemini API/YouTube Transcript API)

今回のシステムは、3つの主要な技術で成り立っています。

  • Python: メインとなるプログラミング言語です。各種APIを連携させ、全体の処理を自動化するスクリプトを記述します。
  • Gemini API: Googleが提供する最新の生成AIモデルです。今回は、YouTubeの字幕データから高精度な要約を生成するためにGeminiAPIを利用します。
  • YouTube Data API v3: Googleが提供するAPIで、特定チャンネルの動画リストや、動画の詳細情報(タイトル、説明、統計など)を取得するために使います。
  • YouTube Transcript API (Pythonライブラリ): YouTube動画の字幕データを簡単に取得できるサードパーティ製のPythonライブラリです。公式APIではありませんが、非常に便利です。

どんな課題を解決できるのか

この仕組みが解決するのは、ズバリ「情報収集の時短」です。

私自身、見たいyoutubeチャンネルが多すぎることが悩みでした。有益な情報が詰まった動画でも、1本あたり15分や30分、時には1時間を超えるものもあります。すべてを視聴していては、時間がいくらあっても足りません。

しかし、このシステムを導入してからは、動画がアップロードされたら自動で要約がMarkdownファイルに出力されるようになりました。

メモを読むだけで、動画の主要なポイントを5分ほどで把握できます。「これは詳しく見るべきだ」と判断した動画だけを、後でじっくり視聴すれば良いため、動画閲覧の時間を劇的に短縮できました。

YouTube要約スクリプトの仕組み

システムがどのような流れで動作するのか、その仕組みを分解して解説します。全体のプロセスは大きく4つのステップに分かれています。

YouTubeの字幕データを取得する方法

要約の元データとなる「字幕」の取得が、このシステムの肝です。

最初に、YouTube Data APIを使い、指定したチャンネルIDから最新の動画リストを取得します。次に、新しく発見した動画のIDを使い、YouTube Transcript APIにリクエストを送ります。

このライブラリが、動画に付与されている字幕データをテキストとして抽出してくれます。

字幕データが存在しない動画(例: 字幕オフ、音楽のみの動画)の場合、このステップはスキップされ、要約も主にタイトルと説明欄から生成されます。

Gemini APIに要約をリクエストする処理

字幕テキストが取得できたら、いよいよGemini APIの出番です。

取得した「動画タイトル」「動画の説明文」、そして「字幕テキスト」をひとまとめにします。これらを、あらかじめ定義したプロンプト(指示文)と一緒にGemini APIに送信します。

プロンプトには「以下の情報を要約してください」「主要ポイントを3点でまとめてください」といった具体的な指示を含めます。GeminiAPIは、受け取った情報を基に、自然な日本語の要約テキストを生成して返します。

メモ(テキストファイル)への書き出し処理

Gemini APIが生成した要約テキストを受け取ったら、それをローカルのファイルとして保存します。

今回のコードでは、読みやすさと管理のしやすさを考慮し、Markdown形式で出力します。

ファイルには、Geminiによる要約内容だけでなく、元の動画タイトル、投稿日、チャンネル名、動画URL、統計情報(再生回数など)も一緒に記録します。これにより、後からメモを見返したときに、元の動画をすぐに見つけ出せるようになります。

全体のコード構成と実行の流れ

全体の流れをまとめると、以下のようになります。

flowchart TD A(["スクリプト起動"]) --> B["前回チェックした動画リスト読込(last_checked_videos.json)"] B --> C["設定情報読込(youtube_config.json)"] C --> D["YouTube Data APIで最新動画5件を取得"] D --> E{"新規動画あり?"} E -->|はい| F["1件ずつ処理ループ開始"] E -->|いいえ| K(["処理終了"]) F --> G["YouTube Transcript APIで字幕を取得"] G --> H["Gemini APIでタイトル・説明・字幕を要約"] H --> I["YouTube Data APIで動画詳細を取得"] I --> J["要約と動画情報をMarkdownファイルに保存(summariesフォルダ)"] J --> M{"次の動画あり?"} M -->|はい| F M -->|いいえ| L["last_checked_videos.jsonを更新(最新リストに書き換え)"] L --> K(["終了"])
  1. スクリプトが起動します。
  2. last_checked_videos.json(前回チェックした動画リスト)を読み込みます。
  3. youtube_config.json(APIキーやチャンネルID)を読み込みます。
  4. YouTube Data APIで指定チャンネルの最新動画5件を取得します。
  5. 取得した動画IDと、前回チェックしたIDリストを比較し、新規動画(まだ処理していない動画)を見つけます。
  6. 新規動画が見つかった場合、1件ずつ処理ループに入ります。
  7. YouTube Transcript APIで字幕を取得します。
  8. Gemini APIでタイトル、説明、字幕を要約します。
  9. YouTube Data APIで動画の詳細情報(統計など)を取得します。
  10. 要約結果と動画情報をMarkdownファイルとしてsummariesフォルダに保存します。
  11. 全ての新規動画の処理が終わったら、last_checked_videos.jsonを最新の動画IDリストで上書き保存します。

このスクリプトを定期的に実行することで、チャンネルの更新を自動で追いかけられます。

実際のコード例と解説

お待たせしました。実際に動作するPythonコードを紹介します。

必要なライブラリと事前準備

このスクリプトを実行するには、いくつかの準備が必要です。

1. Pythonライブラリのインストール

以下のライブラリをpipでインストールしてください。

pip install google-api-python-client google-generativeai youtube-transcript-api

2. APIキーの取得

  • YouTube Data API v3 キー: Google Cloud Platform (GCP) コンソールから取得が必要です。「YouTube Data API v3」を有効化し、APIキーを発行します。
  • Gemini API キー: Google AI Studio から無料で取得できます。

3. 設定ファイルの作成

スクリプトと同じ階層に、youtube_config.jsonという名前のファイルを作成し、以下の内容を記述します。YOUR_の部分はご自身の情報に書き換えてください。

{
  "youtube_api_key": "YOUR_YOUTUBE_API_KEY",
  "gemini_api_key": "YOUR_GEMINI_API_KEY",
  "channel_ids": [
    "UC_CHANNEL_ID_1",
    "UC_CHANNEL_ID_2"
  ],
  "output_folder": "summaries",
  "gemini_model": "gemini-1.5-flash"
}

channel_idsには、監視したいYouTubeチャンネルのID(UCから始まる文字列)をリスト形式で複数指定できます。

Pythonコード全文

以下が、YouTubeチャンネルを監視し、新規動画を自動要約するPythonスクリプトの全文です。

import os
import json
import time
import logging
from datetime import datetime, timedelta
from googleapiclient.discovery import build
import google.generativeai as genai
from youtube_transcript_api import YouTubeTranscriptApi

class YouTubeMonitor:
    def __init__(self, config_path='youtube_config.json'):
        # 設定ファイルから読み込み
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)

        self.youtube_api_key = config['youtube_api_key']
        self.gemini_api_key = config['gemini_api_key']
        if 'channel_ids' in config:
            if isinstance(config['channel_ids'][0], dict):
                self.channel_ids = [ch['id'] for ch in config['channel_ids']]
            else:
                self.channel_ids = config['channel_ids']
        elif 'channel_id' in config:
            self.channel_ids = [config['channel_id']]
        else:
            raise ValueError("設定ファイルにchannel_idsまたはchannel_idが必要です")

        gemini_model = config.get('gemini_model', 'gemini-1.5-flash')

        # 出力フォルダ設定(デフォルトは'summaries')
        self.output_folder = config.get('output_folder', 'summaries')

        # YouTube API設定
        self.youtube = build('youtube', 'v3', developerKey=self.youtube_api_key)

        # Gemini API設定
        genai.configure(api_key=self.gemini_api_key)
        self.model = genai.GenerativeModel(gemini_model)

        # 最後にチェックした動画IDを保存するファイル
        self.last_check_file = 'last_checked_videos.json'

        # ロギング設定
        self.setup_logging()

    def setup_logging(self):
        """ロギングの設定"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler('youtube_monitor.log', encoding='utf-8'),
                logging.StreamHandler()
            ]
        )
        logging.info('YouTube監視システムを起動しました')

    def get_latest_videos(self, channel_id, max_results=5):
        """特定チャンネルの最新動画を取得"""
        try:
            request = self.youtube.search().list(
                part='snippet',
                channelId=channel_id,
                maxResults=max_results,
                order='date',
                type='video'
            )
            response = request.execute()
            logging.info(f"チャンネル {channel_id} から {len(response['items'])} 件の動画を取得しました")
            return response['items']
        except Exception as e:
            logging.error(f"YouTube API エラー (チャンネル: {channel_id}): {e}")
            return []

    def get_video_details(self, video_id):
        """動画の詳細情報を取得"""
        try:
            request = self.youtube.videos().list(
                part='snippet,statistics',
                id=video_id
            )
            response = request.execute()
            if response['items']:
                logging.info(f"動画詳細を取得しました: {video_id}")
                return response['items'][0]
            else:
                logging.warning(f"動画詳細が見つかりません: {video_id}")
                return None
        except Exception as e:
            logging.error(f"動画詳細取得エラー: {e}")
            return None

    def get_video_transcript(self, video_id):
        """動画の字幕を取得"""
        try:
            transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=['ja', 'en'])
            logging.info(f"字幕を取得しました: {video_id}")
            return ' '.join([item['text'] for item in transcript])
        except Exception as e:
            logging.warning(f"字幕取得エラー({video_id}): {e}")
            return None

    def summarize_with_gemini(self, title, description, transcript=None):
        """Geminiを使って動画内容を要約"""
        try:
            # プロンプト作成
            content = f"タイトル: {title}\n\n説明: {description}"

            if transcript:
                content += f"\n\n字幕内容:\n{transcript[:3000]}"  # 字幕は3000文字まで

            prompt = f"""
以下のYouTube動画の内容を日本語で要約してください。
要約は以下の形式で出力してください:

## 📹 動画要約

### 🎯 主要ポイント
- [主要な内容を3-5点でまとめる]

### 📝 詳細内容
[動画の詳細な内容説明]

### 🔍 キーワード
[関連するキーワードを3-5個]

---

動画情報:
{content}
"""

            response = self.model.generate_content(prompt)
            logging.info(f"Geminiで要約を生成しました: {title[:30]}...")
            return response.text
        except Exception as e:
            logging.error(f"Gemini API エラー: {e}")
            return None

    def load_last_checked_videos(self):
        """最後にチェックした動画IDリストを読み込み"""
        try:
            if os.path.exists(self.last_check_file):
                with open(self.last_check_file, 'r', encoding='utf-8') as f:
                    return set(json.load(f))
            return set()
        except Exception as e:
            logging.error(f"前回チェックファイル読み込みエラー: {e}")
            return set()

    def save_last_checked_videos(self, video_ids):
        """チェックした動画IDリストを保存"""
        try:
            with open(self.last_check_file, 'w', encoding='utf-8') as f:
                json.dump(list(video_ids), f, ensure_ascii=False, indent=2)
        except Exception as e:
            logging.error(f"チェックファイル保存エラー: {e}")

    def save_summary_to_markdown(self, video_info, summary):
        """要約をMarkdownファイルに保存"""
        try:
            # 出力フォルダが存在しない場合は作成
            os.makedirs(self.output_folder, exist_ok=True)

            # ファイル名を作成(日時 + 動画タイトル)
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            safe_title = "".join(c for c in video_info['title'][:30] if c.isalnum() or c in (' ', '-', '_')).strip()
            filename = f"{timestamp}_{safe_title}.md"
            filepath = os.path.join(self.output_folder, filename)

            # Markdownコンテンツを作成
            markdown_content = f"""# {video_info['title']}

**投稿日**: {video_info['published_at']}  
**チャンネル**: {video_info['channel_title']}  
**動画URL**: https://www.youtube.com/watch?v={video_info['video_id']}

---

{summary}

---

## 📊 統計情報
- 視聴数: {video_info.get('view_count', 'N/A')}  
- 高評価数: {video_info.get('like_count', 'N/A')}.  
- コメント数: {video_info.get('comment_count', 'N/A')}

## 📝 動画説明
{video_info['description'][:500]}{'...' if len(video_info['description']) > 500 else ''}
"""

            # ファイルに保存
            with open(filepath, 'w', encoding='utf-8') as f:
                f.write(markdown_content)

            logging.info(f"要約を保存しました: {filepath}")
            return filepath
        except Exception as e:
            logging.error(f"Markdown保存エラー: {e}")
            return None

    def check_for_new_videos(self):
        """全チャンネルの新規動画をチェックして要約を作成"""
        print("新規動画をチェック中...")

        last_checked = self.load_last_checked_videos()
        all_new_videos = []
        all_current_video_ids = set()

        # 各チャンネルをチェック
        for channel_id in self.channel_ids:
            print(f"\nチャンネル {channel_id} をチェック中...")
            latest_videos = self.get_latest_videos(channel_id)

            for video in latest_videos:
                video_id = video['id']['videoId']
                all_current_video_ids.add(video_id)

                if video_id not in last_checked:
                    all_new_videos.append(video)

        if not all_new_videos:
            print("\n新規動画はありませんでした。")
            self.save_last_checked_videos(all_current_video_ids)
            return

        print(f"\n合計 {len(all_new_videos)}件の新規動画を発見しました。")

        # 各新規動画を処理
        for video in all_new_videos:
            video_id = video['id']['videoId']
            snippet = video['snippet']

            print(f"\n処理中: {snippet['title']}")

            video_details = self.get_video_details(video_id)
            stats = video_details['statistics'] if video_details else {}

            video_info = {
                'video_id': video_id,
                'title': snippet['title'],
                'description': snippet['description'],
                'published_at': snippet['publishedAt'],
                'channel_title': snippet['channelTitle'],
                'view_count': stats.get('viewCount', 'N/A'),
                'like_count': stats.get('likeCount', 'N/A'),
                'comment_count': stats.get('commentCount', 'N/A')
            }

            transcript = self.get_video_transcript(video_id)
            summary = self.summarize_with_gemini(
                video_info['title'],
                video_info['description'],
                transcript
            )

            if summary:
                self.save_summary_to_markdown(video_info, summary)
            else:
                print(f"要約の作成に失敗しました: {video_info['title']}")

        self.save_last_checked_videos(all_current_video_ids)
        print("\n処理完了!")

if __name__ == "__main__":
    # 設定ファイルから読み込んで監視システムを初期化
    monitor = YouTubeMonitor('youtube_config.json')

    monitor.check_for_new_videos()

ポイント解説(エラーハンドリング/APIキー管理)

このコードを運用する上で重要な点を2つ解説します。

APIキーの管理 (youtube_config.json)

コード内にAPIキーを直接書き込むのは非常に危険です。

今回のコードでは、youtube_config.jsonという別のファイルにAPIキーを書き出し、それをPythonスクリプトが読み込む形にしています。

youtube_config.jsonファイルは、GitHubなどのパブリックな場所に絶対にアップロードしないでください。

実行結果と出力サンプル

このスクリプトを実際に実行すると、ターミナルと出力フォルダ(summaries)にどのような結果が得られるかを紹介します。

YouTube動画を自動で要約した結果

スクリプトを実行すると、ターミナルには以下のようなログが表示されます。どのチャンネルをチェックし、どの動画を新規として処理したかがリアルタイムでわかります。

$ python your_script_name.py
2025-11-03 09:40:00,000 - INFO - YouTube監視システムを起動しました
新規動画をチェック中...

チャンネル UC_CHANNEL_ID_1 をチェック中...
2025-11-03 09:40:01,000 - INFO - チャンネル UC_CHANNEL_ID_1 から 5 件の動画を取得しました

チャンネル UC_CHANNEL_ID_2 をチェック中...
2025-11-03 09:40:02,000 - INFO - チャンネル UC_CHANNEL_ID_2 から 5 件の動画を取得しました

合計 1件の新規動画を発見しました。

処理中: 【新機能】Gemini 2.0 が発表されました!
2025-11-03 09:40:03,000 - INFO - 動画詳細を取得しました: video_id_example
2025-11-03 09:40:04,000 - INFO - 字幕を取得しました: video_id_example
2025-11-03 09:40:06,000 - INFO - Geminiで要約を生成しました: 【新機能】Gemini 2.0 が発表...
2025-11-03 09:40:06,100 - INFO - 要約を保存しました: summaries/20251103_094006_新機能Gemini 20 が発表.md

処理完了!

生成されたメモファイルの例

summariesフォルダには、以下のようなMarkdownファイル(例: 20251103_094006_新機能Gemini 20 が発表.md)が生成されます。要約と動画情報が一つのファイルにまとまるため、後からの見返しや情報管理が非常に楽になります。

# 【新機能】Gemini 2.0 が発表されました!

**投稿日**: 2025-11-03T09:00:00Z  
**チャンネル**: とあるITニュースチャンネル  
**動画URL**: https://www.youtube.com/watch?v=video_id_example

---

## 📹 動画要約

### 🎯 主要ポイント
- Googleが新しいAIモデル「Gemini 2.0」を発表しました。
- 従来のモデルと比較し、推論能力と速度が大幅に向上しています。
- 特に日本語の処理精度が改善され、より自然な応答が可能になりました。

### 📝 詳細内容
今回の発表では、Gemini 2.0のアーキテクチャの変更点と、それによるパフォーマンス向上が中心に語られました。開発者向けには新しいAPIエンドポイントが提供され、Flashモデルもアップデートされています。動画では、実際のデモを交えながら新機能が紹介されています。

### 🔍 キーワード
- Gemini 2.0
- Google AI
- 生成AI
- 新機能

---

## 📊 統計情報
- 視聴数: 10500  
- 高評価数: 2100.  
- コメント数: 350

## 📝 動画説明
本日発表されたGoogleの最新AI、Gemini 2.0について速報でお伝えします!
(...以下、動画の説明文が続く)

応用編|自分好みにカスタマイズする方法

提供したコードはあくまで基本形です。ここから、さらに自分好みにカスタマイズするアイデアを2つ紹介します。

出力形式をMarkdownにする

「生成されたメモファイルの例」で示したとおり、このスクリプトは既に出力形式としてMarkdownを採用しています

Markdown形式の最大の利点は、Obsidian、Notion、VS Codeなど、多くのメモアプリやエディタでそのまま美しく表示・編集できる点です。

もし、save_summary_to_markdown関数のmarkdown_content変数を編集すれば、出力するMarkdownのフォーマットを自由に変更できます。例えば、見出しのレベルを変更したり、特定の情報を追加・削除したりするカスタマイズも簡単です。

自動でObsidianに保存する

もしあなたがメモアプリとしてObsidianを使用しているなら、かなり便利です。

ObsidianはローカルのMarkdownファイルを直接管理する(Vaultと呼ばれるフォルダを監視する)仕組みです。

したがって、youtube_config.jsonoutput_folder設定を、あなたのObsidian Vault内の特定のフォルダ(例: "output_folder": "C:/Users/YourName/Documents/ObsidianVault/YouTubeSummaries")に変更するだけです。

これだけで、Pythonスクリプトが生成した要約メモが、自動的にObsidianのサイドバーに表示されるようになります。手動でファイルを移動する手間すら不要になり、完全な情報収集の自動化が実現します。

まとめ|GeminiとPythonで情報整理を自動化しよう

この記事では、PythonとGemini API、そしてYouTubeのAPIを組み合わせて、特定チャンネルの最新動画を自動で要約し、メモに出力するシステムを構築する方法を解説しました。

今回のポイントおさらい

今回のシステムのポイントは以下の3点です。

  1. APIの連携: YouTube Data APIで動画情報を取得し、YouTube Transcript APIで字幕を抽出し、Gemini APIで要約を生成する、という複数のサービスをPythonが繋ぎました。
  2. 差分チェック: last_checked_videos.jsonファイルで処理済みの動画を管理し、新規動画のみを対象とすることで、無駄なAPI呼び出しを防ぎます。
  3. Markdown出力: 要約結果を汎用性の高いMarkdown形式で保存することで、Obsidianなどのナレッジベースとシームレスに連携できます。

私はこのスクリプト毎日自動で実行するように設定しております。要望があれば別記事にまとめようと思います。

  • この記事を書いた人
  • 最新記事

トム

・都内自社開発IT企業勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

-AI, Python