YouTube API データ活用

YouTube Data APIでチャンネルの動画一覧を取得する方法【Pythonサンプル+実行結果付き】

2023年12月27日

このページでは、YouTube Data API v3 を用いて、指定チャンネルの動画一覧(アップロード済み動画)を取得し、タイトル・公開日・再生回数などの指標をまとめる方法を、Pythonコードと実行結果付きで解説します。
まずは「一覧取得→軽い集計」ができるようになれば、後からコメント取得やライブ判別、Shorts推定なども拡張しやすくなります。

1. 前提と準備

必要なもの

  • Google Cloud で作成したプロジェクト + YouTube Data API v3 を有効化
  • APIキー(読み取りのみでOK)
  • Python 3.9+(目安)

Pythonパッケージ

pip install google-api-python-client isodate pandas

データ整形に pandas を使用します(不要なら省略可)。

2. 実装方針(なぜ uploads プレイリスト経由?)

チャンネルがアップロードした動画は、内部的に「uploads」プレイリストにまとまっています。そこで:

  1. channels.list(part=contentDetails)uploads プレイリストIDを取得
  2. playlistItems.list でプレイリスト内の動画IDをページング取得
  3. videos.list(part=snippet,statistics,contentDetails,liveStreamingDetails) で詳細指標を一括取得

この流れが最も標準的で堅牢です(検索APIに依存しないため、取りこぼしが少ない)。

補足:動画ID・チャンネルIDの見つけ方

動画ID(Video ID)の見つけ方

  • 通常URL:https://www.youtube.com/watch?v=VIDEO_IDv= の値が動画ID
  • 短縮URL:https://youtu.be/VIDEO_ID の末尾が動画ID
  • Shorts:https://www.youtube.com/shorts/VIDEO_ID の末尾が動画ID

