ブログ記事のURLを与えると要約して読み上げてくれるコマンドラインツール url-to-narrate の作成 by #ChatGPT

2025年5月31日

image

はじめに

結城さんからこんな要望がありました。

技術ブログのURLを1本渡すと、その内容を1分程度に要約して、さらに読み上げてくれるツールが欲しくなりました。
NotebookLMのPodcastのように「声でお話ししてくれる」スタイルがいいですね。
コマンドラインで完結し、UIに凝らずシンプルに動いてほしいです。

このやりとりを受けて、私たちは url-to-narrate というシンプルなCLIツールを開発しました。
ブログ記事のURLを与えると、その要点を音声でナレーションしてくれる実用的なツールです。


やりたいこと

以下の機能を備えたコマンドラインツールを目指しました。

  • 技術系ブログのURLを入力として渡す
  • 記事本文を抽出し、話し言葉で1分程度に要約する
  • 生成した要約を音声に変換し、MP3ファイルとして保存する
  • macOSでは自動的に音声を再生する
  • 必要な作業はすべて1コマンドで完結させる

実現方法の概略

このツールは以下の処理ステップで構成されています:

  1. URLからHTMLを取得
  2. 記事本文を抽出(readabilityアルゴリズム使用)
  3. ChatGPTを使って話し言葉の要約を生成
  4. OpenAI TTS APIで音声ファイルを生成
  5. 音声ファイルを保存し、macOSの open で再生

処理はすべてPythonで書かれており、実行可能スクリプトとして ./url-to-narrate という名前で動作します。環境変数 OPENAI_API_KEY を利用します。


技術的詳細

使用ライブラリ

  • requests:WebページのHTML取得
  • readability-lxml:記事本文の抽出
  • openai:ChatGPT API(gpt-4o)と TTS API(tts-1)の利用

対応済みのAPI仕様

OpenAIのPythonライブラリは、v1.0.0 以降で ChatCompletion.create などの旧構文が削除されているため、
以下のような新構文に完全対応しています:

from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

response = client.chat.completions.create(...)

ファイル出力

  • 音声ファイルは out/summary.mp3 に保存されます。
  • 初回実行時に out/ ディレクトリが自動生成されます。
  • 結城さんの環境(macOS)に合わせて、open コマンドで再生まで完了します。

終わりに

このツールのおかげで、「技術記事を読む時間はないけれど、ざっと内容だけ知りたい」というニーズに、声で応えてくれるスタイルが実現しました。

日々の情報収集やインスピレーションに、きっと役立つツールになることでしょう。


結城さん、また一緒に開発しましょうね。

url-to-narrate

#!/usr/bin/env python3
import sys
import os
import requests
from readability import Document
from openai import OpenAI

# --- Step 1: 入力確認 ---
if len(sys.argv) != 2:
    print("Usage: ./url-to-narrate <URL>")
    sys.exit(1)

url = sys.argv[1]
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
    print("Error: OPENAI_API_KEY not set")
    sys.exit(1)

# --- Step 2: HTML取得と本文抽出 ---
print("[1] Fetching HTML...")
response = requests.get(url, headers={"User-Agent": "url-to-narrate/0.1"})
doc = Document(response.text)
title = doc.title()
content = doc.summary()

# --- Step 3: 要約プロンプト作成 ---
print("[2] Generating summary...")
prompt = f"""以下の記事の内容を、1分以内の長さでわかりやすく話し言葉で要約してください。
丁寧で自然な口調で、聞き手が記事の主旨を理解できるように語ってください。

タイトル: {title}

本文:
{content}
"""

# --- Step 4: OpenAI API による要約生成 ---
client = OpenAI(api_key=api_key)

chat_response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": prompt}],
    temperature=0.7
)

summary = chat_response.choices[0].message.content.strip()
print("\n[3] Summary:\n")
print(summary)

# --- Step 5: 音声合成(TTS) ---
print("\n[4] Converting to audio...")
speech_response = client.audio.speech.create(
    model="tts-1",
    voice="nova",
    input=summary
)

os.makedirs("out", exist_ok=True)
path = "out/summary.mp3"
with open(path, "wb") as f:
    f.write(speech_response.content)

print(f"\n[5] Done! → {path}")

# --- Step 6: 音声再生(macOS) ---
print("[6] Playing audio...")
os.system(f"open {path}")

original gist

(2025年6月1日)