PR

Python+gTTS+MoviePyでブログ記事を自動ナレーション動画化する方法

こんにちは、イカPOです。

今回は 「ChatGPTとPythonを組み合わせて、画像とナレーションから自動で動画を作る方法」 を紹介します。

記事を ChatGPT で要約し、台本化したテキストを gTTS でナレーション音声に変換。その音声とスライド画像を MoviePy で結合すれば、ワンクリックで動画が完成します。

手順はシンプルで、

  1. スライドを用意する
  2. 原稿を用意する
  3. Python スクリプトを実行する
    ──これだけです。

スライド内容の要約や原稿作成は ChatGPT に任せれば、制作スピードは一気に加速。発表のアーカイブや、社内向けの教育・共有コンテンツを短時間で動画化したい場面に最適です。

さらに記事内には サンプルコードとZIPファイル(bat付き) を添付しています。Pythonさえインストールすれば、そのまま実行できる仕組みになっていますので、ぜひ試してみてください。

サンプルコード (20 ダウンロード )

ライブラリ紹介 — gTTS と MoviePy をかんたん解説

  • gTTS:文章→音声への変換が得意。声のイントネーションや句読点もキレイに反映してくれるのが最高。
  • MoviePy:動画編集がコードで完結。Slidesスライドの結合〜ナレーションとの合体まで、一気に処理できる優れもの。

どちらも「自動化」「再現性」「テキスト軸」の強みがあって、記事やスライドから動画コンテンツを自動生成したいって人には超おすすめです。

gTTS(Google Text-to-Speech)

Google 翻訳の読み上げ機能を Python から扱えるライブラリで、文章をそのまま MP3音声ファイル に変換できます。
lang="ja" を指定すれば日本語の読み上げも可能で、句読点を多めに入れるとより自然な読み上げになります。
軽量で導入が簡単であり、読み仮名や略語の補正など前処理で発音をコントロールできる点も特徴です。

インストールはターミナルまたはコマンドプロンプトで

pip install gTTS

と入力するだけです。
例えば、以下のコードで簡単に音声ファイルが作成できます。

from gtts import gTTS
tts = gTTS("こんにちは、ブログを音声にします。", lang="ja")
tts.save("output.mp3")

MoviePy(Pythonで動画編集まるごと)

Python で動画編集を自動化できるライブラリです。カット、連結、タイトル挿入、音声合成、速度変更などをスクリプトでまとめて処理できます。
GIF や MP4 など多数のフォーマットに対応し、裏では ffmpeg を利用して高品質な動画を生成します。直感的な API で、数行のコードでも複雑な編集が可能です。

インストールはターミナルまたはコマンドプロンプトで

pip install moviepy

としますが、もし

ModuleNotFoundError: No module named 'moviepy.editor'

というエラーが出た場合は、安定して動くこちらのバージョンを使ってください。

pip install moviepy==2.0.0.dev2
サンプルコード(画像→動画)

まず、ダミー画像をダウンロードして、スクリプトと同じフォルダに置きます。
📷 dummy_image.png をダウンロード

そのうえで、以下のコードを img_to_video.py として保存し、実行します。

# img_to_video.py
from moviepy.editor import ImageClip

# ダミー画像(同一フォルダの dummy_image.png)を5秒の動画に変換
clip = ImageClip("dummy_image.png").set_duration(5)

# mp4として出力(24fps)
clip.write_videofile("result_from_image.mp4", fps=24)
ディレクトリ構成(最小)
moviepy_image_sample/
├─ img_to_video.py # コード
├─ dummy_image.png      # ← ダウンロードした画像をここに置く
└─ result_from_image.mp4  # 実行後に生成


こんな感じのディレクトリ構成でサンプルは動きます。
了解。実装編を「必要なもの」中心に再構成しました。サンプルコードは出さず、**ディレクトリ構成(最小)**だけ掲載しています。


実装編。画像とナレーションテキストを用意して動画をつくろう

実際に画像とナレーションを用意して動画を作る方法を紹介します。サンプルコードも用意しているので試しに動かしてみてください。記事の最後にクリックで動くbat付きZIPファイルを用意してるので、そちらも参考にしてください。

