0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

user:

次のコードのエラーを修正してください。

# ==============================================================================
# 1. 必要なライブラリのインストール
# ==============================================================================
!pip install gTTS pydub
!pip install --upgrade gtts
# ※ pydubで音声結合を行うため、システム側のffmpegをインストールします
!apt-get install -y ffmpeg

import os
from datetime import datetime
from gtts import gTTS
from pydub import AudioSegment

# ==============================================================================
# 2. 文例データ(ここだけ書き換え可能)
# ==============================================================================

# ==============================================================================
# 3. 音声ファイルの生成と結合処理
# ==============================================================================
# 出力用の一時フォルダと最終的な結合音声オブジェクトの初期化
os.makedirs("temp_audio", exist_ok=True)
combined_audio = AudioSegment.empty()

# センテンス間の無音時間を定義(ミリ秒単位)
silence_short = AudioSegment.silent(duration=800)   # 同じ文の中の言語の切り替え時の間(0.8秒)
silence_long = AudioSegment.silent(duration=1500)   # 次の番号の文へ移る時の間(1.5秒)

print("音声の生成と結合を開始します。しばらくお待ちください(約1〜2分かかります)...")

for idx, (zh_text, en_text) in enumerate(sentences_data, 1):
    # ファイル名用のパス定義
    zh_path = f"temp_audio/zh_{idx}.mp3"
    en_path = f"temp_audio/en_{idx}.mp3"

    # 1. 中国語(女性・台湾/繁体字向けの自然なアクセント 'zh-TW')の生成
    tts_zh = gTTS(text=zh_text, lang='zh-TW', slow=False)
    tts_zh.save(zh_path)

    # 2. 英語(女性・標準英語 'en')の生成
    tts_en = gTTS(text=en_text, lang='en', slow=False)
    tts_en.save(en_path)

    # pydubで音声として読み込み
    audio_zh = AudioSegment.from_mp3(zh_path)
    audio_en = AudioSegment.from_mp3(en_path)

    # 指示通りの順序で結合: 文X(中) -> 文X(英) -> 文X(中) -> 文X(英)
    sentence_block = (
        audio_zh + silence_short +
        audio_en + silence_short +
        audio_zh + silence_short +
        audio_en
    )

    # 全体の音声オブジェクトに、長めの無音(文の区切り)を挟んで追加
    if idx == 1:
        combined_audio += sentence_block
    else:
        combined_audio += silence_long + sentence_block

    if idx % 10 == 0:
        print(f"進捗: {idx} / 80 文が完了しました...")

# ==============================================================================
# 4. 最終ファイルの保存とダウンロード
# ==============================================================================
date_str = datetime.now().strftime("%Y%m%d")
output_filename = f"{date_str}_中国語・英語リピート学習音声80選.mp3"
print("すべての音声を結合しています。ファイルをエクスポート中...")

# 高音質な128kbpsのmp3として保存
combined_audio.export(output_filename, format="mp3", bitrate="128k")
print(f"保存が完了しました: {output_filename}")

# Google ColabからPCへ自動ダウンロードを走らせる
from google.colab import files
files.download(output_filename)
print("ダウンロードが開始されました。")

エラーメッセージは次の通りです。

音声の生成と結合を開始します。しばらくお待ちください(約1〜2分かかります)...
WARNING:gtts.lang:'zh-TW' has been deprecated, falling back to 'zh-TW'. This fallback will be removed in a future version.
WARNING:gtts.lang:'zh-TW' has been deprecated, falling back to 'zh-TW'. This fallback will be removed in a future version.
---------------------------------------------------------------------------
gTTSError                                 Traceback (most recent call last)
/tmp/ipykernel_441/774351411.py in <cell line: 0>()
     40     # 2. 英語(女性・標準英語 'en')の生成
     41     tts_en = gTTS(text=en_text, lang='en', slow=False)
---> 42     tts_en.save(en_path)
     43 
     44     # pydubで音声として読み込み

2 frames
/usr/local/lib/python3.12/dist-packages/gtts/tts.py in stream(self)
    298                         # Request successful, good response,
    299                         # no audio stream in response
--> 300                         raise gTTSError(tts=self, response=r)
    301             log.debug("part-%i created", idx)
    302 

gTTSError: 200 (OK) from TTS API. Probable cause: Unknown

ChatGPT:

はい。今回のエラーは、pydubやffmpegではなく、gTTS → Google TTS API の通信部分で起きています。

特に、

gTTSError: 200 (OK) from TTS API. Probable cause: Unknown

というのがポイントです。HTTPとしては 200 OK が返っているのに、gTTSが期待した音声データとして解釈できなかった状態です。

