YouTube の字幕を取得し、OpenAI API を使用して詳細な日本語ブログ記事を生成する Python スクリプトです。さらに、音声ナレーション付きの動画も自動生成します。動画コンテンツをブログ記事やオーディオコンテンツに効率的に変換するためのツールです。
- YouTube 動画から字幕を取得(URL 言語コードを指定)
- 🆕 ローカル転写機能: YouTube動画をダウンロードしてWhisperで転写生成
- OpenAI API を使用して 2000〜3000 文字の日本語ブログ記事を生成
- ブログ記事をテキストファイルとして保存
- テキストを音声に変換(OpenAI Text-to-Speech API 使用)
- ブログコンテンツに基づくワードクラウド画像の生成
- 音声ファイルと静止画像から MP4 動画ファイルを生成
- 音声出力用の最適化されたテキスト生成(URL やポイントセクションを除外)
- 通常の 16:9 フォーマットまたは YouTube Shorts 用の縦型 9:16 フォーマットに対応
- Python 3.7 以降
- Python ライブラリ:
youtube-transcript-api
openai
pillow
(PIL)wordcloud
janome
numpy
yt-dlp
(ローカル転写機能用)openai-whisper
(ローカル転写機能用)pydub
(音声処理用)
- OpenAI API キー
- FFmpeg (動画生成用)
-
リポジトリをクローン:
git clone https://github.com/takurot/youtube2blog.git cd youtube2blog
-
必要な依存関係をインストール:
pip install -r requirements.txt
-
OpenAI API キーを環境変数として設定するか、スクリプト内で直接指定:
- 環境変数として設定:
export OPENAI_API_KEY="your_api_key_here"
- 環境変数として設定:
-
FFmpeg をインストール(動画生成に必要):
- macOS:
brew install ffmpeg
- Ubuntu:
sudo apt install ffmpeg
- Windows: FFmpeg ウェブサイトからダウンロード
- macOS:
以下のコマンドでスクリプトを実行:
python youtube2blog.py <言語コード> <YouTube動画URL> [オプション]
YouTube Transcript APIの問題を回避し、より高品質な転写を得るためのローカル転写モードが利用できます:
python youtube2blog_local.py <YouTube動画URL> [オプション]
または便利なシェルスクリプトを使用:
./run_youtube2blog_local.sh <YouTube動画URL> [whisper_model] [オプション]
通常モード:
言語コード
: 字幕の言語コード (例:en
,ja
)YouTube動画URL
: 字幕を含む YouTube 動画の URL
ローカル転写モード:
YouTube動画URL
: 転写したい YouTube 動画の URLwhisper_model
: 使用するWhisperモデル (tiny, base, small, medium, large)
通常モード:
--image <画像パス>
: 動画作成に使用するカスタム画像ファイルのパス--shorts
: YouTube Shorts 形式(縦長 9:16)の動画を生成--voice <音声名>
: 使用する音声を指定(指定しない場合はランダム選択)- 選択肢: "alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"
--wordcloud
: タイトル画像の代わりにワードクラウド画像を生成して使用--no-bgm
: バックグラウンド音楽を使用しない--blog-only
: ブログ記事のみを生成し、音声・動画は生成しない
ローカル転写モード:
--whisper-model <モデル>
: 使用するWhisperモデル (tiny, base, small, medium, large)--blog-only
: ブログ記事のみを生成し、音声・動画は生成しない--min-words <数値>
: ブログ記事の最小文字数 (デフォルト: 2000)--max-words <数値>
: ブログ記事の最大文字数 (デフォルト: 2500)
通常モード:
- 日本語字幕を取得してブログ記事と音声付き動画を生成:
python youtube2blog.py ja https://www.youtube.com/watch?v=example_video_id
- ワードクラウド画像と特定の音声を使用して Shorts ビデオを生成:
python youtube2blog.py ja https://www.youtube.com/watch?v=example_video_id --shorts --wordcloud --voice nova
ローカル転写モード:
- 基本的な使用(baseモデル使用):
python youtube2blog_local.py https://www.youtube.com/watch?v=example_video_id
- シェルスクリプトを使用してmediumモデルでブログ記事のみ生成:
./run_youtube2blog_local.sh https://www.youtube.com/watch?v=example_video_id medium --blog-only
- 高品質モデルを使用:
python youtube2blog_local.py https://www.youtube.com/watch?v=example_video_id --whisper-model large
スクリプトは以下のファイルを生成します(<日付>
は実行日、<動画ID>
は YouTube 動画 ID):
- ブログ記事:
<日付>_blog_article_<動画ID>.txt
- 音声出力用テキスト:
<日付>_blog_audio_text_<動画ID>.txt
- 音声ファイル:
<日付>_blog_audio_<動画ID>.mp3
- 画像ファイル:
- タイトル画像:
<日付>_title_image_<動画ID>.png
または - ワードクラウド画像:
<日付>_wordcloud_<動画ID>.png
- タイトル画像:
- 動画ファイル:
<日付>_blog_video_<動画ID>.mp4
または<日付>_blog_video_shorts_<動画ID>.mp4
youtube2blog/
│
├── youtube2blog.py # メインスクリプト
├── requirements.txt # 必要な依存関係
└── README.md # このファイル
通常モード:
- 指定された YouTube 動画に字幕が利用可能である必要があります。そうでない場合、文字起こしは失敗します。
- 最近YouTube Transcript APIが一部の環境(特にクラウドサーバー)でブロックされる問題が発生しています。この場合はローカル転写モードを使用してください。
ローカル転写モード:
- 動画のダウンロードとWhisperによる転写処理が必要なため、通常モードよりも時間がかかります。
- Whisperモデルは初回使用時にダウンロードされるため、インターネット接続が必要です。
- 長い動画の場合、相当な処理時間と計算リソースが必要になります。
- より高品質な転写を得るにはlarge モデルを使用しますが、GPUが推奨されます。
共通:
- OpenAI API キーが必要で、API 使用料が発生する場合があります。
- 長い動画の場合、トークン制限により、トランスクリプトを小さな部分に分割するための追加のロジックが必要になることがあります。
- OpenAI の Text-to-Speech API は使用量に応じて課金される場合があります。
- FFmpeg がシステムにインストールされていない場合、動画生成機能は動作しません。
このプロジェクトは MIT ライセンスの下でライセンスされています。詳細については、LICENSE
ファイルを参照してください。
youtube2blog.py
で生成されたブログ記事の主要なポイントに基づいて、元の YouTube 動画から短いクリップ動画を自動生成する機能です。ブログ記事作成からタイムスタンプのマージ、クリップ作成までの一連の処理を自動化するシェルスクリプト run_process_video_to_clips.sh
を提供します。
ブログ記事のハイライト部分に対応する動画クリップを自動で作成し、コンテンツの再利用性を高めます。
run_process_video_to_clips.sh
:youtube2blog.py --blog-only
を実行し、ブログ記事と初期タイムスタンプJSONファイルを生成します。merge_timestamp_clips.py
を実行し、ブログ記事の段落とタイムスタンプ情報をマージしたJSONファイルを生成します。create_clips_from_blog.py
を実行し、マージされたタイムスタンプ情報に基づいて動画クリップを生成します。
youtube2blog.py
: (このプロセスでは--blog-only
モードで使用) 指定されたYouTube動画から字幕を取得し、ブログ記事と初期タイムスタンプファイルを出力します。merge_timestamp_clips.py
: ブログ記事のテキスト構造(段落)と初期タイムスタンプ情報を照合し、各段落に対応する開始・終了時間を含むマージ済みタイムスタンプファイルを作成します。create_clips_from_blog.py
: マージ済みタイムスタンプファイルとブログ記事、そして元の動画(ローカルパス指定またはyt-dlpによるダウンロード)を使用して、各ポイントに対応する動画クリップを切り出し、指定されたディレクトリに保存します。また、クリップの情報をまとめたclips_index.json
も生成します。
コマンド構文:
./run_process_video_to_clips.sh VIDEO_ID [OPTIONS]
必須引数:
VIDEO_ID
: クリップを作成したい YouTube 動画の ID (例:dQw4w9WgXcQ
)
オプション:
--lang LANG_CODE
: 文字起こしとブログ記事生成に使用する言語コード (デフォルト:ja
)。--video-path PATH
: オリジナルの高解像度動画ファイルのローカルパス。このオプションを指定しない場合、スクリプトはyt-dlp
を使用して動画のダウンロードを試みます。--output-dir DIR_BASE_NAME
: 生成されたクリップを保存するディレクトリのベース名。最終的なディレクトリ名はDIR_BASE_NAME_VIDEOID
となります (デフォルトのベース名:my_clips
)。--help
: ヘルプメッセージを表示します。
実行例:
# 基本的な実行 (言語: ja, 出力先: my_clips_VIDEOID/, 動画は自動ダウンロード)
./run_process_video_to_clips.sh dQw4w9WgXcQ
# オリジナル動画ファイルを指定して実行
./run_process_video_to_clips.sh dQw4w9WgXcQ --video-path /path/to/my_original_video.mp4
# 出力ディレクトリのベース名を指定
./run_process_video_to_clips.sh dQw4w9WgXcQ --output-dir processed_clips
出力:
- 指定された出力ディレクトリ (例:
my_clips_VIDEOID/
) 内に、ブログの各ポイントに対応するクリップ動画ファイル (例:clip_01.mp4
,clip_02.mp4
, ...) が生成されます。 - 出力ディレクトリ内に
clips_index.json
ファイルが生成され、各クリップのファイル名、元のブログ記事での段落インデックス、テキスト内容、開始・終了時間などの情報が記録されます。 - 中間ファイルとして、以下のファイルがプロジェクトルートに生成されます:
<日付>_blog_<VIDEO_ID>_article.txt
<日付>_blog_<VIDEO_ID>_timestamps.json
(初期タイムスタンプ)<日付>_blog_<VIDEO_ID>_timestamps_merged.json
(マージ済みタイムスタンプ)
- クリップへの情報付加(オプション): 切り出したクリップに、関連するブログのテキストを字幕としてオーバーレイ表示する機能などを検討します。
YouTube2Blogを実行したとき、以下のようなエラーが発生する場合があります:
ワードクラウド画像生成中にエラーが発生しました: 'utf-8' codec can't decode byte 0xb0 in position 37: invalid start byte
これはmatplotlibのスタイルファイルに関連するエンコーディングの問題です。WordCloudはmatplotlibに依存しており、隠しファイル(._から始まるファイル)を適切に処理できないことが原因です。
このリポジトリで提供されているfix_all_matplotlib_styles.py
スクリプトを実行することで、問題を自動的に修正できます:
python fix_all_matplotlib_styles.py
または:
chmod +x fix_all_matplotlib_styles.py
./fix_all_matplotlib_styles.py
このスクリプトは問題のあるスタイルファイルを検出して修正します。
手動で修正したい場合は、以下のようにmatplotlibのスタイルディレクトリ内の隠しファイルを削除または修正します:
-
matplotlibのインストールパスを確認する:
import matplotlib print(matplotlib.__file__)
-
stylelib ディレクトリに移動:
cd /path/to/matplotlib/mpl-data/stylelib/
-
問題のあるファイルを削除または空のファイルに置き換え:
# 例: rm ._* rm .__* # または # echo "# Fixed" > .__classic_test_patch.mplstyle
上記の方法で解決しない場合は、matplotlibを再インストールすることで問題が解消する場合があります:
pip install --force-reinstall matplotlib==3.5.3
修正が正常に適用されたかを確認するには、テストスクリプトを実行します:
python test_wordcloud_simple.py
正常に動作すれば、カレントディレクトリに日付付きのワードクラウド画像ファイルが生成されます。
もし上記の方法で問題が解決しない場合:
-
仮想環境を再構築する:
rm -rf .env python -m venv .env source .env/bin/activate pip install -r requirements.txt
-
WordCloudを使わずにタイトル画像のみを生成する(--wordcloud オプションを指定しない):
./run_youtube2blog.sh VIDEO_ID
この問題はmacOSで特に発生しやすく、macOSがリソースフォークのために生成する隠しファイル(._で始まるファイル)がUTF-8以外のエンコーディングを使用していることが原因です。これらのファイルはmatplotlibのスタイル設定の読み込み時に問題を引き起こします。
修正スクリプトはこれらの隠しファイルを検出し、UTF-8でエンコードされた空のファイルに置き換えることで問題を解決します。
生成したブログ記事と音声読み上げ動画をYouTubeに自動アップロードすることができます。
- 必要なライブラリをインストール:
pip install --upgrade google-api-python-client google-auth-oauthlib google-auth-httplib2
-
Google Cloud Platformで認証情報を取得:
- Google Cloud Console にアクセス
- プロジェクトを作成またはすでにあるプロジェクトを選択
- 「APIとサービス」→「ライブラリ」で「YouTube Data API v3」を検索し有効化
- 「認証情報」→「認証情報を作成」→「OAuthクライアントID」を選択
- アプリケーションタイプ: デスクトップアプリ
- 作成された認証情報の「JSONをダウンロード」をクリック
- ダウンロードしたJSONファイルを
client_secrets.json
としてプロジェクトのルートディレクトリに保存するか、環境変数で指定(下記参照)
-
環境変数の設定(オプション):
- クライアントシークレットファイルのパスを環境変数で指定できます
# Linuxまたは macOS export YOUTUBE_CLIENT_SECRET=/path/to/your/client_secrets.json # Windows (コマンドプロンプト) set YOUTUBE_CLIENT_SECRET=C:\path\to\your\client_secrets.json # Windows (PowerShell) $env:YOUTUBE_CLIENT_SECRET="C:\path\to\your\client_secrets.json"
コマンドラインから特定のYouTube動画ID(元にしたコンテンツ)を指定して実行:
python youtube_uploader.py [YouTube動画ID]
または、引数なしで実行すると最新の生成ファイルを検索してアップロード:
python youtube_uploader.py
特定のYouTubeチャンネルを指定してアップロード:
python youtube_uploader.py --channel チャンネル名 [YouTube動画ID]
run_youtube2blog.shからまとめて実行する場合:
./run_youtube2blog.sh VIDEO_ID [LANGUAGE] [CHANNEL]
*_blog_article_*.txt
ファイルからタイトルを抽出(ハッシュタグは除去)- 動画の説明文に元動画のURLを含める
- 説明欄の最後にハッシュタグを追加
*_blog_audio_text_*.txt
ファイルを字幕としてアップロード*_wordcloud_*.png
ファイルをサムネイルとして設定- 動画は非公開状態で投稿(後で確認してから公開可能)
初回実行時はブラウザが開き、YouTubeアカウントでの認証が求められます。認証後はtoken_チャンネル名.json
に認証情報が保存され、以降の実行では自動的に再利用されます。
生成される動画にバックグラウンド音楽を自動的に追加することができます。
bgm
ディレクトリ内の音楽ファイル(MP3形式)からランダムに選択- BGMは動画の長さに合わせて自動的にループ再生(ナレーションが終わるまで継続)
- 音声とBGMをミックス(メイン音声を優先した適切な音量バランス)
- 動画終了時に自然なフェードアウト
- プロジェクトフォルダ内に
bgm
ディレクトリを作成 - 著作権フリーのMP3ファイルを配置
# BGMを有効にして実行(デフォルト)
python youtube2blog.py ja "https://www.youtube.com/watch?v=VIDEO_ID" --wordcloud
# BGMを無効にして実行
python youtube2blog.py ja "https://www.youtube.com/watch?v=VIDEO_ID" --wordcloud --no-bgm
- BGMには著作権フリーの音楽を使用してください
- 音楽ファイル名に特殊文字が含まれている場合、処理に問題が生じる可能性があります
- 生成される動画のサイズはBGMを使用すると大きくなります
- 極端に短いMP3ファイルを使用すると、ループの継ぎ目が目立つ場合があります
ブログ記事から音声用テキストを生成する際に、以下の処理を行い、自然な音声出力を実現します:
-
不要なセクションの除去
- YouTube URLの行
- 「ポイント」セクションの内容(箇条書きリスト)
-
マークダウン記号の削除
- 見出し記号(#)
- 水平線(---、***、___)
- リストマーカー(-、*、+、1. など)
- 強調記号(太字、斜体)
- コード記号(
コード
) - リンク記号(テキスト→テキストのみ残す)
これにより、音声合成エンジンがマークダウン記号を読み上げてしまう問題を防ぎ、より自然な音声出力が可能になります。
--image
: 動画作成に使用する静止画像ファイルを指定--shorts
: YouTube Shorts形式(縦長動画)で出力--voice
: 使用する音声を指定(alloy, echo, fable, onyx, nova, shimmer)--wordcloud
: ワードクラウド画像を生成して使用--no-bgm
: バックグラウンド音楽を使用しない--blog-only
: ブログ記事のみを生成し、音声・動画は生成しない
# 基本的な使用方法
python youtube2blog.py ja "https://www.youtube.com/watch?v=VIDEO_ID"
# ブログ記事のみを生成する場合
python youtube2blog.py ja "https://www.youtube.com/watch?v=VIDEO_ID" --blog-only
# ワードクラウド画像を使用して動画を生成
python youtube2blog.py ja "https://www.youtube.com/watch?v=VIDEO_ID" --wordcloud
# 特定の音声を指定して動画を生成
python youtube2blog.py ja "https://www.youtube.com/watch?v=VIDEO_ID" --voice nova
# YouTube Shorts形式(縦長動画)で出力
python youtube2blog.py ja "https://www.youtube.com/watch?v=VIDEO_ID" --shorts
# BGMなしで動画を生成
python youtube2blog.py ja "https://www.youtube.com/watch?v=VIDEO_ID" --no-bgm