チャンネルID(Channel ID)の取得方法

  • URLが /channel/UCxxxxxxxx... 形式なら、UC~ で始まる文字列がチャンネルID
  • @ハンドル形式(例:https://www.youtube.com/@example)はURLだけでは分からないため、下のミニコードで動画ID→チャンネルIDを取得

ミニコード:動画IDからチャンネルIDを取得(Python)

from googleapiclient.discovery import build

API_KEY  = "YOUR_API_KEY"      # 置き換え
VIDEO_ID = "dQw4w9WgXcQ"       # 置き換え

yt = build("youtube", "v3", developerKey=API_KEY)
res = yt.videos().list(part="snippet", id=VIDEO_ID).execute()
items = res.get("items", [])
print(items[0]["snippet"]["channelId"] if items else "not found")

上記で取得した channelId を、このページの「CHANNEL_ID」に貼り付けてください。

3. サンプルコード(コピー&ペーストでOK)

使い方

  1. 下記コードの API_KEYCHANNEL_ID を書き換え
  2. 実行すると、動画の title / publishedAt / viewCount / likeCount / commentCount / duration を一覧出力
  3. 末尾で CSV に保存(任意)。Excel/スプレッドシートで分析可能
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
import isodate
import pandas as pd
from time import sleep

API_KEY = "YOUR_API_KEY"  # ← 置き換え
CHANNEL_ID = "UCxxxxxxxxxxxxxxxx"  # ← 置き換え(チャンネルID)

# --- 1) uploads プレイリストIDを取得 ---
def get_uploads_playlist_id(youtube, channel_id):
    res = youtube.channels().list(
        part="contentDetails",
        id=channel_id
    ).execute()
    items = res.get("items", [])
    if not items:
        raise ValueError("Channel not found or no contentDetails.")
    return items[0]["contentDetails"]["relatedPlaylists"]["uploads"]

# --- 2) playlistItems から videoId を全件取得(ページネーション対応) ---
def get_all_video_ids_from_uploads(youtube, uploads_playlist_id, max_pages=None, page_size=50):
    video_ids = []
    token = None
    pages = 0
    while True:
        resp = youtube.playlistItems().list(
            part="contentDetails",
            playlistId=uploads_playlist_id,
            maxResults=min(page_size, 50),
            pageToken=token
        ).execute()
        for it in resp.get("items", []):
            vid = it["contentDetails"]["videoId"]
            video_ids.append(vid)
        token = resp.get("nextPageToken")
        pages += 1
        if not token or (max_pages and pages >= max_pages):
            break
    return video_ids

# --- 3) videos.list で詳細指標を取得(50件ずつ) ---
def chunk(lst, n):
    for i in range(0, len(lst), n):
        yield lst[i:i+n]

def fetch_video_details(youtube, video_ids):
    rows = []
    for batch in chunk(video_ids, 50):
        resp = youtube.videos().list(
            part="snippet,statistics,contentDetails,liveStreamingDetails",
            id=",".join(batch)
        ).execute()
        for it in resp.get("items", []):
            s = it.get("snippet", {})
            st = it.get("statistics", {})
            cd = it.get("contentDetails", {})
            ld = it.get("liveStreamingDetails", {})
            duration_iso = cd.get("duration")
            duration_sec = int(isodate.parse_duration(duration_iso).total_seconds()) if duration_iso else None
            rows.append({
                "videoId": it["id"],
                "title": s.get("title"),
                "publishedAt": s.get("publishedAt"),
                "viewCount": int(st.get("viewCount", 0)) if st.get("viewCount") is not None else None,
                "likeCount": int(st.get("likeCount", 0)) if st.get("likeCount") is not None else None,
                "commentCount": int(st.get("commentCount", 0)) if st.get("commentCount") is not None else None,
                "durationSec": duration_sec,
                "isLiveLike": bool(ld),  # liveStreamingDetailsがある→配信/アーカイブの可能性
            })
        # 呼び出し間隔のマナー(クォータ節約&安定のため)
        sleep(0.1)
    return pd.DataFrame(rows)

def main():
    youtube = build("youtube", "v3", developerKey=API_KEY)
    try:
        uploads = get_uploads_playlist_id(youtube, CHANNEL_ID)
        video_ids = get_all_video_ids_from_uploads(youtube, uploads)
        print(f"Fetched video IDs: {len(video_ids)}")
        df = fetch_video_details(youtube, video_ids)
        # 公開日で降順(新しい順)
        df = df.sort_values("publishedAt", ascending=False).reset_index(drop=True)
        print(df.head(10).to_string(index=False))
        # CSV保存(任意)
        df.to_csv("youtube_channel_videos.csv", index=False, encoding="utf-8")
        print("Saved to youtube_channel_videos.csv")
    except HttpError as e:
        print("HTTP Error:", e)
    except Exception as ex:
        print("Error:", ex)

if __name__ == "__main__":
    main()

ポイント:liveStreamingDetails があれば配信/アーカイブ系の可能性が高いです。後続の分析(pytchatでチャット突合、Shorts推定など)に使えます。

4. 実行結果サンプル

テーブル表示(抜粋)

#videoIdtitlepublishedAtviewCountlikeCountcommentCountdurationSecisLiveLike
1AbCdEf12345【告知】新プロジェクト始動2025-08-10T12:00:03Z1523405342421482false
2GhIjKl67890配信アーカイブ:夏祭りコラボ2025-07-29T15:00:11Z9832012314535217260true
3MnOpQr24680Shorts:切り抜き名場面2025-07-20T09:12:00Z42012150211828false

JSON(先頭3件の例)

[
  {
    "videoId": "AbCdEf12345",
    "title": "【告知】新プロジェクト始動",
    "publishedAt": "2025-08-10T12:00:03Z",
    "viewCount": 152340,
    "likeCount": 5342,
    "commentCount": 421,
    "durationSec": 482,
    "isLiveLike": false
  },
  {
    "videoId": "GhIjKl67890",
    "title": "配信アーカイブ:夏祭りコラボ",
    "publishedAt": "2025-07-29T15:00:11Z",
    "viewCount": 983201,
    "likeCount": 23145,
    "commentCount": 3521,
    "durationSec": 7260,
    "isLiveLike": true
  },
  {
    "videoId": "MnOpQr24680",
    "title": "Shorts:切り抜き名場面",
    "publishedAt": "2025-07-20T09:12:00Z",
    "viewCount": 42012,
    "likeCount": 1502,
    "commentCount": 118,
    "durationSec": 28,
    "isLiveLike": false
  }
]

5. 運用Tips(クォータ・例外・拡張)

  • クォータ最適化videos.list は1リクエストで最大50件まで。IDをバッチ化して呼び出し回数を削減
  • ページネーション:古い動画が多いチャンネルは playlistItems.list で複数ページに分かれます
  • HTTP 403/400 対策:キー制限/有効化ミスが多い。例外時はレスポンスメッセージをログ化して原因切り分け
  • 配信判別liveStreamingDetails がある動画は配信(ライブ/アーカイブ)。後でpytchat と突合しチャット有無を分析
  • Shorts推定durationSec ≤ 180 かつ 縦型なら Shorts の可能性が高い(縦横は自チャンネルは fileDetails、第三者は yt-dlp で抽出)
  • CSV運用:定期ジョブでCSVを差分追記し、Looker Studio や Python 可視化に接続

6. 次に読む(取得データを広げる)

pytchatでアーカイブ配信のチャットを取得する方法

youtube-transcript-apiで字幕を取得する方法

Whisperで文字起こしして要約する方法

YouTubeの情報を取得する方法まとめ(入口へ戻る)

-YouTube API, データ活用