youtubeの動画一覧をチャンネル毎にpythonで取得してCSVで保存する

youtube

最近youtubeの検索にちょっと不満がありまして、チャンネル内でのシリーズ動画ってあるじゃないですか、再生リストを作ってくれていてもあれは手動で作る物なのでたまに抜けていたりしますし、再生リストが無い場合はチャンネルページの動画タブから永遠に過去に遡ってみたい動画を探す必要があるんですが、かなり面倒くさいです。

なので、チャンネルを指定するだけで動画一覧・再生数・コメント数などなど、youtubeAPIで公開されているデータを一通りごっそり取得するスクリプトを作ってみます。

完成後のスクリプト動作はこんな感じ

動画情報一括取得マシーンの仕様

  • pythonで書く
  • データはyoutubeAPIで取得
  • CSVで吐き出す

以上、シンプルイズベストなり。

まずはyoutubeAPIで取れるデータの中身を確認

今回は「チャンネルから動画の一覧を取得する」「動画の詳細情報を取得する」の2段構えなので、searchメソッドとvideoメソッドの二つ組み合わせる必要があります。

それぞれのメソッドを実際に実行してみて必要なデータを吟味していきます。

youtubeAPIキーを用意する

まずはyoutbeAPIを利用するのに必須なyoutubeAPIキーを取得します、難しくはないですが手順がちょっと長いので「YouTube APIを利用するための手順を画像付きで丁寧に解説する」の記事にまとめました。

すでにAPIキーを持っている方はそれを流用すればOKですが、未入手の方は無料で何個でも作れるんで上記の記事を参考にパパっと作っちゃって下さい。

チャンネルIDを取得する

今回のスクリプトの仕様は「チャンネルIDを入力するだけで全動画&動画情報の一覧をCSVで取得できる」機能を作りたいので、唯一の手入力パラメータであるチャンネルIDをyoutubeからコピペします。

動画データを取得したいyoutuberのチャンネルページに行くと、上記画像のように「https://www.youtube.com/channel/【チャンネルID】」というURLの構造になっているので、末尾をコピペしてスクリプトに使用します。

たまに下記画像のようにチャンネルページにチャンネルIDが表示されないURLが設定されている場合があります。

この場合は変換ツールを使って元々のURLに戻すとチャンネルIDが分かります、変換ツールは「youtubeカスタムURLコンバーター」のページで使えます。

使いそうなデータを吟味する

youtubeAPIキーとチャンネルIDが用意できたらAPIを実行する準備は完了なので、データの中身を見るためにさっそくyoutubeAPIを叩いてみます。

youtubeAPIはGETでリクエストできるのでデータの中身を確認するだけならコードを書く必要は無く、ブラウザの窓にオプション値を設定したエンドポイントのURLを入力してenterキーを押すだけです。

searchメソッドの中身

searchメソッドのエンドポイントは下記の通り、指定できるオプション値は公式ドキュメントから確認できます。今回はチャンネルIDを指定するので下記のようにしてみました。

https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=【チャンネルID】&maxResults=50&order=date&type=video&key=【youtubeAPIキー】

searchメソッドではチャンネルIDを指定してチャンネル内の全videoidが取得できればOKです。

長いので画像は途切れていますが、公開されている40件の動画すべてのvideoidが確認できました、必要なデータが見つかったので次にいきます。

videoメソッドの中身

vvideoメソッドのエンドポイントは下記の通り、公式ドキュメントを確認してオプション値を設定します。videoメソッドは細かいデータがたくさん含まれているので、とりあえず一通り表示させてみます。

https://www.googleapis.com/youtube/v3/videos?part=id,snippet,contentDetails,liveStreamingDetails,player,recordingDetails,statistics,status,topicDetails&id=【videoid】&key=【youtubeAPIキー】

こんな感じで細々とデータが表示されるので欲しい物を吟味してみました。

