[PR]
本サイトはアフィリエイト広告を利用しています。
Pinpoint Information Hubでは、各電力会社が公開している停電情報を取得し、 共通形式へ変換して表示する仕組みを開発しています。
その中でも、最後まで取得方法に悩まされたのが 東京電力パワーグリッドの停電情報でした。
東京電力では、次のようなXML形式の停電情報が公開されています。
https://teideninfo.tepco.co.jp/flash/xml/00000000000.xml
普通のChromeでこのURLを開くと、XMLは問題なく表示されます。
ところがWordPressやサーバー上のcurlから取得すると、 同じURLなのにトップページへリダイレクトされたり、 期待したXMLではなくHTMLが返されたりしました。
ここから長い検証が始まりました。
まず試したのは一般的な方法です。
しかし結果は安定しませんでした。
代表的な応答は次のようなものでした。
HTTP 302
Location: /
Server: AkamaiGHost
XMLのURLを要求しているにもかかわらず、 トップページへ戻されます。
さらに、リダイレクトを自動追跡すると一見取得に成功したように見えても、 実際に保存されていたのはXMLではなくトップページのHTMLでした。
この経験から、 HTTP 200だけでは成功判定にしてはいけない という重要な教訓も得ました。
同じPC、同じインターネット回線でも結果が違いました。
通常のChrome → XML表示成功
curl.exe → リダイレクト
Headless自動取得 → 失敗
単純なfetch() → 失敗
つまり単純にIPアドレスだけで判定されているわけではなさそうです。
ブラウザのCookie、セッション、JavaScriptの実行状態、 HTTP通信の特徴など、複数の要素によって結果が変わっている可能性があります。
ここで発想を変えました。
「HTTPクライアントでChromeを真似する」のではなく、 本物のChrome自身に取得させればいいのではないか?
そこで登場したのが Chrome DevTools Protocol(CDP)です。
CDPを使うと、Chromeをプログラムから操作できます。
最初は、東京電力トップページを開いた後にJavaScriptの
fetch() でXMLを取得する方法を試しました。
しかし、これも失敗しました。
検証していて、非常に面白い違いに気付きました。
人間がChromeを操作した場合は、 東京電力トップページを開いたあと、 XMLのURLを別のページとして開けば正常に表示されます。
そこでCDPでも同じ操作順序を再現しました。
タブ1
東京電力トップページを開く
↓ 数秒待つ
タブ2
停電情報XMLをトップレベルページとして開く
すると結果が変わりました。
TAB1_NAVIGATE
https://teideninfo.tepco.co.jp/
TAB2_NAVIGATE
https://teideninfo.tepco.co.jp/flash/xml/00000000000.xml
HTTP 200
RESULT OK
Windows上のHeadless Chromeで、 ついに完全自動取得に成功しました。
fetchで取得するのと、 ブラウザのページ遷移として取得するのでは挙動が違った のです。
ここでさらに欲が出ました。
Windows PCで取得してFTPでサーバーへ送る方法でも運用できます。
しかし理想は、 WordPressが動いているレンタルサーバー内ですべて完結すること です。
そこでSSHでサーバー環境を調査しました。
CPU:x86_64
PHP:8.4系
exec:利用可能
proc_open:利用可能
shell_exec:利用可能
curl:利用可能
unzip:利用可能
外部プログラムをPHPから起動できる環境でした。
ところがChromeもChromiumもインストールされていません。
Chrome for TestingのHeadless Shellをユーザー領域へ配置してみました。
しかし、サーバーのglibcは2.17。 現行Chromeが必要とするライブラリより古く、起動時にエラーとなりました。
GLIBC_2.18 not found
GLIBC_2.25 not found
root権限のない共用レンタルサーバーなので、 OS側のglibcを更新するわけにもいきません。
ここで最新Chromeを使う方法は断念しました。
次に、glibc 2.17で動作可能な世代のChromiumを探しました。
Chromium公式のSnapshotから、 2021年のChromium 94系をユーザー領域へ配置して試します。
Chromium 94.0.4606.0
結果は成功。
No missing libraries detected
さらにHeadlessモードで起動し、 ローカルのCDPポートへ接続しました。
Browser:
HeadlessChrome/94.0.4606.0
Protocol-Version:
1.3
これで、 共用レンタルサーバー上でもHeadless Chromium+CDPが動作する ことが確認できました。
Windowsで成功した方法をそのままサーバーへ移植します。
Headless Chromium起動
↓
タブ1
https://teideninfo.tepco.co.jp/
↓
数秒待機
↓
タブ2
東京電力の停電情報XML
↓
XML内容取得
さらに検証を進める中で、 サーバー版では view-source: を使ってXMLをブラウザ自身に表示させる方法 が安定して利用できることが分かりました。
CDPのRuntime機能からChromeが表示している内容を取得すると、 ついに次の結果になりました。
BYTES=7973
CANONICAL_ROOT=YES
ExitCode=0
取得した内容の中に、 東京電力公式XMLのルート要素が存在することを確認できました。
レンタルサーバー内だけでの取得成功です。
検証だけで終わらせず、 この取得方法をPinpoint Information Hubへ組み込みました。
本番版では東京電力の取得を次の順番で試します。
WordPress HTTP API
↓
PHP cURL
↓
Server Chromium CDP
↓
公式HTML等の既存フォールバック
通常のHTTP取得が成功すれば、 わざわざChromiumは起動しません。
従来方法が失敗したときだけ、 サーバー上のChromiumを一時的に起動してCDP方式へ切り替えます。
取得後はChromiumを終了し、 一時プロファイルも削除します。
Information Hubの管理画面から東京電力のテスト解析を実行しました。
結果は成功。
fetch_transport:
server_cdp_multitab_view_source
Server Chromium CDP:
HTTP 200
Content-Type:
application/xml; charset=UTF-8
Browser:
HeadlessChrome/94.0.4606.0
CDP Protocol:
1.3
さらに公式XMLの解析にも成功し、 停電件数、地域情報、公式案内、更新日時まで既存のInformation Hubへ正常に渡りました。
取得した公式XMLの原文も管理画面から確認できます。
Windows PCもFTP中継も不要。
東京電力の取得から解析まで、 レンタルサーバー内だけで完結しました。
今回の検証では、 単純なHTTP取得が失敗したからといって 「その情報は自動取得できない」とは限らないことが分かりました。
Webサイトによっては、 HTTPクライアントから直接取得する場合と、 実際のブラウザでページとしてアクセスする場合で 結果が異なる場合があります。
その場合、 本物のブラウザエンジンを利用した取得方法が選択肢になります。
ただしこれは、 アクセス制御を無理に回避するという意味ではありません。
公開情報を取得する場合でも、 提供元の利用条件やアクセス頻度に配慮し、 必要最小限の取得にすることが重要です。
今回、HTTP 200が返っていても、 実際には期待したXMLではなくHTMLだったケースがありました。
そのため現在のInformation Hubでは、 単にHTTPステータスだけを見るのではなく、 取得データそのものを確認します。
HTTPステータス
Content-Type
最終URL
XMLルート
データ構造
公式更新日時
公式停電件数
これらを組み合わせて、 初めて「正常取得」と判定します。
これは災害・停電情報のような、 誤表示をできるだけ避けたいデータでは特に重要です。
今回の方法にたどり着くまでには、 かなり多くの失敗がありました。
WordPress HTTP API → 失敗
PHP cURL → 失敗
Windows curl.exe → 失敗
クラウド環境 → 失敗
単純Headless → 失敗
CDP + fetch → 失敗
CDP + 複数タブ → 成功
サーバーChromium → 成功
Information Hub実装 → 成功
途中では、 「これはローカルPCからFTPで送るしかないかもしれない」 というところまで行きました。
しかし、 人間がChromeで操作すると成功する手順を細かく分解してみることで、 自動化でも同じ条件を再現できました。
動かない理由を一つずつ切り分ける。
今回一番役に立ったのは、 結局この基本的なデバッグ方法だったと思います。
最後にInformation Hubの画面へ表示されたのは、 東京電力公式XMLを取得・解析した正常な結果でした。
長く続いた東京電力XML取得問題は、 レンタルサーバー上の Headless Chromium+Chrome DevTools Protocol という形で決着しました。
単に「取得できた」だけではありません。
今回作ったブラウザ取得方式は、 今後Information Hubで同じように 「通常HTTPでは取得しにくい公式情報」が出てきたときの 新しい取得手段としても応用できます。
失敗した方法も含めて、 全部が次の仕組みの材料になりました。
我々は、完全に勝利した!
今回紹介した東京電力の停電情報取得や、 災害・障害情報を収集・解析する仕組みは、 Pinpoint(ピンポイント)で実際に開発・運用しています。
Pinpointでは、 「ピンポイントをキーワードに、新しい世界をつくる。」 をテーマに、AIやWeb技術を活用しながら、 生活、災害情報、AIツール、投資など、 さまざまな情報を扱っています。
今回の記事で登場した Pinpoint Information Hubも、 公式情報をできるだけ正確に取得し、 人にもAIにも扱いやすい形へ整理することを目的に開発している仕組みの一つです。
▼ Pinpoint(ピンポイント)
AI・Web・生活・投資など、さまざまなテーマの記事を掲載しています。
Pinpoint(ピンポイント)
今回のような開発・検証についても、 成功した方法だけでなく、 失敗した方法や途中で分かったことも含めて記録していきます。