GAS x YouTube Data API v3 ではてブで人気のYouTube動画のプレイリストを作るの巻

この記事ははてなエンジニアアドベントカレンダー 2023の7日目の記事です。

6日目の記事は id:tomato3713の「組み込み用途向けのGo言語のサブセットTinyGoによるM5Stack Basicの制御を試す - tomato3713’s blog」でした。


はてなブックマークでは、日々インターネット上の注目コンテンツが更新されています。様々なサイトにブックマークがつけられていますが、その中にはもちろんYouTubeの動画もあります。はてなブックマークのページから各動画ページへ遷移して動画を観ても良いんですが、もしかすると特定期間の人気エントリに入った動画をプレイリストにしたらおもしろいのでは?と考えて、Google Apps Scriptでコードをかいてみました。

全体の方針

できあがったプレイリスト

2023年10月の人気エントリに入っていたYouTube動画のプレイリストです。やはりカテゴリを指定しないと雑多感ありすぎて連続してみたいという気持ちにならないですね...。

ということで「テクノロジー」のカテゴリを指定してみました。1ヶ月という期間だと動画の数が少なかったので期間を1年にして、話題の変化を知りたかったので「2022年」と「2023年」でそれぞれ作ってみました。2023年はChatGPT関連の動画が多く見られますね。

「アニメとゲーム」「エンタメ」で、10月の動画リストを作ってみました。「アニメとゲーム」の方はだいぶ分かりやすいですね。

コード

function exec() {
  createHBYouTubePlaylist('2023-10-01','2023-10-30','アニメとゲーム');
}

function createHBYouTubePlaylist(startdate, enddate, category , users) {
  let title = startdate + 'から' + enddate + 'にはてブで人気エントリになった動画';
  if (category) {
    title += " (" + category+ ")";
  }
  const results = createPlaylist(title,title); // 「非公開」でプレイリストを作成
  const videos = getYouTubeHotentry(startdate, enddate, category , users);
  let pattern = /https?:\/\/www\.youtube\.com\/watch\?v=(.*)/g;
  for (var i = 0 ; i < videos.length; i++) {
    var result = videos[i].match(pattern);
    let videoId = RegExp.$1;
    try {
      YouTube.PlaylistItems.insert({
        snippet: {
          playlistId: results.id,
          resourceId: {kind:'youtube#video', videoId: videoId},
        }
      },['snippet']);
    } catch (e) {
      console.log(e);
    }

  }
}

function createPlaylist(title,description,privacyStatus) {
  const results = YouTube.Playlists.insert(
    {
      snippet: {
        title: title || 'プレイリスト',
        description: description || 'プレイリストの説明文'
      },
      status: {
        privacyStatus: privacyStatus || 'private'
      }
    },
    ['snippet','status']
  );
  return results;
}

function getYouTubeHotentry(dateBegin, dateEnd , category , users ) {
  dateBegin = dateBegin || '2023-01-01';
  dateEnd = dateEnd || '2023-01-31';

  const categories = {
    "世の中" : 1,
    "政治と経済" : 1,
    "暮らし" : 1,
    "学び" : 1,
    "テクノロジー" : 1,
    "おもしろ" : 1,
    "エンタメ" : 1,
    "アニメとゲーム" : 1
  };
  category = (categories[category]) ? category : null;

  users = users || '5';

  const rss = XmlService.getNamespace("", 'http://purl.org/rss/1.0/');
  const ns_rdf = XmlService.getNamespace("rdf","http://www.w3.org/1999/02/22-rdf-syntax-ns#");
  const ns_dc = XmlService.getNamespace("dc","http://purl.org/dc/elements/1.1/");
  let videos = [];
  let page = 1;
  while (videos.length < 30) {
    let url = "https://b.hatena.ne.jp/q/site%3Awww.youtube.com%2Fwatch?safe=on&target=text&mode=rss&users="+ users +"&sort=popular&date_end="+ dateEnd +"&date_begin=" + dateBegin + "&page=" + page;
    let responseText = UrlFetchApp.fetch(url).getContentText();
    let xml = XmlService.parse(responseText);
    let root = xml.getRootElement();
    let items = root.getChildren('item', rss);
    if (items.length == 0) {
      break;
    }
    items.forEach((item) => {
      if (category == null || item.getChild('subject',ns_dc).getValue() == category) {
        videos.push(item.getAttribute("about",ns_rdf).getValue())
      }
    });
    page ++;
  }
  return videos.slice(0,30);
}

使い方

Googleドライブの「+新規」> 「その他」 > 「Google Apps Script」を選択

エディタのエリアに上記コードをコピペして貼り付け

左の「サービス + 」をクリックして表示されたダイアログで「YouTube Data API v3」を選択して「追加」

execの中の日付、カテゴリを書き換えて、メニューの関数プルダウンから「exec」を選択し、「実行」をクリック

初回のみ権限を求められるので内容を確認して「許可する」をクリック

注意点

YouTubeAPIは、1日の利用量がクォータ(Quota)という単位で決まっています。Googleに申請して特別対応してもらわなければ、基本的には1日10,000クォータが上限となります。APIのタイプごとに消費されるクォータが設定されていて、サーバーの処理の負荷が高いものについては消費されるクォータが多く設定されています。消費される量については以下のページで見ることができます。

YouTube Data API(v3) - 割り当て計算ツール  |  Google for Developers

例えば「再生リスト」のlistメソッド(再生リストの一覧を取得する)はREADなので消費量は「1」で、insertメソッド(再生リストを作る)はCREATEなので消費量は「50」という感じ。最も多いのが動画のinsertメソッドで1,600となっていますね。一日に6本をアップロードするだけで上限近くまでいっちゃいますね。

今回のスクリプトでは、1回の動作でプレイリストを1つinsert(50quota)して、そのプレイリストに動画を最大30本追加(50quota x 30 = 1500quota)するので、1,550quotaとなり、およそ動画を1本追加するのと同じくらいのコストを使うことになります。