項目内容
publishedAt公開日item[‘snippet’][‘publishedAt’]
title動画タイトルitem[‘snippet’][‘title’]
description動画の概要欄item[‘snippet’][‘description’]
url動画ページURL‘https://www.youtube.com/watch?v=’ + item[‘id’]
thumbnail_urlサムネイルitem[‘snippet’][‘thumbnails’][‘high’][‘url’]
categoryId動画のカテゴリitem[‘snippet’][‘categoryId’]
liveBroadcastContentライブ配信フラグitem[‘snippet’][‘liveBroadcastContent’]
duration動画の長さitem[‘contentDetails’][‘duration’]
viewCount再生回数item[‘statistics’][‘viewCount’]
likeCountグッドitem[‘statistics’][‘likeCount’]
favoriteCountお気に入り?item[‘statistics’][‘favoriteCount’]
commentCountコメント数item[‘statistics’][‘commentCount’]
embedHtml埋め込みコードitem[‘player’][‘embedHtml’]

これだけ情報がとれれば十分ですね。

クォータ使用量を推定してみる

youtubeAPIは無制限に使える物ではなくてAPIキー単位でクォータと呼ばれるリソースが付与され、APIを実行する毎に一定数消費されます。クォータが無くなるとAPIが実行できなくなり24時間経過でクォータ数は復活します。

つまりスクリプトを書く際はAPIが使える回数が限られているのでなるべく消費を抑えつつ、必要なデータは網羅するようにコーディングしたいワケです。なので想定している仕様でどれくらいのクォータ数を消費するのかも調べておかないと、2-3回実行するだけで上限に引っかかっちゃうような実用に耐えない仕上がりになっちゃうかもしれません。

ですが、クォータ数に関する公式ドキュメントを読んでもいまいちピンとこないので色々条件を変えてyoutubeAPIを実行しつつ消費クォータを観察した所、searchメソッドは固定で100クォータ、videoメソッドは固定で1クォータ消費する事がわかりました。公式ドキュメントと違う気がしますが観測できてしまったのでこの数字をベースに今回のスクリプトのクォータ数を推定してみます。

searchメソッドもvideoメソッドも1度のAPI実行で取得できるレコードの件数は最大50件、さらにスクリプトではそれぞれのメソッドをセットで実行する仕様なので、例えば50件の動画を公開しているチャンネルを対象とすると、searchメソッドとvideoメソッドをそれぞれ1回ずつ実行するだけですべての情報が取得できるので、消費するクォータは100+1で101になるハズです。

そしてyoutubeAPIキーはデフォルトで10,000クォータ/日を付与されるので、1日辺りで取得できる動画情報の件数は(10,000クォータ/101クォータ)×50件=4,950件/日となりそうです。

個人的は無料の範囲で毎日約5,000件動画情報が収集できるんなら上々かなと思いますが、データサイエンスとか仕事とかでゴリゴリ必要な方はyoutubeAPIキーのクォータ数を増やしてもらう申請とかもあるので、必要ならやってみて下さい。

urllibライブラリでAPIデータを取得

youtubeAPI側で考える事は一通り終わったんで次はデータを取得する仕組みを作っていきます。

pythonのデータ取得系ライブラリはたくさんありますが僕はurllibを使ってます、とりあえず標準装備を使いこなせるのがいいかなーと思って使ってます。新しいライブラリは無数に増殖するので覚えられません笑

ざっくりした仕組みのイメージは、

  • searchメソッドにアクセスする
  • itemsをforでぐるぐるする
  • その中でvideoidを使ってvideoメソッドを呼ぶ
  • itemsをforでぐるぐるする
  • その中でCSV書き込み用の変数にデータを追加する

こんな感じですね、詳しくは下記のコピペコードを参考にして下さい。

CSVで出力する

出力は万人に汎用性の高いCSVにしてみました。

ここまでデータが準備できればあとはCSVで出力するだけ、標準ライブラリにCSVがあるので難しい事は特になし。with openして用意したCSV書き込み用の変数をwriterowsするだけです。

サンプルコード(コピペで動きます)

import urllib.request
import urllib.parse
import json
import csv
import isodate
import datetime

#-------↓パラメータ入力↓-------

APIKEY = 'APIキー'
channel_id = 'チャンネルID'

#-------↑パラメータ入力↑-------

