Skip to content

Instantly share code, notes, and snippets.

@hyuki

hyuki/readme.md Secret

Last active May 31, 2025 05:21
Show Gist options
  • Select an option

  • Save hyuki/86898fe607492627b4729c24e374d50a to your computer and use it in GitHub Desktop.

Select an option

Save hyuki/86898fe607492627b4729c24e374d50a to your computer and use it in GitHub Desktop.
ブログ記事のURLを与えると要約して読み上げてくれるコマンドラインツール `url-to-narrate` の作成 by #ChatGPT

ブログ記事の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 コマンドで再生まで完了します。

終わりに

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

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


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

#!/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}")
@hyuki
Copy link
Author

hyuki commented May 31, 2025

$ url-to-narrate https://www.hyuki.com/girl/riemann
[1] Fetching HTML...
[2] Generating summary...

[3] Summary:

『数学ガール/リーマン予想』は、高校三年生の冬を舞台に、「僕」とその仲間たちがリーマン予想に挑む青春物語です。リーマン予想とは、19世紀の数学者リーマンが提唱した、素数に関する未解決問題です。この本は、大人気シリーズ『数学ガール』の第7弾で、最終巻となっています。

物語では、ゼータ関数や複素関数、リーマン面など、数学の深い世界を仲間たちと共に探求し、リーマン予想の論文に挑む姿が描かれています。数学の面白さを感じることができる一冊で、シリーズファンや数学に興味のある人にとって、挑戦と発見に満ちた内容です。ぜひ、この物語の結末を見届けてください。

[4] Converting to audio...

[5] Done! → out/summary.mp3
[6] Playing audio...

@hyuki
Copy link
Author

hyuki commented May 31, 2025

いきなり音声に行くんじゃなくて、いったん止まって「もう少し詳しい要約を表示」「このページにジャンプ」程度に分けるのでもいいかも。少し試したけど、音声である必要が感じられなくなってしまった。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment