特定のチャンネルの配信予定日時を YouTube Data API から取得する
要約
YouTube Data API から
- Search: list で ChannelId にもとづく動画一覧( videoId の一覧)を取得
-
Videos: list に
id={先程のvideoId}
とpart='liveStreamingDetails'
を指定してitems[].liveStreamingDetails.scheduledStartTime
の値を取得
取得したscheduledStartTime
が配信予定日時(UTC)となる
サンプルコード
from apiclient.discovery import build
from datetime import datetime, timedelta
import os
DEVELOPER_KEY = os.environ.get('YOUTUBE_API_KEY', '')
youtube = build('youtube', 'v3', developerKey=DEVELOPER_KEY)
def youtube_search(channel_id: str, max_results: int = 10) -> list:
# Search: list で channel_id から検索する
search_response = youtube.search().list(channelId=channel_id, part='id', order='date').execute()
return search_response.get('items', [])
def youtube_video_details(video_id: str) -> list:
# Videos: list で video_id から検索する
video_response = youtube.videos().list(id=video_id, part='liveStreamingDetails').execute()
return video_response.get('items', [])
if __name__ == '__main__':
for item in youtube_search('specific-channel-id'):
video_id = item['id']['videoId']
details = youtube_video_detail(video_id)
if len(details) == 0:
continue
scheduled_start_time = datetime.strptime(details[0]['liveStreamingDetails']['scheduledStartTime'], '%Y-%m-%dT%H:%M:%SZ')
scheduled_start_time_jst = scheduled_start_time + timedelta(hours=9) # 日本時間(JST)にする
print(video_id, scheduled_start_time_jst)
詳細
事前準備
予め YouTube Data API の API Key を取得する(参考)
また、サンプルコードの Python 環境は以下の通り
[requires]
python_version = "3.9"
[packages]
google-api-python-client = "2.33.0"
チャンネルの動画一覧を取得
特定のチャンネルの動画一覧を取得するには Search: list を使う
def youtube_search(channel_id: str, max_results: int = 10) -> list:
# Search: list で channel_id から検索する
search_response = youtube.search().list(channelId=channel_id, part='id', order='date').execute()
return search_response.get('items', [])
order='date'
を指定することで、最新の動画順になる(配信も動画というオブジェクトとして取り扱われる)
API のレスポンスは以下のようになる
{
"kind": "youtube#searchListResponse",
"etag": "3LsmrmmY376AFR4PJuk5OkUofT0",
"nextPageToken": "CAEQAA",
"regionCode": "JP",
"pageInfo": {
"totalResults": 223,
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#searchResult",
"etag": "x9fp9u9w-o4tJvgo1cvh1zT9QIE",
"id": {
"kind": "youtube#video",
"videoId": "whTWQw7RdTk"
}
},
...
]
}
なので、items
の中身を for ループして videoId を取り出してゆく
for item in youtube_search('specific-channel-id'):
video_id = item['id']['videoId']
動画詳細情報から配信予定時刻を取得
配信予定時刻を取得するには Videos: list を使い、part='liveStreamingDetails'
を指定することで取得できる
def youtube_video_details(video_id: str) -> list:
# Videos: list で video_id から検索する
video_response = youtube.videos().list(id=video_id, part='liveStreamingDetails').execute()
return video_response.get('items', [])
API のレスポンスは以下のようになる
{
"kind": "youtube#videoListResponse",
"etag": "-cbm6QZrnDdr18MIaJEQ80xCzq8",
"items": [
{
"kind": "youtube#video",
"etag": "8xuNa38mAiZKoS_Wiq6eEYDNhqE",
"id": "whTWQw7RdTk",
"liveStreamingDetails": {
"actualStartTime": "2021-12-28T14:30:21Z",
"actualEndTime": "2021-12-28T15:38:22Z",
"scheduledStartTime": "2021-12-28T14:30:00Z"
}
}
],
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 1
}
}
※この配信は既に終了したものなので、actualStartTime
とactualEndTime
が存在する。scheduledStartTime
は配信前・配信後に存在する
また、これが普通の公開された動画の場合は、liveStreamingDetails
という属性が存在しないので注意(プレミアム公開は未検証)
”動画が配信である”という前提で、items[].liveStreamingDetails.scheduledStartTime
の値を取得して、datetime
オブジェクトへ変換する
details = youtube_video_detail(video_id)
if len(details) == 0:
continue
scheduled_start_time = datetime.strptime(details[0]['liveStreamingDetails']['scheduledStartTime'], '%Y-%m-%dT%H:%M:%SZ')
このときの時刻は UTC なので、 JST に変換する(実際にこの時刻を計算で取り扱いたい場合、タイムゾーンも指定したほうが良い)
scheduled_start_time_jst = scheduled_start_time + timedelta(hours=9) # 日本時間(JST)にする
こうして得られたscheduled_start_time_jst
が配信予定時刻となる
その他
Quota の制限について
一般的なデベロッパーの上限が10000
なので、1時間に1回で数チャンネルに対して実行すると、API の上限に達してしまう
自分の観測している範囲では
- Search: list -> 100 Queries
- Videos: list -> 1 Queries
といった感じ。検索にコストがかかるが、1回のリクエストで指定できる channelId は1つまでらしい
そのため、10チャンネルあると10回APIを叩くので、1度の実行で 1000 Queries 消費する
逆に Videos API の方はほとんどコストが掛からないので、n + 1 な呼出し方でもそこまで問題はない
Quota の節約について(まとめてAPIを叩く方法)
Videos: list は1回のリクエストに複数の videoId を指定できるので、Quota を節約することができる
def youtube_video_details(video_ids: list[str]) -> list:
video_response = youtube.videos().list(id=','.join(video_ids), part='liveStreamingDetails').execute()
return video_response.get('items', [])
video_ids
に videoId のリストを入れて、リクエスト時に,
区切りで送信する
そうすることで items に複数の動画の結果が含まれる
Search: list は channelId を複数指定できない(2021/12/30確認)
Quota の節約について(RSSを使う方法)
この記事の公開後に RSS をつかう方法を教えてもらったので追記
YouTube はチャンネル単位でも RSS を提供しているらしい
https://www.youtube.com/feeds/videos.xml?channel_id={channelId}
取得した RSS の一つのアイテムは以下の通り
<entry>
<id>yt:video:whTWQw7RdTk</id>
<yt:videoId>whTWQw7RdTk</yt:videoId>
<yt:channelId>UC_T1YqknD5yrpVlupc-ZXzg</yt:channelId>
<title>【ASMR】お耳のオイルマッサージ⯎大きく触るやついっぱい。近めの吐息とほんのりささやき。睡眠導入、作業用。Oil Ear Massage/Ear Blowing【#イル_フローラ/Vtuber】</title>
<link rel="alternate" href="https://www.youtube.com/watch?v=whTWQw7RdTk"/>
<author>
<name>イル_フローラ</name>
<uri>https://www.youtube.com/channel/UC_T1YqknD5yrpVlupc-ZXzg</uri>
</author>
<published>2021-12-28T15:42:45+00:00</published>
<updated>2021-12-29T02:51:26+00:00</updated>
<media:group>
...
</media:group>
</entry>
RSS の一つのアイテムには配信予定時刻が含まれていないので、 Videos: list の API を叩いてデータを取得する必要がある
Discussion
さらに追加の情報ですが、RSSの方に高頻度でリクエストを投げると429が返ってきます。複数のチャンネルの毎分チェック等を行うと起きます。(YouTubeの機嫌が良い時は429が返ってこないこともあります)
多くのチャンネルの更新状況を取得したい場合は、RSSの更新をPush通知で受け取る以下の方法を使うと良いです。
取得出来る情報はRSSと同じなのでVideoAPIは叩く必要があります。
コメントありがとうございます
そこまで高頻度で使っていなかったのでエラーが返されたことがなかったのですが、レートリミットがあるのですね(まあ、ありそうですが。。)
こちら初耳でした。試してみたあと記事を更新しようと思います!