YouTubeトレンドのサムネイルを一括取得するツールの作り方 — Data API v3で集めて保存する

アプリの作り方

YouTubeトレンドのサムネイルを一括取得するツールの作り方 — Data API v3で集めて保存する

参考にしたい動画のサムネイルだけを、まとめて手元に集めたい。1枚ずつ右クリックで保存するのは骨が折れるし、そもそも高解像度版がどこにあるのか分かりにくい。トレンド動画を一覧して、気になったサムネをまとめて取得できるツールを、フロントとバックに分けて作る。

目次
  1. このガイドで作るもの
  2. 集めたサムネは、こう研究に使う
  3. 設計の起点:「一覧」はフロント、「保存」はバックに分ける
  4. 作り方の要点
    1. 1. Data API v3 でトレンド・検索・統計を取る
    2. 2. サムネは動画IDから予測できるURLで取る
    3. 3. 保存はバックエンドでストリーム書き出し
  5. つまずきと対策
  6. 次に足すなら、この2つ
  7. 使用ライブラリと順守メモ(重要)
  8. まとめ

このガイドで作るもの

YouTube Data API v3 で日本のトレンド動画を一覧表示し、カテゴリ絞り込み・キーワード検索・地域切り替え(JP/US 等)をしながら、選んだ動画のサムネイルを一括ダウンロードするダッシュボード。一覧・検索は React(Vite) フロント、画像の取得・保存は小さな Express サーバーが担う。

完成したダッシュボードの画面。左にジャンル、中央にトレンド一覧、上部に検索とフィルタ
完成形。左にジャンル、中央にトレンド一覧、上部に検索・フィルタを備えたダッシュボード。各サムネイル画像は権利保護のためぼかしています(以下の画面も同様)。

構成は素直に分かれている。API呼び出しは src/services/youtube.js、画面は src/App.jsxsrc/components/VideoCard.jsx、保存サーバーは server.js。フロント(一覧)とバック(保存)が別プロセスで、間を動画IDで繋ぐだけだ。

開発中は、この2プロセスを両方起動して使う。ターミナルを2枚開き、片方でフロント(Vite の開発サーバー)、もう片方で server.js(Express)を動かす。「画面は出るのに保存だけ失敗する」ときは、たいてい Express 側を起動し忘れている。2プロセス構成の定番のつまずきなので、先に知っておくと慌てない。毎回2枚のターミナルを開くのが面倒になったら、両方を一度に起動するスクリプトをAIに書いてもらえば1コマンドにまとまる。この「面倒になったら自動化」の順番なら、仕組みを理解してから楽になれる。

集めたサムネは、こう研究に使う

ツールの前に、集めた後の話を少し。例えば「ゲーム実況」ジャンルのトレンド上位50本のサムネをフォルダに並べると、1枚ずつ見ていたときには気づかない共通項が浮かぶ。文字は何文字まで載せているか、顔のアップは全体の何割か、背景は暗色系と明色系のどちらが多いか。エクスプローラーの大アイコン表示で俯瞰するだけで、そのジャンルの「勝ちパターンの土俵」が見えてくる。

比較の軸を先に決めておくと、収集が調査になる。「同ジャンルで登録者が1桁違う2チャンネルのサムネを並べる」「同じ投稿者の半年前と今を並べる」。差分を見る問いを持って集めれば、50枚のフォルダは立派な研究資料になる。逆に、問い無しで集めた100枚は、眺めて終わる壁紙になりがちだ。集める前に「何と何を比べたいか」を1行メモしておく。それだけで、このツールの元は取れる。念のため繰り返すと、用途は自分の企画の参考(私的利用)までだ。

設計の起点:「一覧」はフロント、「保存」はバックに分ける

ブラウザだけでは、外部ドメインの画像をローカルへ自動保存するのは扱いにくい(クロスオリジン制約・保存ダイアログ)。そこで一覧・検索は React フロントサムネの取得・ファイル書き出しは Express バックに分けた。フロントは動画IDを渡すだけ、サーバーが画像を取りに行ってディスクに書く。役割を割ると、それぞれを単純に保てる。

サイドバーのジャンルでゲームを選び、一覧を絞り込んだ画面
サイドバーのジャンル(例:ゲーム)で一覧を絞り込む。この「一覧・検索」を担うのが React(Vite) フロント側だ。

作り方の要点

