こんにちは、今回のテーマは 「YouTubeの再生リストからレシピ動画を一括取得」 です!
前回の記事 ↓ では、Djangoテンプレートでレシピを一覧表示する機能を実装しましたが、今回は レシピを一括追加するための自動化ツール を作っていきます。
今回から使用するフレームワークや言語が変わります。
フロントエンド HTML/CSS & JavaScript → React
バックエンド Django → FastAPI
🎯 今回やること
以下のような処理を実装して、YouTubeの再生リストからレシピ情報(タイトル・説明・URL・サムネイル)をまとめて取得できるようにします。
📌 実装の目的
-
レシピを1件ずつ手動で入力するのは大変…
-
自分はYouTube再生リストに料理動画をためてる
-
再生リストURLを入れるだけで自動登録できたら最高!
実装後はこんな感じで、再生リストの動画すべてを一括で追加できました
🛠️FastAPI× Reactでの 実装の流れ(全体像)
-
フロントエンドに再生リストURLの入力フォームを用意
-
動画情報をフロントエンドに返却
-
必要に応じて編集・一括登録ができるUIを用意
🔑 YouTube Data API v3の準備
まずは、APIを使えるように Google Cloud Consoleで設定します。
✅ APIキーの取得手順
-
Google Cloud Console にアクセス
-
左サイドバー「APIとサービス」 →「ライブラリ」→「YouTube Data API v3」を選択して有効化
-
「認証情報」→「認証情報を作成」→「APIキー」を発行
-
発行されたキーに制限をかける(重要)
🔐 APIキーのセキュリティ設定
🧪 バックエンドの実装(FastAPI)
APIキーを .env
に書いて、FastAPIから読み込みます。
✅ .env の例
YOUTUBE_API_KEY=ここに取得したキーを貼る
✅ FastAPI側のコード
# main.py
import os, requests, re
from fastapi import FastAPI, Body
from dotenv import load_dotenv
load_dotenv()
YOUTUBE_API_KEY = os.getenv("YOUTUBE_API_KEY")
app = FastAPI()
@app.post("/api/youtube/playlist")
def fetch_youtube_playlist(playlist_url: str = Body(..., embed=True)):
match = re.search(r"[?&]list=([\w-]+)", playlist_url)
if not match:
return {"error": "Invalid playlist URL"}
playlist_id = match.group(1)
items = []
nextPageToken = None
while True:
params = {
"part": "snippet",
"playlistId": playlist_id,
"maxResults": 50,
"key": YOUTUBE_API_KEY,
}
if nextPageToken:
params["pageToken"] = nextPageToken
r = requests.get("https://www.googleapis.com/youtube/v3/playlistItems", params=params)
data = r.json()
items.extend(data.get("items", []))
nextPageToken = data.get("nextPageToken")
if not nextPageToken:
break
result = []
for item in items:
snippet = item["snippet"]
result.append({
"videoId": snippet["resourceId"]["videoId"],
"title": snippet["title"],
"description": snippet.get("description", ""),
"thumbnail": snippet["thumbnails"]["default"]["url"],
"url": f"https://www.youtube.com/watch?v={snippet['resourceId']['videoId']}"
})
return result
💻 フロントエンドの実装(React)
API呼び出し関数(api.ts)
export async function fetchYoutubePlaylist(playlistUrl: string): Promise<any[]> {
const res = await fetch("/api/youtube/playlist", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ playlist_url: playlistUrl }),
});
return await res.json();
}
プレイリスト追加画面(PlaylistBulkAdd.tsx)
import { useState } from "react";
import { fetchYoutubePlaylist } from "./api";
function PlaylistBulkAdd() {
const [playlistUrl, setPlaylistUrl] = useState("");
const [videos, setVideos] = useState<any[]>([]);
const [error, setError] = useState("");
const fetchPlaylist = async () => {
try {
const data = await fetchYoutubePlaylist(playlistUrl);
setVideos(data);
} catch {
setError("取得に失敗しました");
}
};
return (
<div className="container mt-5">
<h2>YouTube再生リスト一括追加</h2>
<input
type="text"
className="form-control mb-3"
placeholder="再生リストのURLを入力"
value={playlistUrl}
onChange={(e) => setPlaylistUrl(e.target.value)}
/>
<button onClick={fetchPlaylist} className="btn btn-primary">取得</button>
{error && <p className="text-danger">{error}</p>}
<div className="mt-4">
{videos.map((v, i) => (
<div key={i} className="card mb-3">
<div className="row g-0">
<div className="col-md-2">
<img src={v.thumbnail} className="img-fluid" alt="thumbnail" />
</div>
<div className="col-md-10">
<div className="card-body">
<h5 className="card-title">{v.title}</h5>
<p className="card-text">{v.description}</p>
<a href={v.url} target="_blank" className="btn btn-sm btn-outline-secondary">YouTubeで見る</a>
</div>
</div>
</div>
</div>
))}
</div>
</div>
);
}
export default PlaylistBulkAdd;
⚠️ 注意点とポイントまとめ
項目 | 内容 |
---|---|
APIキーの管理 | .env + .gitignore で非公開にする |
APIの制限 | API/IP制限を必ず設定 |
再生リストの種類 | 「非公開」「限定公開」は取得できません |
クォータ消費 | 利用頻度に注意。開発中は取得数を制限する |
フォールトトレランス | 失敗時のエラーハンドリング必須 |
✅ まとめ
YouTubeの再生リストからレシピを一括取得する機能を実装しました!
-
一から入力する手間が大幅に削減され、かなり時短になります。
-
APIを使う際は、セキュリティ設定とキーの非公開管理を徹底しましょう。
レシピ提案アプリ開発記事一覧はこちらから ↓