マイペースなRailsおじさん

Ruby、Ruby on Rails、オブジェクト指向設計を主なテーマとして扱います。だんだん大きくなっていくRuby on Rails製プロダクトのメンテナンス性を損なわない方法を考えたり考えなかったりしている人のブログです。

YouTube Data APIを使って登録者の少ないYouTubeチャンネルを探す

YouTubeからデータを取得しまくってアプリケーションを作ろうと思っていたが、一日あたりにリクエストできる量が限られていて結構厳しくて頓挫した。 アプリケーションを作る前にやりたいことができるのか確かめるためにスクリプトを書いたので、供養のために記事にしておく。

背景

いわゆる底辺Youtuberを見つけて応援したかった。 しかしながら、彼らを効率的に探す方法が見当たらなかった。

頑張って人力で探す方法は下記ページにまとめられている。

底辺ユーチューバー 探し方4選 見つけ方 YouTuber 登録数が少ない | 知恵袋wikiまとめ

そこで、YouTubeAPIを使うことで、効率的に底辺YouTuberを見つけることができないか試した。

やりたいこと

底辺YouTuberには明確な定義がないので、探す対象を「登録者が少ないが、活発に活動しているYouTubeチャンネル」とした。

「登録者が少ないが、活発に活動しているYouTubeチャンネル」を、仮に次のように定義した。

  • 登録者が1万人以下
  • 直近一ヶ月の動画投稿数が15本以上

YouTube Data API

YouTube Data APIは、Youtubeが公式に提供しているAPI群。 情報収集や分析に必要なAPIは一通り揃っている。

API Reference  |  YouTube Data API  |  Google Developers

利用制限

GCPのアカウントがあれば誰でも利用できる。クォータと呼ばれる利用上限を設けている。クエリごとに消費するクォータが異なるが、ざっくりいうと多くの情報を取得するほどクォータを消費する。

YouTube Data APIのクォータ数増加申請が通った件の振り返り - Qiita

クォータは、GCPのコンソールの「IAMと管理」→「割当て」から確認できる。

f:id:ytnk531:20210925073628p:plain

取得方法の設計

次の手順で、「登録者が1万人以下かつ直近一ヶ月の動画投稿数が15本以上チャンネルのリスト」を得る。

  1. Search: list APIを用いて、すべてのチャンネルのIDを得る https://developers.google.com/youtube/v3/docs/search/list

  2. Channels: list APIを用いて、1. で得たチャンネルの登録者数とアップロード動画リストのIDを取得する Channels: list  |  YouTube Data API  |  Google Developers

  3. 2で得た登録者数を用いて1のチャンネルリストを登録者数1万人以下のものに絞り込む

  4. 3で得たチャンネルのアップロード動画リストIDを用いて、アップロード動画を取得する

  5. 4で得たアップロード動画リストを用いて、一ヶ月の動画投稿数が15本以上のチャンネルを絞り込む

結論から言うと、この設計ではうまく行かない。1のチャンネルの取得の時点で、簡単に利用制限に引っかかってしまう。このため、APIリクエストの少ない別の方法が必要になる。

2まで作ってうまく行かないことに気がついたので、次節移行に、以下に2までをrubyで実行する方法を示す。

下準備

YouTube Data APIを使う前に、下記の準備が必要。

  • GCPへの登録
  • APIキーの発行

こちらのページに詳しく記載されている。

blog.codecamp.jp

APIを色々たたいて実験したかったのでAPIドキュメントをよみつつpostman を利用した。 postmanの利用でもクォータは消費されるので注意する。

Search: list APIを用いて、すべてのチャンネルのIDを得る

API通信にfaradayを使いたいのでインストールしておく。

gem install faraday faraday_middleware

search: list APIを利用して、チャンネルを50件ずつ取得する。

  • 本来は取得できる全量を取得したいが6×50で300件としておく。
  • next_page_tokenを使ってページネーションするので、取得結果とセットで同じオブジェクトにまとめる
require 'faraday'
require 'faraday_middleware'

class ChannelResult
  attr_reader :channels, :next_page_token

  def initialize(channels, next_page_token)
    @channels = channels
    @next_page_token = next_page_token
  end
end

def fetch_channels(next_page_token = nil)
  url = "https://www.googleapis.com/youtube/v3/search?part=id&type=channel&maxResults=50&regionCode=JP&key=#{API_KEY}"
  conn = Faraday.new(url: url) { |faraday|
    faraday.adapter Faraday.default_adapter
    faraday.response :json
  }
  response = conn.get(nil, "pageToken" => next_page_token)
  pp response.body
  channels = response.body["items"].map { _1["id"]["channelId"] }
  next_page_token = response.body["nextPageToken"]

  ChannelResult.new(channels, next_page_token)
end

def channels
  t = nil
  (1..6).flat_map do
    resp = fetch_channels(t)
    t = resp.next_page_token
    resp
  end
end

puts channels

Channels: list APIを用いて、チャンネルの登録者数を取得する

  • idを指定することで、まとめて50件取得できる
  • レスポンスの構造はドキュメントを見て把握する
class ChannelDetail
  attr_reader :id, :subscribers, :title

  def initialize(id, subscribers, title)
    @id = id
    @subscribers = subscribers
    @title = title
  end

  def format
    "#{title} #{subscribers}"
  end
end

def channel_details(list)
  ids = list.channels.join(",")
  url = "https://www.googleapis.com/youtube/v3/channels?part=statistics,snippet&id=#{ids}&key=#{API_KEY}"
  conn = Faraday.new(url: url) { |faraday|
    faraday.adapter Faraday.default_adapter
    faraday.response :json
  }
  response = conn.get
  response.body["items"].map do
    ChannelDetail.new(
      _1["id"],
      _1["statistics"]["subscriberCount"],
      _1["snippet"]["title"]
    )
  end
end

def ch(channel_lists)
  channel_lists.flat_map do |list|
    channel_details(list)
  end
end

details = ch(channels)
sorted = details.sort_by do
  _1.subscribers.to_i
end

実行すると、こんなかんじでランキング形式でチャンネルと登録者数を得られる

f:id:ytnk531:20210925082029p:plain

コード全量

gist.github.com

問題点

API呼び出しとデータ取得の量が多すぎるため、一瞬でクォータの上限に達する。

今後

少ないAPI呼び出して所望のチャンネルを得られるような工夫をする。 searchの時点である程度絞れるようにするとよさそう。