1. Data API v3 でトレンド・検索・統計を取る

APIキーは .envVITE_YOUTUBE_API_KEY)に置き、コードへ直書きしない。トレンドは chart=mostPopularregionCode で取る。

APIキーの取得は無料だ。Google Cloud Console でプロジェクトを作り、「YouTube Data API v3」を有効化して、認証情報からキーを発行する。クレジットカードの登録は不要で、既定の無料クォータ(1日10,000ユニット)の範囲ならお金は一切かからない。発行したキーを .env に書けば準備完了になる。

// src/services/youtube.js
import axios from 'axios';

const youtubeApi = axios.create({
  baseURL: 'https://www.googleapis.com/youtube/v3',
  params: { key: import.meta.env.VITE_YOUTUBE_API_KEY },
});

export const getTrendingVideos = async (categoryId = '0', maxResults = 50, region = 'JP') => {
  const res = await youtubeApi.get('/videos', {
    params: {
      part: 'snippet,statistics',
      chart: 'mostPopular',
      videoCategoryId: categoryId === '0' ? undefined : categoryId,
      maxResults,
      regionCode: region || 'US',   // mostPopular は regionCode 必須
    },
  });
  return res.data.items;
};

地域切り替え(regionCode)は、単なるおまけ機能ではない。JP と US を切り替えて同じジャンルのトレンドを見比べると、サムネの作法が地域でまるで違うことが分かる。文字量・色使い・顔の写し方。海外の型を早めに取り入れる参考にも、日本向けのローカライズの確認にも、この1パラメータが効く。

フィルタパネルで地域・期間・並び替えを切り替える画面
フィルタパネルで地域(日本/世界)・期間・並び替えを切り替える。regionCode ひとつで一覧の顔つきが変わる。

検索は2段構えにする。/search は動画IDしか返さないので、まずIDを集め、/videos でまとめて統計を取る。search は1回あたりのコストが高いので、まとめて1回で引くのがクォータ節約の要だ。

キーワード「料理 レシピ」で検索した結果一覧の画面
キーワード検索の結果。search で集めた動画IDを videos でまとめて引く2段構えで取得している。
export const searchVideos = async (query, { region = 'JP' } = {}, maxResults = 50) => {
  // 1) search で動画IDを集める
  const search = await youtubeApi.get('/search', {
    params: {
      part: 'snippet', q: query, type: 'video',
      maxResults, regionCode: region || 'US',
      relevanceLanguage: region === 'JP' ? 'ja' : undefined,
    },
  });
  const items = search.data.items;
  if (items.length === 0) return [];

  const ids = items.map(i => i.id.videoId).join(',');
  // 2) videos でまとめて統計を取る(search を何度も叩かない=クォータ節約)
  const details = await youtubeApi.get('/videos', {
    params: { part: 'snippet,statistics', id: ids },
  });
  return details.data.items;
};

2. サムネは動画IDから予測できるURLで取る

YouTube のサムネイルは動画IDから URL が決まる。最高解像度は https://img.youtube.com/vi/<videoId>/maxresdefault.jpg だ。「IDさえあればサムネのURLは組み立てられる」と知っているだけで実装は一気に楽になる。なお maxresdefault が存在しない動画もあるため、無い場合は hqdefault.jpg 等へフォールバックする想定にしておく。

解像度違いも同じ規則で並んでいる。用途で使い分けられるよう、URLの“品揃え”を一覧にしておく。

ファイル名 解像度 用途の目安
default.jpg 120×90 最小。ほぼ確実に存在する
mqdefault.jpg 320×180 一覧表示に十分・軽い
hqdefault.jpg 480×360 フォールバックの定番
sddefault.jpg 640×480 無い動画もある
maxresdefault.jpg 1280×720 保存用の本命。無い動画もある

このアプリでは、一覧のカード表示は軽いサムネ、保存だけ maxresdefault、と役割を分けている。一覧まで最高解像度で並べると、50件の画面表示だけで無駄に重くなるからだ。

細かい癖も1つ。hqdefault(480×360)は4:3の枠なので、16:9のサムネは上下に黒帯が付いた形で返ってくる。参考閲覧なら気にならないが、黒帯を避けたい一覧表示には16:9のまま返る mqdefault(320×180)が向いている。

3. 保存はバックエンドでストリーム書き出し

