YouTube動画統計の取得とグラフ化
YouTube Data APIを使って動画データを取得し、視覚化する方法を解説します。
この記事で学べること
- YouTube Data API v3の使い方
- 動画統計データの取得方法
- Matplotlibでのグラフ作成
- チャンネル分析の自動化
1. 準備:必要なライブラリ
# インストール
pip install google-api-python-client
pip install matplotlib
pip install pandas
pip install python-dotenvプロジェクト構成
youtube_scraper/
├── .env # API キー保存
├── scraper.py # スクレイピングコード
├── visualizer.py # グラフ作成
└── requirements.txt # 依存関係2. YouTube Data API キーの取得
Google Cloud Consoleでの設定
1. Google Cloud Console にアクセス
https://console.cloud.google.com/
2. 新しいプロジェクトを作成
「プロジェクトの選択」→「新しいプロジェクト」
3. YouTube Data API v3 を有効化
「APIとサービス」→「ライブラリ」
→ 「YouTube Data API v3」を検索して有効化
4. 認証情報を作成
「認証情報」→「認証情報を作成」→「APIキー」
5. APIキーをコピーして保存.envファイルに保存
# .env
YOUTUBE_API_KEY=YOUR_API_KEY_HERE3. 基本的な動画情報取得
scraper.py
#!/usr/bin/env python3
"""YouTube動画統計スクレイパー"""
import os
from googleapiclient.discovery import build
from dotenv import load_dotenv
import pandas as pd
from datetime import datetime
# 環境変数読み込み
load_dotenv()
API_KEY = os.getenv('YOUTUBE_API_KEY')
# YouTube APIクライアント
youtube = build('youtube', 'v3', developerKey=API_KEY)
def get_video_stats(video_id):
"""動画IDから統計情報を取得"""
try:
request = youtube.videos().list(
part='snippet,statistics,contentDetails',
id=video_id
)
response = request.execute()
if not response['items']:
print(f"動画が見つかりません: {video_id}")
return None
video = response['items'][0]
snippet = video['snippet']
stats = video['statistics']
data = {
'video_id': video_id,
'title': snippet['title'],
'channel': snippet['channelTitle'],
'published_at': snippet['publishedAt'],
'view_count': int(stats.get('viewCount', 0)),
'like_count': int(stats.get('likeCount', 0)),
'comment_count': int(stats.get('commentCount', 0)),
'duration': video['contentDetails']['duration']
}
return data
except Exception as e:
print(f"エラー: {e}")
return None
def extract_video_id(url):
"""YouTubeのURLから動画IDを抽出"""
if 'youtu.be/' in url:
return url.split('youtu.be/')[1].split('?')[0]
elif 'watch?v=' in url:
return url.split('watch?v=')[1].split('&')[0]
else:
return url # すでにIDの場合
# 使用例
if __name__ == '__main__':
# 動画URL(例)
video_url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
video_id = extract_video_id(video_url)
# 統計取得
stats = get_video_stats(video_id)
if stats:
print(f"タイトル: {stats['title']}")
print(f"再生数: {stats['view_count']:,}")
print(f"高評価数: {stats['like_count']:,}")
print(f"コメント数: {stats['comment_count']:,}")実行結果
タイトル: Rick Astley - Never Gonna Give You Up
再生数: 1,450,000,000
高評価数: 15,000,000
コメント数: 2,500,0004. 複数動画の統計比較
def get_multiple_videos_stats(video_ids):
"""複数動画の統計を一括取得"""
all_stats = []
for video_id in video_ids:
stats = get_video_stats(video_id)
if stats:
all_stats.append(stats)
# DataFrameに変換
df = pd.DataFrame(all_stats)
return df
# 使用例
video_ids = [
'dQw4w9WgXcQ', # Rick Astley
'kJQP7kiw5Fk', # Despacito
'9bZkp7q19f0' # Gangnam Style
]
df = get_multiple_videos_stats(video_ids)
print(df[['title', 'view_count', 'like_count']])出力例
title view_count like_count
0 Rick Astley - Never Gonna Give You Up 1450000000 15000000
1 Luis Fonsi - Despacito 8100000000 52000000
2 PSY - GANGNAM STYLE 4800000000 240000005. チャンネルの最新動画を取得
def get_channel_videos(channel_id, max_results=10):
"""チャンネルの最新動画を取得"""
try:
# チャンネルのアップロード再生リストIDを取得
request = youtube.channels().list(
part='contentDetails',
id=channel_id
)
response = request.execute()
uploads_id = response['items'][0]['contentDetails'][
'relatedPlaylists']['uploads']
# 再生リストから動画を取得
request = youtube.playlistItems().list(
part='snippet',
playlistId=uploads_id,
maxResults=max_results
)
response = request.execute()
video_ids = [
item['snippet']['resourceId']['videoId']
for item in response['items']
]
return get_multiple_videos_stats(video_ids)
except Exception as e:
print(f"エラー: {e}")
return None
# 使用例
channel_id = 'UC_x5XG1OV2P6uZZ5FSM9Ttw' # Google Developers
df = get_channel_videos(channel_id, max_results=20)
print(df.head())6. データの可視化
visualizer.py
#!/usr/bin/env python3
"""YouTube統計データの可視化"""
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime
import pandas as pd
import japanize_matplotlib # 日本語フォント対応
# 日本語フォント設定(Windows)
plt.rcParams['font.sans-serif'] = ['MS Gothic', 'Yu Gothic', 'Arial']
plt.rcParams['axes.unicode_minus'] = False
def plot_video_comparison(df):
"""複数動画の統計比較グラフ"""
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# タイトルを短縮
df['short_title'] = df['title'].str[:20] + '...'
# 再生数
axes[0].barh(df['short_title'], df['view_count'], color='#FF0000')
axes[0].set_xlabel('再生数', fontsize=12)
axes[0].set_title('動画別再生数', fontsize=14, fontweight='bold')
axes[0].ticklabel_format(style='plain', axis='x')
# 高評価数
axes[1].barh(df['short_title'], df['like_count'], color='#065FD4')
axes[1].set_xlabel('高評価数', fontsize=12)
axes[1].set_title('動画別高評価数', fontsize=14, fontweight='bold')
axes[1].ticklabel_format(style='plain', axis='x')
# コメント数
axes[2].barh(df['short_title'], df['comment_count'], color='#F9AB00')
axes[2].set_xlabel('コメント数', fontsize=12)
axes[2].set_title('動画別コメント数', fontsize=14, fontweight='bold')
axes[2].ticklabel_format(style='plain', axis='x')
plt.tight_layout()
plt.savefig('youtube_comparison.png', dpi=300, bbox_inches='tight')
plt.show()
def plot_engagement_rate(df):
"""エンゲージメント率のグラフ"""
# エンゲージメント率を計算
df['engagement_rate'] = (
(df['like_count'] + df['comment_count']) / df['view_count'] * 100
)
fig, ax = plt.subplots(figsize=(10, 6))
bars = ax.bar(
range(len(df)),
df['engagement_rate'],
color=['#FF0000', '#065FD4', '#F9AB00', '#00D924']
)
ax.set_xlabel('動画', fontsize=12)
ax.set_ylabel('エンゲージメント率 (%)', fontsize=12)
ax.set_title('動画別エンゲージメント率', fontsize=14, fontweight='bold')
ax.set_xticks(range(len(df)))
ax.set_xticklabels(df['title'].str[:15], rotation=45, ha='right')
# 値をバーの上に表示
for bar in bars:
height = bar.get_height()
ax.text(
bar.get_x() + bar.get_width()/2., height,
f'{height:.2f}%',
ha='center', va='bottom', fontsize=10
)
plt.tight_layout()
plt.savefig('engagement_rate.png', dpi=300, bbox_inches='tight')
plt.show()
def plot_time_series(df):
"""時系列グラフ(投稿日ごとの再生数)"""
df['published_date'] = pd.to_datetime(df['published_at'])
df = df.sort_values('published_date')
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(
df['published_date'],
df['view_count'],
marker='o',
linewidth=2,
markersize=8,
color='#FF0000'
)
ax.set_xlabel('投稿日', fontsize=12)
ax.set_ylabel('再生数', fontsize=12)
ax.set_title('投稿日別再生数の推移', fontsize=14, fontweight='bold')
ax.grid(True, alpha=0.3)
# 日付フォーマット
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('time_series.png', dpi=300, bbox_inches='tight')
plt.show()グラフ作成実行
# データ取得
from scraper import get_multiple_videos_stats
from visualizer import plot_video_comparison, plot_engagement_rate
video_ids = ['dQw4w9WgXcQ', 'kJQP7kiw5Fk', '9bZkp7q19f0']
df = get_multiple_videos_stats(video_ids)
# グラフ作成
plot_video_comparison(df)
plot_engagement_rate(df)7. 検索結果から動画を取得
def search_videos(query, max_results=10):
"""キーワード検索で動画を取得"""
try:
request = youtube.search().list(
part='snippet',
q=query,
type='video',
maxResults=max_results,
order='viewCount' # 再生数順
)
response = request.execute()
video_ids = [
item['id']['videoId']
for item in response['items']
]
return get_multiple_videos_stats(video_ids)
except Exception as e:
print(f"エラー: {e}")
return None
# 使用例
df = search_videos('Python tutorial', max_results=20)
print(df[['title', 'view_count']].head(10))8. トレンド動画の取得
def get_trending_videos(region='JP', max_results=10):
"""トレンド動画を取得"""
try:
request = youtube.videos().list(
part='snippet,statistics',
chart='mostPopular',
regionCode=region,
maxResults=max_results
)
response = request.execute()
videos = []
for item in response['items']:
video_data = {
'video_id': item['id'],
'title': item['snippet']['title'],
'channel': item['snippet']['channelTitle'],
'view_count': int(item['statistics'].get('viewCount', 0)),
'like_count': int(item['statistics'].get('likeCount', 0)),
'comment_count': int(item['statistics'].get('commentCount', 0))
}
videos.append(video_data)
return pd.DataFrame(videos)
except Exception as e:
print(f"エラー: {e}")
return None
# 日本のトレンド取得
df_trending = get_trending_videos('JP', max_results=50)
plot_video_comparison(df_trending.head(10))9. レート制限への対応
APIクォータ管理
import time
from functools import wraps
def rate_limit(calls_per_minute=60):
"""レート制限デコレータ"""
min_interval = 60.0 / calls_per_minute
last_called = [0.0]
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
elapsed = time.time() - last_called[0]
wait_time = min_interval - elapsed
if wait_time > 0:
time.sleep(wait_time)
result = func(*args, **kwargs)
last_called[0] = time.time()
return result
return wrapper
return decorator
@rate_limit(calls_per_minute=30)
def get_video_stats_limited(video_id):
"""レート制限付き統計取得"""
return get_video_stats(video_id)クォータ使用量の確認
YouTube Data API v3のクォータ:
- 1日のクォータ: 10,000ユニット
- videos().list: 1コール = 1ユニット
- search().list: 1コール = 100ユニット
節約のコツ:
✅ 必要なpartだけ指定(snippet, statisticsなど)
✅ キャッシュを活用
✅ バッチ処理で複数IDを一度に取得10. 完全な実装例
#!/usr/bin/env python3
"""YouTubeチャンネル分析ツール"""
import os
from googleapiclient.discovery import build
from dotenv import load_dotenv
import pandas as pd
import matplotlib.pyplot as plt
load_dotenv()
API_KEY = os.getenv('YOUTUBE_API_KEY')
youtube = build('youtube', 'v3', developerKey=API_KEY)
def analyze_channel(channel_id, max_videos=50):
"""チャンネルの詳細分析"""
print(f"チャンネル分析中... (最新{max_videos}本)")
# チャンネル情報取得
channel_data = youtube.channels().list(
part='snippet,statistics',
id=channel_id
).execute()
channel_info = channel_data['items'][0]
stats = channel_info['statistics']
print(f"\nチャンネル名: {channel_info['snippet']['title']}")
print(f"登録者数: {int(stats['subscriberCount']):,}")
print(f"総視聴回数: {int(stats['viewCount']):,}")
print(f"動画数: {stats['videoCount']}\n")
# 動画データ取得
df = get_channel_videos(channel_id, max_results=max_videos)
if df is not None and len(df) > 0:
# 統計サマリー
print("=== 動画統計サマリー ===")
print(f"平均再生数: {df['view_count'].mean():,.0f}")
print(f"平均高評価数: {df['like_count'].mean():,.0f}")
print(f"平均コメント数: {df['comment_count'].mean():,.0f}")
print(f"最高再生数: {df['view_count'].max():,.0f}")
# グラフ作成
plot_video_comparison(df.head(10))
plot_engagement_rate(df.head(10))
# CSV保存
output_file = f'channel_{channel_id}_analysis.csv'
df.to_csv(output_file, index=False, encoding='utf-8-sig')
print(f"\n結果を保存: {output_file}")
return df
else:
print("動画データを取得できませんでした")
return None
if __name__ == '__main__':
# 分析したいチャンネルID
channel_id = 'UC_x5XG1OV2P6uZZ5FSM9Ttw' # Google Developers
analyze_channel(channel_id, max_videos=30)11. エラーハンドリング
from googleapiclient.errors import HttpError
def safe_api_call(func):
"""API呼び出しのエラーハンドリング"""
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except HttpError as e:
if e.resp.status == 403:
print("❌ クォータ超過またはAPI無効")
elif e.resp.status == 404:
print("❌ リソースが見つかりません")
else:
print(f"❌ APIエラー: {e}")
return None
except Exception as e:
print(f"❌ 予期しないエラー: {e}")
return None
return wrapper
@safe_api_call
def get_video_stats_safe(video_id):
return get_video_stats(video_id)まとめ
YouTube Data APIで動画統計を取得・分析できます!
重要ポイント
- ✅ Google Cloud ConsoleでAPI有効化必須
- ✅ APIキーは.envファイルで管理
- ✅ クォータ制限に注意(1日10,000ユニット)
- ✅ Matplotlibで効果的に可視化
- ✅ pandasでデータ分析を効率化
応用例
- 競合チャンネルの分析
- 動画パフォーマンス予測
- 最適な投稿時間の分析
- タイトル・タグの効果測定
- トレンド追跡ダッシュボード
注意事項
- ⚠️ 利用規約を遵守
- ⚠️ 個人情報の取り扱いに注意
- ⚠️ 商用利用は制限を確認
- ⚠️ APIキーは公開しない