YouTubeデータ取得 データ取得

YouTube Data APIでチャンネルの動画一覧をCSV保存する方法|uploads playlistを使う手順

2023年12月27日

この記事では、YouTube Data API v3を使って、指定したチャンネルの動画一覧を取得し、CSVに保存する方法をまとめます。

チャンネルの動画一覧を取得する方法はいくつかありますが、この記事では channels.list でアップロード用再生リストIDを取得し、playlistItems.list で動画一覧をたどる方法を使います。検索APIを使うより意図が明確で、クォータも抑えやすいです。

取得対象は、YouTube Data APIで取得できる公開動画のメタデータです。非公開動画や削除済み動画、権限のない情報は通常取得できません。実行時はYouTube API Servicesの利用規約やクォータ制限を確認し、必要以上に繰り返し取得しないようにします。

この記事でできること

  • チャンネルIDからアップロード動画一覧を取得する
  • nextPageToken を使って複数ページを取得する
  • 動画ID、タイトル、公開日、説明文などをCSVに保存する
  • 必要に応じて再生数などの統計情報を追加する
  • search.list との使い分けを理解する

全体の流れ

  1. APIキーを読み込む
  2. channels.list でチャンネルの uploads playlist IDを取得する
  3. playlistItems.list でアップロード動画一覧を取得する
  4. ページングしながら全件を集める
  5. CSVに保存する

公式ドキュメントでも、チャンネルにアップロードされた動画を取得する例では、チャンネルの uploads playlistを取得してから playlistItems.list を呼び出す流れが紹介されています。

PlaylistItems: list - YouTube Data API

search.listを使わない理由

search.list でもチャンネルIDを指定して動画を検索できます。ただし、検索APIは検索結果として返るため、チャンネルの投稿一覧を安定して順番に集めたい用途では、uploads playlistを使うほうが素直です。

方法向いている用途補足
playlistItems.list特定チャンネルの投稿動画一覧uploads playlistをたどる。クォータコストは1
search.listキーワードや条件で探す検索用途。クォータコストが高い

Googleのクォータ表では、playlistItems.list は1ユニット、search.list は100ユニットです。単純に投稿動画一覧を作るだけなら、検索APIを使わないほうが効率的です。

Quota Calculator - YouTube Data API

事前準備

APIキーは .env に保存します。

YOUTUBE_API_KEY=ここにAPIキーを書く

必要なライブラリをインストールします。

pip install requests python-dotenv

APIキーの管理方法は、次の記事で整理しています。

APIキーを外部ファイルで管理する方法|.envとconfig.iniの使い分け

チャンネルIDからuploads playlist IDを取得する

まず、channels.list を使って、チャンネルのアップロード動画が入っている再生リストIDを取得します。

import os

import requests
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("YOUTUBE_API_KEY")

if not API_KEY:
    raise RuntimeError("YOUTUBE_API_KEY が設定されていません")


def get_uploads_playlist_id(channel_id: str) -> str:
    url = "https://www.googleapis.com/youtube/v3/channels"
    params = {
        "part": "contentDetails",
        "id": channel_id,
        "key": API_KEY,
    }

    response = requests.get(url, params=params, timeout=30)
    response.raise_for_status()
    data = response.json()

    if not data["items"]:
        raise RuntimeError("チャンネルが見つかりませんでした")

    return data["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"]

uploads は、そのチャンネルに投稿された動画が入る再生リストです。以降は、この再生リストをページングしながら取得します。

動画一覧をページング取得する

playlistItems.list は、1回のリクエストで最大50件まで取得できます。さらに取得する場合は、レスポンスに含まれる nextPageToken を次のリクエストに渡します。

def get_playlist_items(playlist_id: str, max_pages: int | None = None) -> list[dict]:
    url = "https://www.googleapis.com/youtube/v3/playlistItems"
    items = []
    page_token = None
    page_count = 0

    while True:
        params = {
            "part": "snippet,contentDetails",
            "playlistId": playlist_id,
            "maxResults": 50,
            "key": API_KEY,
        }

        if page_token:
            params["pageToken"] = page_token

        response = requests.get(url, params=params, timeout=30)
        response.raise_for_status()
        data = response.json()

        items.extend(data.get("items", []))

        page_count += 1
        if max_pages is not None and page_count >= max_pages:
            break

        page_token = data.get("nextPageToken")
        if not page_token:
            break

    return items

max_pages を用意しておくと、動作確認時に最初の1ページだけ取得できます。いきなり全件取得するより、まず少量で確認するほうが安全です。

CSVに保存する

取得した動画一覧から、分析に使いやすい項目だけを取り出してCSVに保存します。

import csv
from pathlib import Path


def normalize_playlist_item(item: dict) -> dict:
    snippet = item["snippet"]
    content_details = item["contentDetails"]

    return {
        "video_id": content_details["videoId"],
        "title": snippet.get("title", ""),
        "published_at": content_details.get("videoPublishedAt", ""),
        "channel_id": snippet.get("channelId", ""),
        "channel_title": snippet.get("channelTitle", ""),
        "position": snippet.get("position", ""),
        "description": snippet.get("description", ""),
    }


