はじめに
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 を三段で呼びます。
-
channels── チャンネルIDから「アップロード動画プレイリスト」のIDを得る -
playlistItems── そのプレイリストを50件ずつページ送りして、全動画のIDを集める -
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箇所は無改造です。
最初の躓き ──「アクセスが拒否されました」
意気揚々と動かしたら、うんともすんとも言いません。エラーも出ない。結果だけが空。
この「エラーすら出ない」が曲者でした。CallAPI は On 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本と、向こう岸に置いた転送係だけ。眠っていた半年のあいだにブックが古びたのではなく、鍵という前提のほうが先に古びていた──そういうことだったのだと思います。
蔵には、まだ何冊か眠っています。
それでは、また。
