YouTube (Analytics/Reporting v1) API のメモ

そこまで有益ではない記事

環境

PHP 8.1.18
API のバージョンは少なくとも Reporting API の方は v1 です

API を利用するまで

ご存じの通り、YouTube の所有者は Google です。
Google API は基本的には OAuth 2.0 による認可を経て、API を利用するためのトークン(以下、アクセストークン)を取得し、
リクエスト毎にアクセストークンを投げることで API の利用を可能にします。

ちなみに、Google API では OAuth 2.0 を利用しないフローの 1 つにサービスアカウントがありますが、
YouTube Analytics/Reporting API はサービスアカウントフローを利用できません。

https://developers.google.com/youtube/reporting/guides/authorization?hl=ja
サービス アカウント向け OAuth 2.0 フローは、ユーザー情報にアクセスしないサーバー間のやり取りをサポートします。
YouTube Reporting APIYouTube Analytics API は、このフローをサポートしていません。

準備

まずは普通にチャンネルを取得します。
GCP にアクセスし、プロジェクトを作成します。

次に、以下の URL から YouTube Analytics/Reporting API を有効にします。
https://console.cloud.google.com/apis/library/youtubeanalytics.googleapis.com?hl=ja
https://console.cloud.google.com/apis/library/youtubereporting.googleapis.com?hl=ja

更に、認証情報 > 同意画面を構成 より、OAuth 同意画面を作成します。
User Type は可能な限り内部をおすすめしますが、今回は外部にし、テストユーザーに 1 つ追加します。
スコープは以下を追加しました。
https://www.googleapis.com/auth/yt-analytics.readonly
https://www.googleapis.com/auth/yt-analytics-monetary.readonly
https://www.googleapis.com/auth/youtube
https://www.googleapis.com/auth/youtubepartner

次に、 認証情報 > OAuth クライアント ID の作成 をします。
ウェブアプリケーションを指定し、
承認済みのリダイレクト URIhttp://localhost:8000/callback.php を指定しました。
この時、認証情報を json でダウンロードし、client_secret.json にリネームします。

認証

PHP 用のクライアントライブラリを composer でインストールします。

$ composer require google/apiclient:^2.12.1

php ファイルを作成し、まずは認証をするための URL を発行します。
同じディレクトリに client_secret.json を設置します。

// youtube.php

require 'vendor/autoload.php';

use Google_Client as Client;

$client = new Client();
$client->setAuthConfig(__DIR__ . '/client_secret.json');
$client->setScopes([
     'https://www.googleapis.com/auth/yt-analytics.readonly',
     'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',
     'https://www.googleapis.com/auth/youtube',
     'https://www.googleapis.com/auth/youtubepartner',
]);
$client->setRedirectUri('http://localhost:8000/callback.php');
$client->setAccessType('offline');

print_r($client->createAuthUrl() . "\n");

Google_Client の createAuthUrl() で認証 URL を作成します。
認証 URL にはクエリパラメータが付与されており、セットしたクライアント ID やスコープ等のデータが付属しています。
作成された URL をブラウザ上で開き、通常通り認証します。
スコープは基本的には全て選択します。

認証が完了すると、事前に指定したリダイレクト URL
http://localhost:8000/callback.php)にクエリパラメータが付与された URL にリダイレクトされます。
現時点では当然 404 なはずです。
この時のリダイレクト URL には認可コード(Authorization Code)とブラウザ上で選択したスコープが付与されています。

アクセストークンの取得

まず、リダイレクト URL に指定した callback.php を作成します。
いろいろ不要なパラメータがありそうなんですが特に検証はしてないです。

require 'vendor/autoload.php';

use Google_Client as Client;

$client = new Client();
$client->setAuthConfig(__DIR__ . '/client_secret.json');
$client->setScopes([
     'https://www.googleapis.com/auth/yt-analytics.readonly',
     'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',
     'https://www.googleapis.com/auth/youtube',
     'https://www.googleapis.com/auth/youtubepartner',
]);
$client->setRedirectUri('http://localhost:8000/callback.php');
$client->setAccessType('offline');

print_r($client->fetchAccessTokenWithAuthCode($_GET['code']));

次に PHP のビルドインサーバをローカルに立てます。
下記のコマンドを実行します。

// t オプションで指定したディレクトリのファイルを実行できるようにする
$ php -S localhost:8000 -t .

この状態で先ほど 404 を踏んだリダイレクト URL(クエリパラメータ付き)に GET リクエストします。

$ curl http://localhost:8000/callback.php?code=xxx&scope=xxx