サーバー(Express + axios)で画像をストリーム取得し、メモリに溜めず createWriteStream へそのまま流す。大きな画像でもメモリを食わない。

複数のサムネを選択し、下部に一括ダウンロードのバーが表示された画面
フロントでカードを選ぶと、下部に一括ダウンロードのバーが出る(各カードは 4K/SD/HQ の解像度も選べる)。フロントは選んだ動画IDをバックへ渡すだけだ。
// server.js — サムネを取得してディスクへ保存
app.post('/api/download', async (req, res) => {
  const { videoId, title } = req.body;
  const url = `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`;

  // ファイル名を安全化(OS禁止文字を置換+長さ制限)+IDで一意化
  const cleanTitle = title.replace(/[\\/:*?"<>|]/g, '_').substring(0, 50);
  const filePath = path.join(SAVE_DIR, `${cleanTitle}_${videoId}.jpg`);

  try {
    const response = await axios({ url, method: 'GET', responseType: 'stream' });
    const writer = fs.createWriteStream(filePath);
    response.data.pipe(writer);
    writer.on('finish', () => res.json({ success: true, path: filePath }));
    writer.on('error',  () => res.status(500).json({ error: 'ファイルの書き込みに失敗しました' }));
  } catch (e) {
    res.status(500).json({ error: '画像の取得に失敗しました' });
  }
});

ファイル名の作り方にも小さな配慮がある。タイトルからOSの禁止文字を置換し、先頭50文字に制限するのは、Windows のパス長制限を踏みにくくするためだ。末尾に動画IDを付けているので、同じ動画を2回保存しても同名になって上書きされ、重複ファイルが溜まらない。似たタイトルの動画同士がぶつかることもない。ファイル名の設計は書いた瞬間より、100枚溜まった後に効いてくる類の仕事だ。

ダウンロード完了のトーストが表示された画面
「ダウンロード完了」。Express バックが img.youtube.com の予測URLから取得し、image/ フォルダへストリームで書き出す。

つまずきと対策

  • クォータ枯渇search は高コスト。part を絞り、検索後は /videos で一括取得して呼び出し回数を減らす。
  • maxres が無い動画:404 になることがある。hqdefault 等へフォールバックする。
  • なぜブラウザだけで完結しないか:クロスオリジン画像のローカル保存はブラウザの制約が強い。だからこそ保存はサーバー側に置く。

次に足すなら、この2つ

骨格が動いたら、育て先の候補も置いておく。どちらも今の構成(フロント一覧+バック保存)のまま足せる。

  • チャンネル単位のまとめ取り … いまはトレンド・検索起点だが、「このチャンネルの直近50本」を起点にできると、特定の投稿者の変遷を追う研究がしやすくなる。channels → アップロード一覧 → videos と、APIの呼び順が1段増えるだけだ。
  • 保存済みの管理 … 同じ調査を何度かやると、どれを保存済みか分からなくなる。保存時に動画ID・タイトル・保存日をCSVに1行追記しておけば、フォルダが増えても台帳で追える。

逆に、足さない方がいい方向もある。取得の自動化・定期巡回は、私的な参考収集の範囲を超えやすく、APIのクォータも規約も窮屈になる。手で選んで、選んだ分だけ保存する。この道具はその範囲が一番心地よい。

使用ライブラリと順守メモ(重要)

このツールは「自分が参考にするための私的な収集」を前提に作る。公開・配布する成果物ではない。以下を必ず守る。

  • 商標:YouTube / Google は各社の商標。本ガイドは説明のための記述的な言及に留め、提携・公認を示唆しない。
  • API利用規約:YouTube API Services の利用規約・デベロッパーポリシーの範囲内で利用する。APIキーは読者が各自取得し、クォータと規約(取得データの扱い等)を順守する。
  • 著作権:取得する動画のメタデータ・サムネイル画像は各投稿者(権利者)の著作物。本ツールでの取得は私的利用(手元での参考閲覧)の範囲に限る。
  • 再配布の禁止:取得したサムネを再配布・再アップロード・自作物への転用・商用利用しない。あくまで参考資料として手元に置くまで。
  • YouTube 利用規約:YouTube の利用規約は、サービスが提供する手段以外でのコンテンツのダウンロードを原則制限している。img.youtube.com の直接取得は Data API の枠外のアクセスであり、本手法の利用は各自の責任で現行規約を確認のうえ、私的な参考閲覧の範囲に留めること。
  • アプリ自体の依存(React / Vite / Express / axios / cors)はいずれも MIT ライセンス。

まとめ

勘所は3つ。① 一覧はフロント・保存はバックに分ける② サムネは動画IDから予測URLで取れる③ 取得はストリームで書き出す。同じ発想は他のメディア収集ツールにもそのまま効く。「一覧して選ぶ」と「まとめて保存する」を分ける形は、画像に限らず何を集めるときも使い回せる。ただし集めた画像は他者の著作物なので、私的な参考の範囲を超えないことが、長く安全に使う条件だ。


👉 関連: 開発実例:YouTubeサムネイル取得YouTube関連の開発実例ほかのアプリの作り方

Grokで回すXメディア生成パイプラインの作り方 — 検索から画像・音声・動画まで自動化するGrokで回すXメディア生成パイプラインの作り方 — 検索から画像・音声・動画まで自動化する前のページ

Whisper文字起こしアプリの作り方 — 録音から Word 出力までローカルで完結させる次のページWhisper文字起こしアプリの作り方 — 録音から Word 出力までローカルで完結させる

ピックアップ記事

  1. New Eden Intelligence Hub の作り方 — EVE Onl…

  2. 競艇予想AIの作り方 — LightGBMで「当たる順位」を学習させる実装ガイド…

  3. Claude Mission Control の作り方 — Tauri+Pyth…

  4. 高精度OCRデスクトップアプリの作り方 — PaddleOCR-VLとPyIns…

関連記事

  1. large-v3-turbo は large-v3 の代わりになるか。日本語で測って、速度の測り方を2回間違えた記録
  2. Whisper文字起こしアプリの作り方 — 録音から Word 出力までローカルで完結させる

    アプリの作り方

    Whisper文字起こしアプリの作り方 — 録音から Word 出力までローカルで完結させる

    録音/音声ファイルをローカルWhisperで文字起こししWord出力す…

  3. Grokで回すXメディア生成パイプラインの作り方 — 検索から画像・音声・動画まで自動化する
  4. 高精度OCRデスクトップアプリの作り方 — PaddleOCR-VLとPyInstaller凍結の実践

    アプリの作り方

    高精度OCRデスクトップアプリの作り方 — PaddleOCR-VLとPyInstaller凍結の実…

    PaddleOCR-VLで撮影→解析→翻訳まで完結するOCRアプリの作…

  5. 背景除去アプリの作り方 — PyQt6+ONNX(BiRefNet)でローカル完結の透過ツールを作る

注目

AIで、ここまで作れる

AIで作った2D RPGを、ブラウザでそのまま遊べます。その「作り方=最後まで完成させる進め方」も実例つきで公開中。

▶ ゲームを遊ぶやり方を読む

PR

ロリポップ!レンタルサーバー(PR)

レンタルサーバ:ロリポップ!(本サイトの稼働環境・PR)

  1. Claude Code v2.1.214 で allow ルールの dir/** が「効かなくなる」— 棚卸しスクリプトと書き換えの判断基準

    AI・テック動向

    Claude Code v2.1.214 で allow ルールの dir/**…
  2. Kimi K3 のウェイト公開 594GB — 「オープンウェイト=手元で動く」が成り立たない規模をコストで見る

    AI・テック動向

    Kimi K3 のウェイト公開 594GB — 「オープンウェイト=手元で動く」…
  3. 都市開発シミュを作る③|需要・成長・経済の決定論シミュレーション(MVP完成)【Aurum City制作】

    アプリの作り方

    都市開発シミュを作る③|需要・成長・経済の決定論シミュレーション(MVP完成)【…
  4. AIでゲーム素材を量産する⑤|生成→透過切り出し(BiRefNet)→配置のパイプライン【Archipelago Saga制作】

    アプリの作り方

    AIでゲーム素材を量産する⑤|生成→透過切り出し(BiRefNet)→配置のパイ…
  5. 背景除去アプリを作る⑤|5スライダー後処理+消去/復元ブラシ+20段Undoで「自動9割・手で1割」を仕上げる【背景除去Studio制作】

    アプリの作り方

    背景除去アプリを作る⑤|5スライダー後処理+消去/復元ブラシ+20段Undoで「…
PAGE TOP

TAG CLOUD

ドラッグで回転・クリックでそのタグの記事一覧へ