0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

APIキーを挿しに行ったら、鍵のほうが要らなくなった ── 眠っていたYouTube一覧ブックをGASで生き返らせた話

0
Posted at

はじめに

Google ドライブの奥から、ZIP がひとつ出てきました。中身は「ユーチューブ (最新情報) .xlsm」。チャンネルを指定すると、そのチャンネルの全動画のタイトル・再生回数・長さ・投稿日を一覧にしてくれる、以前に AI と作った自作ブックです。

作りは真っ当で、YouTube Data API を VBA から直接呼びます。マクロは41本。チャンネル情報→動画ID→動画詳細と三段で API を叩き、50件ずつまとめて取り、ページ送りにも対応して、返ってきた JSON を正規表現でほどく。当時としては、なかなかの完成品でした。

ただ、モジュールの2行目がこうなっていました。

Private Const API_KEY As String = "ここにあなたのAPIキーを貼り付け"

キーだけ抜いて、眠らせてあったのです。理由は覚えています。API キーは取得にクレジットカードの登録が要る場面があり、無料枠があってもお金の匂いがする。自分は良くても、人に「これ使ってみて」と渡せるものにならない──それで棚に上げたのでした。

今回、このブックを供養するつもりで掘り出しました。新しいキーを挿せば動くはずだ、と。ところが作業を始めてみたら、話が予想と違う方向に転がりました。キーを挿すのではなく、鍵そのものが要らなくなったのです。この記事は、その一部始終と実測の記録です。

なお前回は、VBA から生成AI の API を呼んでいる事例を世界中から探させた話を書きました。

VBA から外の API を叩く話つながりですが、今回の相手は生成AI ではなく YouTube のほうです。

TL;DR

  • 眠っていた YouTube 一覧ブックを、API キーを1文字も入れずに復活させました。間に GAS(Google Apps Script)を1枚挟んだだけです
  • GAS は YouTube Data API を標準装備しています。キー取得なし・課金設定なし。それをウェブアプリとして公開すると URL が1本生え、VBA からその URL を叩けます
  • VBA 側の改造は、通信がすべて通る関数**1本(13行)**だけ。三段呼びも50件バッチも正規表現の JSON 解析も、昔のコードのまま動きました
  • ただし最初の実測は惨敗でした。1,387本の一覧化に107秒。以前キー直叩きで10秒だった処理です
  • 犯人は往復の数でした。HTTP を56回、遅い中継越しに叩いていた。ループを GAS 側へ移して往復を1回にしたら、13.8秒になりました
  • 「キーもカードも要らない」の代価が、直叩き比でおよそ数秒。この交換なら、いくらでも払います

発掘したブックの中身

先に、このブックの構造を説明しておきます。あとで効いてくるからです。

YouTube のチャンネル全動画を取るには、API を三段で呼びます。

  1. channels ── チャンネルIDから「アップロード動画プレイリスト」のIDを得る
  2. playlistItems ── そのプレイリストを50件ずつページ送りして、全動画のIDを集める
  3. videos ── 動画IDを50個ずつまとめて渡し、タイトル・統計・長さを取る

ブックはこれを MSXML2.XMLHTTP で愚直にやります。JSON パーサーは使わず、VBScript.RegExp"title": "(.*?)" を拾う方式。日本語が \u30d3\u30c7\u30aa のようなユニコード表記で返ってくるので、それを戻す自前関数まで持っています。参照設定なし、すべて CreateObject。つまりどの PC に持っていっても動く作りです。

そして API を呼ぶ場所は13箇所ありますが、全部がこの1本の関数を通ります。

Function CallAPI(http As Object, url As String) As String
    On Error Resume Next: http.Open "GET", url, False: http.Send: CallAPI = http.responseText: On Error GoTo 0
End Function

URL を渡すと本文が返る、それだけの通り道です。13箇所の呼び出しが全部ここを通る──この一本道が、今回の改造をほとんどタダにしてくれました。

鍵を取らない、という選択肢

さて、復活させるには新しい API キーを取るのが素直です。ただ、それでは眠らせた理由がそのまま残ります。キーの取得手順を越えられる人にしか渡せない道具のままです。

ここで思い出したのが GAS でした。GAS には「Advanced Google Services」という仕組みがあって、YouTube Data API が標準装備されています。エディタのメニューからひとつ有効にするだけ。キー取得なし、カード登録なし、課金なし。

そして GAS は「ウェブアプリとしてデプロイ」すると、ただの URL が1本生えます。URL なら VBA から叩けます。つまり──

VBA → GAS の URL → (GASが自分の権限でYouTube APIを呼ぶ) → JSON がそのまま返る