fetchAccessTokenWithAuthCode() の戻り値は単純な array で、内容は以下のようになっています。
これでアクセストークン(およびリフレッシュトークン)を取得できました。

access_token: string
expires_in: int
refresh_token: string
scope: "https://www.googleapis.com/auth/youtubepartner
https://www.googleapis.com/auth/yt-analytics-monetary.readonly
https://www.googleapis.com/auth/yt-analytics.readonly
https://www.googleapis.com/auth/youtube"
token_type: "Bearer"
created: int

ここで取得したアクセストークンには期限があり、expires_in で指定された時間分だけ利用ができます。
期限が切れた場合、リフレッシュトークンを利用して再度アクセストークンを取得する必要があります。

print_r($client->fetchAccessTokenWithRefreshToken({{ 先ほど取得したリフレッシュトークン
}}));

リフレッシュトークンは通常無期限で使用できますが、以下のルールで失効する場合があります。
https://developers.google.com/identity/protocols/oauth2?hl=ja#expiration

リフレッシュトークンはアクセスタイプが offline かつ初回の認証時でなければ発行されない

2 回目以降の認証ではどう頑張ってもリフレッシュトークンが発行されません。
もし誤ってリフレッシュトークンを発行できなかった、または発行したけど紛失した場合は以下の URL から一度権限を削除する必要があります。
https://myaccount.google.com/u/0/permissions

API の利用

YouTube Analytics API の例です。

use Google_Client as Client;
use Google_Service_YouTubeAnalytics;

$client = new Client();
$client->setAuthConfig(__DIR__ . '/client_secret.json');
$client->setAccessToken({{ 先ほど取得したアクセストークン }});
$client->setScopes([
     'https://www.googleapis.com/auth/yt-analytics.readonly',
     'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',
     'https://www.googleapis.com/auth/youtube',
     'https://www.googleapis.com/auth/youtubepartner',
     'https://www.googleapis.com/auth/adsense.readonly',
]);
$client->setRedirectUri('http://localhost:8000/callback.php');
$client->setAccessType('offline');

$analytics = new Google_Service_YouTubeAnalytics($client);
$result = $analytics->reports->query([
     'ids' => 'channel=={{ チャンネル ID }}',
     'startDate' => '2023-06-18',
     'endDate' => '2023-06-30',
     'metrics' => 'views,comments',
     'dimensions' => 'day',
]);

雑多

アクセストークンの期限が切れているかどうかをどのようにチェックするか

一応、Google_Client には isAccessTokenExpired() といういかにも期限切れかどうかを判定するメソッドが用意されているのですが、
実装を見ると、Google_Client インスタンスの token 配列に expires_in が含まれていて、かつ現在が期限内かどうかを確かめるという、
簡単に言えばサーバに直接見に行くものではないので正直あまり使えないものになっています。
(実際にアプリで運用するならアクセストークンを取得しない場合のリクエストの方が大半だろうし)

API にリクエストが飛ぶメソッドを実行したタイミングで、try-catch 使って例外捕まえるのが一番楽な気がしてます。
期限切れの際は 401 エラーが返却されることだけ分かっています。

use Google\Service\Exception as GoogleServiceException;

try {
     $result = $analytics->reports->query([
         'ids' => 'channel=={{ チャンネル ID }}',
         'startDate' => '2023-06-18',
         'endDate' => '2023-06-30',
         'metrics' => 'views,comments',
         'dimensions' => 'day',
     ]);
} catch (GoogleServiceException $e) {
     // これもうちょっといい特定方法ないのか
     if ($e->getCode() === 401) {
         print_r($e->getErrors()[0]);
     }
     exit;
}

Analytics API と Reporting API の違い

あんまり調べてない。

Analytics は即時にコメント数とか再生数のデータを json で取得できて、
Reporting はデータを CSV で出力するためのジョブを発行して、後ほど CSV ファイルのダウンロード URL を取得できる、ということらしいです。たぶん CSV は日次発行です。
あと、metrics 等を見ると Reporting API の方が取得できるデータの種類が充実しているように見えます。

PHPAPI クライアントは 各 API へどのメソッドで HTTP リクエストを飛ばすのか

YouTube Analytics だと $analytics->reports->query() だったり、
Google Adsense だと $adsense->accounts_reports->generate() だったりでバラバラです。

基本的に各 API のリファレンス通りだったりするので、ブラウザでそれらを読むのもいいですし、
vendor/google/apiclient-services/src 直下の各 API に相当するクラスのコンストラクタを見るのが一番直感的だったりもします。