また、もう一つ修正した方がいい点があります。

lang='zh-TW'

について、

WARNING:gtts.lang:'zh-TW' has been deprecated

が出ています。現在のgTTSでは台湾中国語の扱いが変わっているため、ここも整理した方が安全です。

おすすめの修正版

以下のように、gTTSのバージョンを固定し、TTS生成をリトライする仕組みを入れるのがおすすめです。

さらに、同じ中国語・英語音声を2回ずつ生成する必要はありません。現在のコードは、

中国語 → 英語 → 同じ中国語 → 同じ英語

なので、最初に生成したMP3をそのまま2回使えばOKです。

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

!pip install -q "gTTS==2.5.4" pydub
!apt-get install -y ffmpeg

import os
import time
import shutil
from datetime import datetime

from gtts import gTTS
from pydub import AudioSegment

# ==============================================================================
# 2. 文例データ
# ==============================================================================
# ここに sentences_data を設定してください
#
# 例:
#
# sentences_data = [
#     ("你好,今天我們來學習經濟學。", "Hello, today we will study economics."),
#     ("什麼是國內生產總值?", "What is gross domestic product?"),
# ]

# ==============================================================================
# 3. TTS音声を安全に生成する関数
# ==============================================================================

def create_tts(text, lang, output_path, max_retries=5):
    """
    gTTSで音声を生成する。
    Google TTS APIの一時的なエラーに備えてリトライする。
    """

    for attempt in range(1, max_retries + 1):

        try:
            print(
                f"  音声生成: {lang} "
                f"(試行 {attempt}/{max_retries})"
            )

            tts = gTTS(
                text=text,
                lang=lang,
                slow=False
            )

            tts.save(output_path)

            # ファイルが正常に生成されたか確認
            if os.path.exists(output_path):
                file_size = os.path.getsize(output_path)

                if file_size > 1000:
                    print(f"  OK: {output_path} ({file_size:,} bytes)")
                    return True

            raise RuntimeError("音声ファイルが正常に生成されませんでした。")

        except Exception as e:

            print(f"  エラー: {e}")

            if os.path.exists(output_path):
                os.remove(output_path)

            if attempt < max_retries:
                wait_time = attempt * 3

                print(
                    f"  {wait_time}秒待ってから再試行します..."
                )

                time.sleep(wait_time)

            else:
                print(
                    f"  【失敗】{lang}: "
                    f"{max_retries}回試行しましたが失敗しました。"
                )

                return False

    return False


# ==============================================================================
# 4. 一時フォルダの準備
# ==============================================================================

temp_dir = "temp_audio"

# 前回の途中生成ファイルを削除
if os.path.exists(temp_dir):
    shutil.rmtree(temp_dir)

os.makedirs(temp_dir, exist_ok=True)

combined_audio = AudioSegment.empty()

# ==============================================================================
# 5. 無音時間
# ==============================================================================

silence_short = AudioSegment.silent(
    duration=800
)

silence_long = AudioSegment.silent(
    duration=1500
)

# ==============================================================================
# 6. 音声生成と結合
# ==============================================================================

print(
    "音声の生成と結合を開始します。"
    "しばらくお待ちください..."
)

total = len(sentences_data)

for idx, (zh_text, en_text) in enumerate(sentences_data, 1):

    print(f"\n===== {idx} / {total} =====")

    zh_path = f"{temp_dir}/zh_{idx}.mp3"
    en_path = f"{temp_dir}/en_{idx}.mp3"

    # --------------------------------------------------------------------------
    # 中国語
    # --------------------------------------------------------------------------
    #
    # 台湾向け中国語。
    # zh-TW の警告が出る場合は、gTTSの仕様変更の影響を受ける可能性があります。
    #
    # まず zh-TW で試します。
    # --------------------------------------------------------------------------

    zh_success = create_tts(
        zh_text,
        "zh-TW",
        zh_path
    )

    if not zh_success:
        raise RuntimeError(
            f"{idx}番目の中国語音声の生成に失敗しました。"
        )

    # --------------------------------------------------------------------------
    # 英語
    # --------------------------------------------------------------------------

    en_success = create_tts(
        en_text,
        "en",
        en_path
    )

    if not en_success:
        raise RuntimeError(
            f"{idx}番目の英語音声の生成に失敗しました。"
        )

    # --------------------------------------------------------------------------
    # MP3を読み込む
    # --------------------------------------------------------------------------

    audio_zh = AudioSegment.from_mp3(zh_path)
    audio_en = AudioSegment.from_mp3(en_path)

    # --------------------------------------------------------------------------
    # 中国語 → 英語 → 中国語 → 英語
    #
    # 既に生成した音声を再利用するので、
    # 同じ音声を2回生成する必要はありません。
    # --------------------------------------------------------------------------

    sentence_block = (
        audio_zh
        + silence_short
        + audio_en
        + silence_short
        + audio_zh
        + silence_short
        + audio_en
    )

    # --------------------------------------------------------------------------
    # 全体に追加
    # --------------------------------------------------------------------------

    if idx == 1:
        combined_audio += sentence_block
    else:
        combined_audio += (
            silence_long
            + sentence_block
        )

    if idx % 10 == 0 or idx == total:
        print(
            f"\n進捗: {idx} / {total} 文が完了しました。"
        )