GAS 側に置いたのは、およそ30行の転送係です。骨子だけ示します。

function doGet(e) {
  // 合言葉の照合と、通すエンドポイントの許可リスト確認(省略)
  const res = UrlFetchApp.fetch(
    'https://www.googleapis.com/youtube/v3/' + e.parameter.q,
    { headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() } }
  );
  return ContentService.createTextOutput(res.getContentText())
                       .setMimeType(ContentService.MimeType.JSON);
}

受け取ったパスを本家 YouTube API に転送して、返ってきた JSON をそのまま返す。本家と同じ形で返るので、受け側の正規表現もユニコード戻しも、一切直さなくていいわけです。

VBA 側は、例の一本道 CallAPI の中に「GAS の URL が設定されていたら、宛先を本家から GAS に付け替える」という分岐を足しました。全部で13行。呼び出し側の13箇所は無改造です。

最初の躓き ──「アクセスが拒否されました」

意気揚々と動かしたら、うんともすんとも言いません。エラーも出ない。結果だけが空。

この「エラーすら出ない」が曲者でした。CallAPIOn Error Resume Next で例外を握りつぶす作りなので、通信が失敗しても静かに空文字が返るだけなのです。そこで HTTP オブジェクトを4種類並べて、同じ URL を叩き比べる実験をしました。結果がこれです。

MSXML2.XMLHTTP             エラー -2147024891 アクセスが拒否されました
MSXML2.XMLHTTP.6.0         エラー -2147024891 アクセスが拒否されました
MSXML2.ServerXMLHTTP.6.0   成功 200
WinHttp.WinHttpRequest.5.1 成功 200

原因は GAS の癖にありました。GAS のウェブアプリは、呼ぶと script.google.com から script.googleusercontent.com へ、別ドメインへのリダイレクトを返します。XMLHTTP は Internet Explorer のセキュリティゾーンの上で動く古株なので、これを危険とみなして蹴る。ゾーンを見ない ServerXMLHTTP なら素通りします。

本家 googleapis.com はリダイレクトをしないので、キー直叩き時代には一度も問題にならなかった罠です。**「今まで動いていたブックが、GAS 経由にした途端に無反応になる」**という形で現れるので、同じことをする方は覚えておいて損はないと思います。直しは CreateObject の文字列ひとつです。

動いた ── そして107秒

差し替えて実行すると、通りました。三段呼びが全部つながり、日本語タイトルも化けずに並びます。API キーの行は「ここにあなたのAPIキーを貼り付け」のまま。鍵穴を空にしたまま、錠が開いたのです。

さっそく実測です。対象は動画1,387本のチャンネル。以前この規模をキー直叩きで約10秒で取っていました。今回は──

107.38秒。

10倍遅い。正直に書くと、ここで少ししょんぼりしました。ただ、原因を数えたらはっきりしました。1,387本だと、動画IDの収集に28ページ、詳細の取得に28バッチで、HTTP 往復が56回あります。GAS 中継は1往復ごとにリダイレクトを1回余分に踏み、さらに GAS から本家への問い合わせが走る。1往復あたり約1.9秒。つまり──

中継が遅いのではなく、遅い中継を56回叩いていた。

ループを、向こう岸へ移す

そうと分かれば、直し方は一つです。56回の往復をこちらでやるから遅い。なら、ループごと GAS 側に引っ越せばいい。

GAS の転送係に「チャンネル一覧モード」を足しました。チャンネルIDを1個受け取ると、GAS が自分でページ送りを回して全動画IDを集め、詳細の28バッチは UrlFetchApp.fetchAll並列に取りに行き、JSON の解釈も日時の日本時間への変換も済ませて、整形済みのタブ区切りテキストを一発で返す。VBA 側は1回だけ URL を叩き、返ってきたテキストを分割してシートに貼るだけです。

GAS から YouTube API への通信は Google のサーバー同士の会話なので、こちらの回線を通りません。往復56回が、1回になりました。

再実測です。同じ1,387本で──

方式 実測
キー直叩き(以前) 約10秒
GAS 中継・素朴版(1往復=1お使い) 107.38秒
GAS 中継・ループ引っ越し版 13.84秒

初回だけは16.4秒でした。VBA のコンパイルと GAS 側の起動が乗るぶんで、2回目からは13秒台で安定します。ついでに「最新50件だけ取る」いつもの使い方も測ったら、4.49秒。体感はほぼ一瞬です。

実は、この勝負は二度目だった

ここで白状することがあります。「往復が多いと遅い」──この犯人に会うのは、実は二度目なのです。