def save_csv(rows: list[dict], csv_path: Path) -> None:
    csv_path.parent.mkdir(parents=True, exist_ok=True)

    fieldnames = [
        "video_id",
        "title",
        "published_at",
        "channel_id",
        "channel_title",
        "position",
        "description",
    ]

    with csv_path.open("w", encoding="utf-8-sig", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(rows)

説明文は長くなることがあります。CSVで扱いにくい場合は、最初は説明文を外して保存しても問題ありません。

実行コード全体

ここまでをまとめたコードです。まずは max_pages=1 で動作確認し、問題なければ None に変更します。

from pathlib import Path

CHANNEL_ID = "UCxxxxxxxxxxxxxxxxxxxxxx"

uploads_playlist_id = get_uploads_playlist_id(CHANNEL_ID)
print("uploads playlist:", uploads_playlist_id)

items = get_playlist_items(uploads_playlist_id, max_pages=1)
rows = [normalize_playlist_item(item) for item in items]

csv_path = Path("outputs/youtube/channel_videos.csv")
save_csv(rows, csv_path)

print(f"saved: {csv_path}")
print(f"rows: {len(rows)}")

全件取得したい場合は次のようにします。

items = get_playlist_items(uploads_playlist_id, max_pages=None)

再生数などの統計情報を追加する

playlistItems.list で取得できるのは、主に再生リスト内の動画情報です。再生数やコメント数を追加したい場合は、取得した動画IDを使って videos.list を呼び出します。

videos.list では、id にカンマ区切りで複数の動画IDを渡せます。1回で扱える件数に合わせて、50件ずつ分割すると扱いやすいです。

def chunked(values: list[str], size: int) -> list[list[str]]:
    return [values[i:i + size] for i in range(0, len(values), size)]


def get_video_statistics(video_ids: list[str]) -> dict[str, dict]:
    url = "https://www.googleapis.com/youtube/v3/videos"
    stats_by_id = {}

    for chunk in chunked(video_ids, 50):
        params = {
            "part": "statistics,contentDetails",
            "id": ",".join(chunk),
            "key": API_KEY,
        }

        response = requests.get(url, params=params, timeout=30)
        response.raise_for_status()
        data = response.json()

        for item in data.get("items", []):
            stats_by_id[item["id"]] = {
                "view_count": item.get("statistics", {}).get("viewCount"),
                "like_count": item.get("statistics", {}).get("likeCount"),
                "comment_count": item.get("statistics", {}).get("commentCount"),
                "duration": item.get("contentDetails", {}).get("duration"),
            }

    return stats_by_id

統計情報は動画や設定によって欠けることがあります。欠損がある前提で保存するほうが後続分析で扱いやすいです。

取得後にできる分析

  • 投稿日ごとの投稿本数を集計する
  • タイトルに含まれる単語を集計する
  • 再生数の多い動画と投稿時期を比較する
  • 動画時間と再生数の関係を見る
  • 特定シリーズや企画の投稿頻度を確認する

この段階では、まだ「動画一覧の取得」が目的です。分析記事に使う場合は、取得日、対象チャンネル、取得項目、欠損の有無をメモしておくと、後で読み返しやすくなります。

動画一覧を取得したあとに、通常動画・ライブ配信アーカイブ・Shortsなどの動画種別や、ゲーム・雑談・音楽などの独自カテゴリを付けたい場合は、YouTube APIで取得した動画一覧を分析用データセットに整える方法で整理しています。

よくあるつまずき

チャンネルIDがわからない

チャンネルURLがハンドル形式の場合、URLだけではチャンネルIDが見えにくいことがあります。公式APIで検索する方法もありますが、検索APIはクォータコストが高いため、最初はYouTube Studioやページ情報、既存ツールでチャンネルIDを確認するほうが簡単です。

取得件数が想定より少ない

max_pages を指定している場合は、そのページ数で止まります。全件取得したい場合は max_pages=None にします。また、非公開動画や削除済み動画は通常の公開データとして取得できません。

クォータが不安

まず1ページだけ取得し、必要な項目が取れているか確認します。統計情報を追加する videos.list もコストは1ですが、不要な項目を何度も取り直さないように、CSVやJSONで途中保存しておくとよいです。

関連記事

まとめ

チャンネルの動画一覧を取得する場合は、channels.listuploads playlist IDを取得し、playlistItems.list でページングしながら動画一覧を取得する流れが扱いやすいです。

検索APIは条件検索には便利ですが、投稿動画一覧を作るだけならクォータ面でも用途面でもuploads playlistを使うほうが明確です。CSVに保存しておけば、投稿頻度、タイトル傾向、再生数との関係など、後続の分析に進みやすくなります。

-YouTubeデータ取得, データ取得