このページでは、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」プレイリストにまとまっています。そこで:
channels.list(part=contentDetails)
でuploads
プレイリストIDを取得playlistItems.list
でプレイリスト内の動画IDをページング取得videos.list(part=snippet,statistics,contentDetails,liveStreamingDetails)
で詳細指標を一括取得
この流れが最も標準的で堅牢です(検索APIに依存しないため、取りこぼしが少ない)。
補足:動画ID・チャンネルIDの見つけ方
動画ID(Video ID)の見つけ方
- 通常URL:
https://www.youtube.com/watch?v=VIDEO_ID
のv=
の値が動画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)
使い方
- 下記コードの
API_KEY
とCHANNEL_ID
を書き換え - 実行すると、動画の title / publishedAt / viewCount / likeCount / commentCount / duration を一覧出力
- 末尾で
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. 実行結果サンプル
テーブル表示(抜粋)
# | videoId | title | publishedAt | viewCount | likeCount | commentCount | durationSec | isLiveLike |
---|---|---|---|---|---|---|---|---|
1 | AbCdEf12345 | 【告知】新プロジェクト始動 | 2025-08-10T12:00:03Z | 152340 | 5342 | 421 | 482 | false |
2 | GhIjKl67890 | 配信アーカイブ:夏祭りコラボ | 2025-07-29T15:00:11Z | 983201 | 23145 | 3521 | 7260 | true |
3 | MnOpQr24680 | Shorts:切り抜き名場面 | 2025-07-20T09:12:00Z | 42012 | 1502 | 118 | 28 | false |
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 可視化に接続