# ==============================================================================
# 7. MP3として保存
# ==============================================================================

date_str = datetime.now().strftime("%Y%m%d")

output_filename = (
    f"{date_str}_中国語・英語リピート学習音声"
    f"{total}選.mp3"
)

print("\nすべての音声を結合しています。")
print("MP3ファイルをエクスポート中...")

combined_audio.export(
    output_filename,
    format="mp3",
    bitrate="128k"
)

print(
    f"\n保存が完了しました:\n"
    f"{output_filename}"
)

# ==============================================================================
# 8. Google Colabからダウンロード
# ==============================================================================

from google.colab import files

files.download(output_filename)

print("ダウンロードを開始しました。")

今回のエラーで重要なところ

実は、エラーが出た場所はここです。

tts_en = gTTS(text=en_text, lang='en', slow=False)
tts_en.save(en_path)

つまり、

中国語TTS
    ↓
成功
    ↓
英語TTS
    ↓
Google TTS API
    ↓
200 OK
    ↓
gTTSが音声として認識できない
    ↓
gTTSError

という流れです。

ffmpegは今回のエラー原因ではありません。

pydubがMP3を読み込むところまで到達していません。


もう一つ重要な改善点

元コードでは毎回、

audio_zh
audio_en
audio_zh
audio_en

を作るために、実際には中国語・英語を2回ずつGoogle TTSに送る設計にしているように見えます

しかし、

audio_zh + silence + audio_en + silence + audio_zh + silence + audio_en

とすれば、1回生成した音声データを再利用できます。

これはかなり重要です。

80文なら、

  • 元の設計:最大 320回 TTS生成
  • 修正版:160回 TTS生成

になります。

Google TTS APIへのアクセス回数を半分にできるので、今回のような一時的なAPIエラーにも遭遇しにくくなります。


さらに安定させるなら

80文を一気に実行する場合、私はもう一段改善して、

「生成済みMP3は残して、途中でColabがエラーになっても途中から再開できる」

方式にするのをおすすめします。

例えば、

temp_audio/
 ├── zh_1.mp3
 ├── en_1.mp3
 ├── zh_2.mp3
 ├── en_2.mp3
 ├── ...
 ├── zh_80.mp3
 └── en_80.mp3

としておけば、35番でGoogle TTSが落ちても、1~34番をもう一度生成する必要がありません

80文の中国語・英語リピート教材をColabで作る用途なら、この「途中再開可能版」がかなり実用的です。

user:

ありがとうございました。今回の内容をブログに記録したいので、タイトルを考えてください。

ChatGPT:

もちろんです。今回の内容なら、**「Colab × gTTS × pydubで音声教材を自動生成する」**という技術要素と、gTTSのAPIエラーをどう直したかの両方がタイトルに入ると、後から検索もしやすいです。

技術ブログらしいタイトル

  1. Google ColabでgTTSの「200 OKなのにgTTSError」が発生したときの原因と対処法
  2. gTTSで音声教材を自動生成する ― Google TTS APIエラーとリトライ処理への対応
  3. Python・gTTS・pydubで中国語&英語のリピート学習音声を自動生成する
  4. Google Colab × gTTS × pydubで音声教材を作る ― gTTSErrorの原因と改善方法
  5. gTTSの「200 (OK) from TTS API」でハマった話 ― Google Colabで安定して音声生成する方法

今回のブログ内容に特に合いそうなもの

「Google Colab × gTTS × pydubで音声教材を自動生成する ― gTTSError『200 OK』の原因とリトライ処理」

これが一番おすすめです。

「何を作った記事なのか」→ 音声教材の自動生成
「何を使ったのか」→ Google Colab / gTTS / pydub
「どんな問題を解決したのか」→ gTTSError 200 OK

が全部タイトルから分かります。

少しシリーズ感を出すなら、

「Pythonで音声教材を作る!Google Colab × gTTS × pydub ― gTTSError『200 OK』を攻略する」

もかなりブログ向きです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?