昨年の暮れ、同じ一覧化をスプレッドシート版と Excel 版で対決させて、動画にしていました。

このときの実測が、1,300本超のチャンネルでスプレッドシート約30秒、Excel は10秒未満。動画の中で私は、スプレッドシートが遅い犯人を「クラウドの遅延。PC とサーバーを何度も往復する通信時間」と名指ししています。

今回の107秒は、その同じ犯人でした。キーを捨てる代わりに中継を挟んだら、自分の Excel 版が「往復で遅い側」に回ってしまった。そして今回は、往復そのものを畳んで取り返した。同じ犯人に二度会って、二度目は勝ったわけです。

もうひとつ、数字を並べると見えてくることがあります。スプレッドシート版も、今回の GAS 中継版も、YouTube からデータを集めているのは同じ Google のサーバーです。エンジンは同じ。違うのは、集めた結果を受け取って並べる側が、ブラウザの向こうのスプレッドシートか、手元の Excel かだけ。それで30秒と13秒の差が付きます。大量の行を捌く作業台としては、手元の表計算のほうが速い──去年の対決の結論は、エンジンを Google に寄せても変わりませんでした。

交換レートの話

最終的な帳尻を書きます。

キー直叩きの10秒に対して、GAS 中継は13〜14秒。約3〜4秒の上乗せです。これは GAS を1往復挟む固定費なので、たぶんこれ以上は縮みません。

その代わりに消えたものを並べます。

  • API キーの取得手順(Google Cloud のプロジェクト作成から始まるあれ)
  • クレジットカード登録の心理的な壁
  • ブックにキーを書き込むこと自体(キーの行は空のままです)

つまり鍵とカードを捨てて、代価は数秒。このブックを眠らせた理由が「お金の匂いのするものは人に渡せない」だったことを思うと、これは完全な解決です。渡された側がやることは、ブックを開いてボタンを押す──それだけになりました。

事実と見立ての仕分け

例によって仕分けます。

事実:GAS の YouTube Advanced Service はキー取得・課金設定なしで有効化できること。API キーの定数を空にしたまま三段呼びが完走したこと。1,387本の実測が素朴版107.38秒・ループ引っ越し版13.84秒(初回16.41秒)・最新50件4.49秒であること(いずれも当夜の実測)。MSXML2.XMLHTTP が GAS のリダイレクトで「アクセスが拒否されました」(-2147024891) を返し、ServerXMLHTTP.6.0 では通ること(4オブジェクトの叩き比べで確認)。昨年12月の動画でスプレッドシート約30秒・Excel10秒未満と実測公開していること。

見立て:素朴版の遅さの主因を「往復56回 × 中継の固定費」と読んでいること(1往復あたりの内訳を厳密に分解したわけではありません)。初回と2回目の差をコンパイルと GAS 側の起動に帰していること。「これ以上は縮まない」も、固定費の構造からの推測です。

正直な線引き

  • GAS のウェブアプリは「全員がアクセス可」で公開する形なので、URL を知られれば誰でも叩けます。合言葉と、通すエンドポイントの許可リスト(今回は YouTube の読み取り系5種だけ)で絞ってありますが、合言葉はブックを開けば読める場所にあります。漏れても公開データの読み取りしかできない構成にしておくのが前提です
  • GAS の無料枠は YouTube API 換算で1日1万ユニット。今回の全件取得1回が約57ユニットなので個人利用では困りませんが、URL を配れば配った全員が同じ枠に相乗りします。広く配るなら「各自が自分のアカウントでデプロイする」手順書のほうが筋です
  • この方式は「投げて返ってくる」API 向けです。応答を少しずつ流すストリーミングは GAS では中継できません
  • キーが要らないのは、YouTube のように GAS が標準サービスとして持っているものの話です。そうでない API はこの限りではありません
  • ブック自体は現時点で未公開です。VBAマネージャーなどの道具は公開リポジトリにあります

おまけ ── 動画にもなりました

この記事の話は、5分の動画にもしてあります。合成音声のナレーションとスライドで、蔵から出てきたブックが動き出すまでを追う形式です。

おわりに

キーを挿すつもりで蔵から出したブックは、鍵穴を空にしたまま動いています。

三段呼びも、50件バッチも、正規表現の JSON ほどきも、書いた当時のまま一行も直していません。直したのは通り道の関数1本と、向こう岸に置いた転送係だけ。眠っていた半年のあいだにブックが古びたのではなく、鍵という前提のほうが先に古びていた──そういうことだったのだと思います。

蔵には、まだ何冊か眠っています。

それでは、また。

1.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?