dt_now = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
nextPageToken = ''
item_count = 0
outputs = []
outputs.append(['publishedAt', 'title', 'description', 'url', 'thumbnail_url', 'categoryId', 'liveBroadcastContent', 'duration', 'viewCount', 'likeCount', 'favoriteCount', 'commentCount', 'embedHtml'])
n = 0

while True:

    #searchメソッドでvideoid一覧取得
    param = {
        'part':'snippet',
        'channelId':channel_id,
        'maxResults':50,
        'order':'date',
        'type':'video',
        'pageToken':nextPageToken,
        'key':APIKEY
    }
    target_url = 'https://www.googleapis.com/youtube/v3/search?'+urllib.parse.urlencode(param)
    print('動画リスト取得')
    print(target_url)

    req = urllib.request.Request(target_url)
    try:
        with urllib.request.urlopen(req) as res:
            search_body = json.load(res)
            item_count += len(search_body['items'])
            video_list = []
            for item in search_body['items']:
                #videoメソッド用list作成
                video_list.append(item['id']['videoId'])
                
            #videoメソッドで動画情報取得
            param = {
                'part':'id,snippet,contentDetails,liveStreamingDetails,player,recordingDetails,statistics,status,topicDetails',
                'id':",".join(video_list),
                'key':APIKEY
            }
            target_url = 'https://www.googleapis.com/youtube/v3/videos?'+(urllib.parse.urlencode(param))
            print('動画情報取得:合計' + str(item_count)+'件')
            print(target_url)

            req = urllib.request.Request(target_url)
            try:
                with urllib.request.urlopen(req) as res:
                    videos_body = json.load(res)
                    #CSV書き込み用データ準備
                    for item in videos_body['items']:
                        #値が存在しない場合ブランク
                        publishedAt = item['snippet']['publishedAt'] if 'publishedAt' in item['snippet'] else ''
                        title = item['snippet']['title'] if 'title' in item['snippet'] else ''
                        description = item['snippet']['description'] if 'description' in item['snippet'] else ''
                        url = 'https://www.youtube.com/watch?v=' + item['id'] if 'id' in item else ''
                        thumbnail_url = item['snippet']['thumbnails']['high']['url'] if 'thumbnails' in item['snippet'] else ''
                        categoryId = item['snippet']['categoryId'] if 'categoryId' in item['snippet'] else ''
                        liveBroadcastContent = item['snippet']['liveBroadcastContent'] if 'liveBroadcastContent' in item['snippet'] else ''
                        if 'duration' in item['contentDetails']:
                            #durationを時分秒へ変換
                            duration = isodate.parse_duration(item['contentDetails']['duration'])
                        else:
                            duration = ''
                        viewCount = item['statistics']['viewCount'] if 'viewCount' in item['statistics'] else 0
                        likeCount = item['statistics']['likeCount'] if 'likeCount' in item['statistics'] else 0
                        favoriteCount = item['statistics']['favoriteCount'] if 'favoriteCount' in item['statistics'] else 0
                        commentCount = item['statistics']['commentCount'] if 'commentCount' in item['statistics'] else 0
                        embedHtml = item['player']['embedHtml'] if 'embedHtml' in item['player'] else ''
                        outputs.append([publishedAt, title, description, url, thumbnail_url, categoryId, liveBroadcastContent, duration, viewCount, likeCount, favoriteCount, commentCount, embedHtml])
                        n += 1

                    #CSV書き込み
                    with open(dt_now + '_' + channel_id + '_channel-video-info.csv', 'w', newline='', encoding='UTF-8') as f:
                        writer = csv.writer(f)
                        writer.writerows(outputs)

            except urllib.error.HTTPError as err:
                print(err)
                break
            except urllib.error.URLError as err:
                print(err)
                break
        
        #nextPageTokenが表示されなくなったらストップ
        if 'nextPageToken' in search_body:
            nextPageToken = search_body['nextPageToken']
        else:
            break

    except urllib.error.HTTPError as err:
        print(err)
        break
    except urllib.error.URLError as err:
        print(err)
        break

使い方

コピペ⇒ライブラリインストール⇒パラメータ入力⇒python実行、で使えます。

ライブラリのインストール

1個だけisodateというサードパーティのライブラリを使ってるんでまずはそれをインストール

pip install isodate

パラメータの入力

