Selenium WebDriverとヘッドレスChromeを用いたスクレイピング2018/12/15 JJUG CCC 2018 Fall中本 武志1
View Slide
自己紹介中本 武志(なかもとたけし)株式会社マネーフォワード(2016〜)サービス基盤本部アプリケーション部サーバーサイドエンジニア(Java)静岡県駿東郡長泉町在住2
3熱海小田原新横浜品川● 通勤は約1時間● 終電は品川発22:54● 週1でリモート勤務○ 保育園の送迎○ 家事三島
内容1. ヘッドレスChromeの検討を始めた経緯2. ヘッドレスChromeの使い方3. アグリゲーションシステムへの組み込み4. まとめ開発中プロジェクトの内容となります。本番環境での運用を始めていないため、遭遇していない問題がある可能性があります。4
内容1. ヘッドレスChromeの検討を始めた経緯2. ヘッドレスChromeの使い方3. アグリゲーションシステムへの組み込み4. まとめ5
マネーフォワードのアグリゲーションシステム● カスタマイズした HtmlUnit を利用したスクレイピング● OkHttp などを利用したAPI連携● 様々な金融機関サイトに対応● 毎分4000セッションhttps://github.com/HtmlUnit/htmlunit6
対応金融機関● 銀行、など ・・・ 2279 サイト● カード ・・・ 141 サイト● 証券、投信、FX・貴金属 ・・・ 87 サイト● ポイント ・・・ 41 サイト● 電子マネー・プリペイド ・・・ 37 サイト● 交通(JAL, ANA) ・・・ 12 サイト● 通販(Amazon, 楽天) ・・・ 10 サイト● 携帯 ・・・ 12 サイト● など ・・・ 計 2678 サイト7
対応金融機関● API連携でのアグリゲーションも増えている○ 仕様が決まっているので、問題なくデータが取得できる○ (基本的には)処理速度も速い○ ログインパスワードなども預からない● とはいえ大多数はスクレイピングを行っている○ 最近スクレイピングが難しいサイトが増えてきた8
スクレイピングの課題例1) MF KESSAI (https://mfkessai.co.jp/)スクレイピングしようとしたらエラーになった9
スクレイピングの課題エラー内容Caused by: net.sourceforge.htmlunit.corejs.javascript.EcmaError:TypeError: Cannot find function resolvedOptions in object [object DateTimeFormat].https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/resolvedOptions2012年ごろ策定されたAPI (?)10
スクレイピングの課題エラー内容Caused by: net.sourceforge.htmlunit.corejs.javascript.EcmaError:TypeError: Cannot find function resolvedOptions in object [object DateTimeFormat].https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/resolvedOptions2012年ごろ策定されたAPI (?)11エラーになる箇所と同等の処理を実装して回避
スクレイピングの課題例2) HtmlUnitの改修とあるサイトのリニューアル後にスクレイピングできなくなったcom.gargoylesoftware.htmlunit.ScriptException:ReferenceError: Assignment to undefined "getHoge" in strict modevar getHoge = function getHoge() {alert ('foo')}名前付き関数式でパースに失敗するパターンが存在した12
スクレイピングの課題例) HtmlUnitの改修暫定対応後に、根本対応を行い、HtmlUnit に報告13
スクレイピングの課題● HtmlUnit ではスクレイピングが難しいサイトが増えた● リニューアル等でこれまでできていたスクレイピングができなくなることが増えた○ 解決策を模索するコストが増加GUIなし(ヘッドレス)で動く本物のブラウザを使ったスクレイピング を選択できるようにしておく14
ブラウザのヘッドレスモード● 2018年7月当時、ヘッドレスモードがあったのは○ Google Chrome v68○ Mozilla Firefox v61● とりあえず Chrome を使うことにした○ ブラウザシェアの多い方に15
内容1. ヘッドレスChromeの検討を始めた経緯2. ヘッドレスChromeの使い方3. アグリゲーションシステムへの組み込み4. まとめ16
ヘッドレスChromeとはGoogle Chrome v59 から利用できるようになったGUIなしでブラウザを動作させる機能https://developers.google.com/web/updates/2017/05/nic59 より17
制御方法● Chromeをプログラムから操作する手段はいくつか存在する○ puppeteer(パペティア) とかが有名● Java アプリケーションから利用できるものは2つくらい○ Chrome DevTools Protocol(CDP)○ Selenium/WebDriver18
制御方法構成AppApp chromedriverWebSocket / CDPHTTP/JSON WebSocket / CDP19Selenium
Chrome DevTools ProtocolChrome DevTools Protocol(CDP) を使って操作することができる● WebSocket で通信● JSON 形式のコマンド20https://chromedevtools.github.io/devtools-protocol/
Chrome DevTools Protocol{"method": "Page.navigate","params": {"url": "https://moneyforward.com/"}}ページ遷移コマンドDevTools Protocol エンドポイントに対して、コマンドを渡して操作する21
Chrome DevTools ProtocolDevTools Protocol のみだとけっこう煩雑22- 要素を検索 (DOM.performSearch)- 検索結果を取得 (DOM.getSearchResults)- 検索結果のボックス情報を取得 (DOM.getBoxModel)- ボックス情報から要素の中心座標を計算- 中心座標で mousePressed イベントを発火(Input.dispatchMouseEvent)- 中心座標で mouseReleased イベントを発火(Input.dispatchMouseEvent)(注: デバッガ経由での観察結果で、実装の一例です。)ある要素をクリックするために必要な処理
Selenium WebDriverブラウザ操作の自動化ツール● Chrome も含めた、ブラウザ共通の操作用API● 様々な言語での実装がある23
Selenium WebDriverAppchromedriverブラウザ固有のWebDriver実装geckodriveredgedriveretc ...WebDriverクライアントJSON Wire Protocol(HTTP)24
chromedriverchrome/chromium 向けの WebDriver 実装WebDriver 標準API以外にも、いろんなAPIが定義されているhttps://chromium.googlesource.com/chromium/src/+/master/chrome/test/chromedriver● W3C standard endpoints● Json wire protocol endpoints● Extension commands from other specs● ChromeDriver specific extension commands● Commands of unknown originsCDPコマンドを渡すAPIもある25
chromedriver指定可能なオプションやAPIの使い方などはテストコードを見てみるとわかりやすいhttps://chromium.googlesource.com/chromium/src/+/master/chrome/test/chromedriver/test/run_py_tests.py26
chromedriverfindElement というコマンドは最終的にJavaScript として実行されていたhttps://chromium.googlesource.com/chromium/+/trunk/third_party/webdriver/atoms.cc27
CDP / WebDriver の比較機能(Chromeの場合)CDP WebDriver多機能(DevTools でできることができる)主にブラウザ操作関連WebDriver でできることはCDPでもできる28
CDP / WebDriver の比較実装(Chromeの場合)CDP WebDriver- ほぼ自前で実装する必要がある(ライブラリはあったが、ライセンスの関係で見送った)- 学習コストが高そう(puppetterなどのライブラリの実装を読んで学んだ)- Selenium が提供するクラスが利用可能- ドキュメントが豊富- 事例も豊富29
スクレイピングの要件アグリゲーションシステムに組み込むための最小限の要件1. プロキシの利用2. Cookie の利用3. ファイルダウンロードの利用2. と 3. については問題なく行えた。4. も動作サンプルは実装できた。30
ファイルダウンロードHtmlUnit31● 同一プロセス内で動作しているため、アプリケーションから直接アクセス可能○ ダウンロード完了イベントなどもわかるヘッドレスChrome● 別プロセスのため、ファイルシステムを介して取得する必要がある○ ダウンロード完了イベントも明示的に取得しないといけない
ファイルダウンロードCDPダウンロードディレクトリの指定Page.setDownloadBehavior コマンドで可能ダウンロード完了判定ディレクトリのファイル名判定(xxx.crdownload)またはリクエスト完了イベントハンドリング(Network.loadingFinished)など32
ファイルダウンロードWebDriverダウンロードディレクトリの指定chrome 起動時のオプションとして設定可能(prefs.download.default_directory)ダウンロード完了判定ディレクトリのファイル名判定(xxx.crdownload)またはリクエスト完了イベントハンドリング(Network.loadingFinished)Performance Log を有効にするとCDPと同様のイベントログを取得できる33
どちらを使うか● CDPのほうがいろいろできるが、実装コストが高い● WebDriver は機能としてはCDPに劣るが、ライブラリの整備などを考えると使いやすい実験的な位置づけで、速くリリースに持っていけるWebDriverを採用34
内容1. ヘッドレスChromeの検討を始めた経緯2. ヘッドレスChromeの使い方3. アグリゲーションシステムへの組み込み4. まとめ35
スクレイピング実装HtmlUnit API WebDriverWebDriver を利用するサービスは、Chrome 及び chromedriver がインストールされた特定のサーバーに振り分ける実装を追加36
構成アグリゲーション chromedriver外部サービスsystemd外部サービス外部サービスserver37
構成● chromedriver は daemon として起動しておく○ スクレイピングセッション毎にchromedriverとchromeが起動するのは重そうだったので(感想)○ 同時スクレイピング数を絞った状態でリリースして調整する予定38
追加開発した機能● Selenium 提供の機能でほぼ対応できた● chrome が起動しっぱなしにならないよう確実に閉じるようにした● 既存の開発スタイルに合わせるため、明示的な待機の実装をラップ○ 指定時間を過ぎても例外を投げないで戻す○ エラー画面になったことを検出できるようにした39
使ってみた感想1● スクレイピングできなかったサイトができるようになって単純にうれしい○ スクレイピングできないサイトがなくなった感がある● HtmlUnit と比べてそこまで遅くなった感じはない○ ページ遷移時などは速く感じることも○ 外部サイトとの通信にかかる時間のほうが大きい○ 単純な動作速度よりも、正しいデータを取得できるようになることのほうが重要なため問題ない40
使ってみた感想2● けっこうリソースを消費する(CPU/メモリ)○ 同時接続数を増やした場合の負荷がどうなるか○ 計測した値を元に調整していく● chrome のバージョンアップなどへの追従はどうするか○ apt では基本的に最新バージョンしかインストールできない○ リポジトリをコピーしてくるなどが必要そう● 既存のライブラリ資産を使えるようにしたい○ HtmlUnit 提供のクラスのラッパーなど○ 便利メソッドを使えるように改修しないといけない41
内容1. ヘッドレスChromeの検討を始めた経緯2. ヘッドレスChromeの使い方3. アグリゲーションシステムへの組み込み4. まとめ42
まとめ● HtmlUnit 以外のスクレイピングの選択肢を準備したい○ そのためにヘッドレス chrome を使う● Chrome DevTools Protocol は使えたら使いたい○ ライブラリ整備ができれば● WebDriver 便利○ サクッと使い始められる○ chrome のバージョンアップ等の運用に課題○ リソースは結構消費する模様● 定量的な評価は今後の課題43
Thank you!44
Javaエンジニア募集中● 扱うデータへ強い責任感をお持ちの方● この世界に流れるデータを綺麗にしたい!というパッションをお持ちの方● 大規模トラフィックを捌いてみたい方● ミドルウェアを使い倒したい方45