コードを動かすのに必要なものリスト

  • Python 3.11
    本ワークフローは 3.11 系で動作確認しています。
  • main_simple.py
    この記事で配布しているスクリプト。narration.txt の本文をそのまま gTTS で読み上げます。
  • ナレーションテキストinput/narration.txt
    セクションは # で区切り、1行目がタイトル/2行目以降が本文。本文が音声になります。
  • 画像(任意)
    input/slide_0.png, input/slide_1.jpg… のように番号付きで置くと、そのセクションの静止画として差し込みます。画像が無いセクションは自動生成のタイトルカード(黒地+タイトル)になります。

ディレクトリ構成(最小)

project/
├─ main.py
├─ input/
│  ├─ narration.txt      # 原稿(本文)— 「#」で区切り、1行目=タイトル/以降=本文
│  ├─ slide_0.png        # 任意:セクション0の画像
│  └─ slide_1.jpg        # 任意:セクション1の画像…
├─ output/               # 自動出力(final_video.mp4)
└─ temp/                 # 中間ファイル(音声など)

サンプルコード

import os
from gtts import gTTS
from moviepy.editor import (
    AudioFileClip, ImageClip, CompositeVideoClip, ColorClip,
    concatenate_videoclips, vfx
)
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import contextlib

# ===== 設定 =====
INPUT_DIR  = "input"
OUTPUT_DIR = "output"
TEMP_DIR   = "temp"
SIZE       = (1280, 720)
FPS        = 24

os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(TEMP_DIR, exist_ok=True)

# ===== タイトル画像生成(スライドが無い場合用) =====
def make_title_clip(title: str, duration: float, size=SIZE):
    w, h = size
    img = Image.new("RGB", size, (0, 0, 0))
    draw = ImageDraw.Draw(img)

    font_candidates = [
        "Yu Gothic UI.ttf", "YuGothR.ttc", "Meiryo.ttc",
        "/System/Library/Fonts/ヒラギノ角ゴシック W6.ttc",
    ]
    font = None
    for path in font_candidates:
        try:
            font = ImageFont.truetype(path, size=64)
            break
        except:
            pass
    if font is None:
        font = ImageFont.load_default()

    max_w = int(w * 0.9)
    lines, line = [], ""
    for ch in title:
        tw, _ = draw.textbbox((0, 0), line + ch, font=font)[2:]
        if tw <= max_w:
            line += ch
        else:
            lines.append(line)
            line = ch
    if line:
        lines.append(line)

    total_h = sum(draw.textbbox((0, 0), l, font=font)[3] for l in lines) + 10 * (len(lines) - 1)
    y = (h - total_h) // 2
    for l in lines:
        _, _, tw, th = draw.textbbox((0, 0), l, font=font)
        x = (w - tw) // 2
        draw.text((x, y), l, fill=(255, 255, 255), font=font)
        y += th + 10

    return ImageClip(np.array(img)).set_duration(duration)

def find_slide_image(index: int):
    for ext in (".png", ".jpg", ".jpeg", ".webp"):
        p = os.path.join(INPUT_DIR, f"slide_{index}{ext}")
        if os.path.exists(p):
            return p
    return None

# ===== 原稿読み込み =====
with open(os.path.join(INPUT_DIR, "narration.txt"), "r", encoding="utf-8") as f:
    raw = f.read()

# 「#」区切りでセクション分割
sections = [s.strip() for s in raw.split("#") if s.strip()]

video_clips = []

for i, section in enumerate(sections):
    lines = section.splitlines()
    if not lines:
        continue

    title = lines[0].strip()
    body = "\n".join(lines[1:]).strip()
    if not body:
        continue

    # 音声生成
    voice_path = os.path.join(TEMP_DIR, f"voice_{i}.mp3")
    tts = gTTS(body, lang="ja")
    tts.save(voice_path)

    audio_clip = AudioFileClip(voice_path)
    duration = audio_clip.duration

    # 画像
    image_path = find_slide_image(i)
    if image_path:
        img_clip = ImageClip(image_path).set_duration(duration)
    else:
        bg = ColorClip(size=SIZE, color=(0, 0, 0), duration=duration)
        title_clip = make_title_clip(title, duration)
        img_clip = CompositeVideoClip([bg, title_clip.set_position("center")], size=SIZE).set_duration(duration)

    clip = img_clip.set_audio(audio_clip)
    video_clips.append((clip, audio_clip))

# ===== 出力 =====
if not video_clips:
    raise RuntimeError("動画にできるクリップがありません。")

final = concatenate_videoclips([vc for vc, _ in video_clips], method="compose")
final = final.fx(vfx.speedx, 1.5)

out_path = os.path.join(OUTPUT_DIR, "final_video.mp4")
final.write_videofile(out_path, fps=FPS)

# 後片付け
with contextlib.suppress(Exception):
    final.close()
for vc, ac in video_clips:
    with contextlib.suppress(Exception):
        vc.close()
        ac.close()

print(f"[done] {out_path}")

メモ(調整ポイントだけ)

  • 再生速度main.py の終盤、vfx.speedx(1.5) の係数を変更(等速=1.0)。
  • 出力サイズSIZE = (1280, 720) を変更(例:縦動画は (720, 1280))。

以上を揃えれば、原稿と画像を置くだけで動画が書き出せます。

画像ついての注意点

画像ファイルは必ず連番で命名
例:slide_0.png, slide_1.jpg, slide_2.png

slide_0 は特別枠(タイトル用)

  • slide_0.xxx があれば冒頭のタイトルシーンに使われます
  • 無ければ自動で黒背景+タイトルテキストが生成されます

slide_1 以降が本文のスライド

  • それぞれのセクションに対応
  • 拡張子は .png, .jpg, .jpeg, .webp のいずれでもOK

スライドが存在しない場合

  • そのセクションは黒背景+タイトルテキストが表示されます

簡単に実行手順

Pythonの知識は不要です。ZIPを解凍して下記の順に進めるだけでOK。

サンプルコードはこちら

サンプルコード (20 ダウンロード )
  1. Python 3.11 をインストール
    • 公式サイトから入手
    • インストール時に「Add Python to PATH」に必ずチェック
    • 確認:python --versionPython 3.13.x と表示されればOK
  2. 添付のZIPファイルを解凍
    解凍後のフォルダに main.pyrun.batinput フォルダがあります。
  3. ナレーションテキストに原稿を貼る
    • input/narration.txt に動画化したい原稿を貼り付け
    • # 原稿(本文)— 「#」で区切りで
  4. スライド画像を入れる
    • input/slide_0.pnginput/slide_1.jpg… の形式で置くと、そのシーンに反映
    • 無ければ自動でタイトルカード生成
  5. run.bat をクリック
    • 初回は必要なライブラリが自動インストールされます
    • 完成動画は output/final_video.mp4 に出力

ナレーションテキストのルール

  1. 区切りは「#」
    • 各セクションは # から始めます。
    • # の行は「スライドタイトル」になり、動画に表示されます。
  2. 読み上げは本文のみ
    • # 行(スライドタイトル)は読み上げません。
    • 読ませたい場合は、タイトルを本文にも書きます。
  3. 空行は無視
    • 空白行は自動的にスキップされます。
  4. 日本語はそのまま書けばOK
    • gTTS が自然に読み上げます。
    • 読みにくい場合は句読点(、。)を増やすと自然になります。

サンプル(input/narration.txt の例)
# はじめに
はじめに
この動画では Python と MoviePy を使って、
画像とナレーションから動画を自動生成する方法を紹介します。

# ライブラリについて
ライブラリについて
まず gTTS で日本語音声を生成し、
MoviePy で動画に合成します。

# まとめ
まとめ
Python と数行のコードで、
スライドつきのナレーション動画が作成できます。

まとめ

ChatGPTで作成した原稿を gTTS で音声化し、MoviePy で画像と結合することで、誰でも手軽にスライド付きの動画を自動生成できます。画像がなくても自動でタイトルカードを生成でき、速度やサイズもコードの一部を変えるだけで調整可能です。発表のアーカイブや社内資料の動画化など、時短に役立つワークフローなのでぜひ活用してみてください。

サンプルコードはこちらから

サンプルコード (20 ダウンロード )
タイトルとURLをコピーしました