プログラミングを頑張る土木系専攻大学院生のブログ

主にプログラミングについて開発備忘録的な形で投稿しています。

YouTubeAPIで、再生リストからデータを一覧取得してみた  FastAPI × React アプリ開発【レシピ提案アプリ⑥】

こんにちは、今回のテーマは YouTubeの再生リストからレシピ動画を一括取得」 です!

前回の記事 ↓ では、Djangoテンプレートでレシピを一覧表示する機能を実装しましたが、今回は レシピを一括追加するための自動化ツール を作っていきます。
今回から使用するフレームワークや言語が変わります。

フロントエンド HTML/CSS  &  JavaScript → React
バックエンド Django → FastAPI 

 

siip.hateblo.jp


🎯 今回やること

以下のような処理を実装して、YouTubeの再生リストからレシピ情報(タイトル・説明・URL・サムネイル)をまとめて取得できるようにします。


📌 実装の目的

  • レシピを1件ずつ手動で入力するのは大変…

  • 自分はYouTube再生リストに料理動画をためてる

  • 再生リストURLを入れるだけで自動登録できたら最高!

 

実装後はこんな感じで、再生リストの動画すべてを一括で追加できました




🛠️FastAPI× Reactでの 実装の流れ(全体像)

  1. フロントエンドに再生リストURLの入力フォームを用意

  2. **バックエンド(FastAPI)**がYouTube Data APIを使って情報を取得

  3. 動画情報をフロントエンドに返却

  4. 必要に応じて編集・一括登録ができるUIを用意


🔑 YouTube Data API v3の準備

まずは、APIを使えるように Google Cloud Consoleで設定します。

APIキーの取得手順

  1. Google Cloud Console にアクセス

  2. 左サイドバー「APIとサービス」 →「ライブラリ」→「YouTube Data API v3」を選択して有効化

  3. 「認証情報」→「認証情報を作成」→「APIキー」を発行

  4. 発行されたキーに制限をかける(重要)


🔐 APIキーのセキュリティ設定

  • .envで管理し、GitHubなどに絶対公開しない

  • API制限を設定(「YouTube Data API v3」のみに許可)

  • IPアドレス制限(FastAPIなどサーバー用途なら)をかける

 


🧪 バックエンドの実装(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を使う際は、セキュリティ設定キーの非公開管理を徹底しましょう。

 

レシピ提案アプリ開発記事一覧はこちらから ↓

siip.hateblo.jp