10-11行目にyoutubeAPIキーとチャンネルIDを入力する箇所があるので、そこを任意のIDに書き換えて下さい。

これで準備完了、実行すればチャンネル内の全動画と動画情報をCSVで出力されます。

課題の発見

当記事を公開後に「この記事のスクリプトだと動画データを500件を取得した所で処理が止まる」という内容のお問い合わせを多数頂きました。僕がサンプルにしていたyoutuberは動画投稿件数が少なかったため、この課題に気付いてなかったようです|д゚)

長考した結果、完全に抜け・漏れなく動画リストをyoutubeAPIで取得するのって難しいかも、という結論に至りまして、試行錯誤し別のアプローチで全動画リスト取得できるようになりました。

その手順は下記の別記事として公開してますんで、興味がある方は読んでみて下さい。

着想~完成までの頭の中

最近、開発の手順を聞かれる事があったのでざっくり記録してみます。

  1. youtubeでチャンネル指定した動画一覧欲しいな~(適当にググる)
  2. 良さそうなツールもサービスもないわ、面倒くさいけど自分で作って記事にしよ
  3. youtubeAPIで大体データ取れるよな、なかったらスクレイピングしよ(自分のまとめ確認
  4. channel・search・videoリソース辺りで取れそうだな、リクエストURL叩いて中身見てみよう
    ■ https://www.googleapis.com/youtube/v3/channels?part=snippet&id=[任意のチャンネルID]&key=[取得したAPIキー]
    ■ https://www.googleapis.com/youtube/v3/search?part=snippet&relatedToVideoId=[任意のvideoid]&type=video&key=[取得したAPIキー]
    ■ https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&regionCode=jp&key=[取得したAPIキー]
  5. searchメソッドでチャンネルID指定して投稿動画を全件取得してから、動画1件毎にvideoメソッドで動画の詳細情報取得する感じでいけそうだな、ユニットの消費量も調べとこう
  6. 2年前の自分の記事だとsearchメソッドのユニット消費量は100ユニットって書いてあるけど、他サイトの記事とか公式ページ確認すると変わってる気がする、実際にリクエストURL叩いて検証してみよう※1
  7. やっぱりsearchメソッドは1回叩いて100ユニット消費固定じゃねーか!自分ので合ってたわ。
  8. という事はデフォのAPIキーだと割り当てが10,000ユニットだから100回API叩くのが限界かな、1回で最大50件の動画取得できるからAPIキー1個につき5,000件まで動画リスト取得するのが限界か
  9. videoメソッドのユニット数も調べておこう
  10. こっちは公式の資料だと「すべてのパーツを取得する videos.list リクエストのコストは約 21 クォータ ユニットになります」って書いてるけどユニット消費のイメージが違うな、取得するパーツを増やしても固定で1ユニットしか消費してないぞ、有利だからいいか※2
  11. どの言語で書こうか、ローカルで使えればいいからpythonでいいか
    ■ 頭の中のプログラム言語振り分けの基準
    ⇒WPプラグイン化:PHP
    ⇒webサービス・ツール、デスクトップアプリ:python
    ⇒自動実行系:GAS
  12. とりあえずurllibでyoutubeAPIのvideoメソッドからデータ取る所から書くか
  13. よし取れた、次はsearchメソッドでチャンネルから動画一覧取得して動画毎に12の処理を行うように合体させよう
  14. おk、これで「チャンネルIDを指定するだけで動画リストと動画情報をまとめて取得する」のはできたな、あとはCSVで保存させる所書いて終わりかな
  15. はい完成、画像と動画作って記事書くか
  16. できた、パチンコ行こう!(ダッシュ)

※1

※2

大体こんな感じです、記録して客観的にみると調べ物している時間が多いですね、手を動かしてコードとか記事を書いている時間は全行程の1/3くらいかもしれない。

まとめ

結構イメージ通りのスクリプトが作れましたね、チャンネルID指定するだけでズバッと全データを取得できる感じにしたかったので満足です(‘ω’)ノ

コメント

  1. のぼる より:

    為になる情報ありがとうございます。
    あるYoutubeチャンネルの動画一覧を作成したいため、本日Pythonを初めて触って
    本記事にあるプログラムを走らせてみました。
    色々ありましたが、500件程の動画の一覧を取得することができました。
    ただ、そのチャンネルは2400本近い動画があり、500件以上の一覧を取得できずに困っています。
    他の記事で、「publishedAfter」等を使った日時指定の説明があるにはあるのですが、中々うまくいきません。
    もしよければ、その部分に日時指定のコマンドを入れると上手い行くか、ご教授いただけると助かります。
    よろしくお願いします。

    • DIYプログラミング管理人 より:

      DIYプログラミングのスマイルです(‘ω’)ノ

      > ただ、そのチャンネルは2400本近い動画があり、500件以上の一覧を取得できずに困っています。

      記事内のサンプルコードはすべての動画を取得できるように作っていますんで一部しか動画が取得できないのは不思議ですね。

      考えられるのは、
      ①色々試しているうちにAPIKEYのリソースを使い切って500件までしか取得できなかった
      ②当該のチャンネルには取得できる動画が500件までしかない
      とかですかね、具体的なチャンネルIDを教えて貰えればもっと詳しく調べられます。

      > 他の記事で、「publishedAfter」等を使った日時指定の説明があるにはあるのですが、中々うまくいきません。

      すいません、どこの記事を参考にされたんでしょうか?

      > もしよければ、その部分に日時指定のコマンドを入れると上手い行くか、ご教授いただけると助かります。

      こっちものぼるさんが何のスクリプトにどんな手を加えようとしているか分からないのでちっとアドバイスできないです、メールとかでスクリプトコードそのまま送って貰えれば調べられます。

      よろしくお願いしますm(_ _)m

      • のぼる より:

        返信ありがとうございます!
        「publishedAfter」を使った説明を書かれていたのはこちらの記事になります。
        https://zenn.dev/jqinglong/articles/1161615fdaa6f6
        https://qiita.com/yuji_saito/items/8f472dcd785c1fadf666

        こちらの記事を読むと「取得件数が500件くらいになると、nextPageToken が返って来なくなります」とあり
        実施にその通りになったため、ある程度数を絞って取得する必要があるのかな?と感じていました。
        また、実施に必要となる情報は
        「publishedAt(公開日)」「title(動画タイトル)」「url(動画ページURL)」「duration(動画の長さ)」の4つになります。

        実際に情報を取得したチャンネルは2つになりますが、できれば公開したくないため、次の返信に書かせていただきます。
        そちらのコメントは非公開でお願いできますでしょうか?
        勝手言って申し訳ありませんがよろしくお願いします。

  2. たら より:

    ド素人の質問ですみません。

    pip install isodate

    で躓いています、、
    どうすればよいでしょうか?

  3. たら より:

    解決できました!

    特定のチャンネルの動画リストを取得したい場合は
    そのチャンネルのAPIである必要はありますか?

    • DIYプログラミング管理人 より:

      DIYプログラミングのスマイルです(‘ω’)ノ

      > そのチャンネルのAPIである必要はありますか?

      youtubeAPIのsearchメソッドにチャンネルIDを指定するパラメータがあるのでそこで指定できますよ。

      よろしくお願いしますm(_ _)m

  4. たいち より:

    はじめまして。 とても分かりやすい記事をありがとうございます!

    記載されているスクリプトを参考に、以下チャンネルの動画一覧の取得をしようと考えております。
    https://www.youtube.com/channel/UCXjTiSGclQLVVU83GVrRM4w

    しかし、のぼるさん同様、500件で取得がストップしてしまいます。
    こちらに関して、事象が解決しているようであれば解決方をご共有いただけますと幸いです。

    よろしくお願いいたします。

    • DIYプログラミング管理人 より:

      DIYプログラミングのスマイルです(‘ω’)ノ

      > こちらに関して、事象が解決しているようであれば解決方をご共有いただけますと幸いです。

      この問題の解決方法を発見しましてちょうど今別の記事にまとめている所です、数日以内には公開できると思いますんで良かったら読んでみて下さい。

      よろしくお願いしますm(_ _)m

      • たいち より:

        ご返信ありがとうございます。そうなんですね!!助かります!記事たのしみにしております!!