Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JJUG-2018-Fall-MF2

 JJUG-2018-Fall-MF2

Ab14487761e505583ecb771328561c87?s=128

Takeshi Nakamoto

December 15, 2018
Tweet

Transcript

  1. Selenium WebDriverと ヘッドレスChromeを用いた スクレイピング 2018/12/15 JJUG CCC 2018 Fall 中本

    武志 1
  2. 自己紹介 中本 武志(なかもとたけし) 株式会社マネーフォワード(2016〜) サービス基盤本部アプリケーション部 サーバーサイドエンジニア(Java) 静岡県駿東郡長泉町在住 2

  3. 3 熱海 小田原 新横浜 品川 • 通勤は約1時間 • 終電は品川発22:54 •

    週1でリモート勤務 ◦ 保育園の送迎 ◦ 家事 三島
  4. 内容 1. ヘッドレスChromeの検討を始めた経緯 2. ヘッドレスChromeの使い方 3. アグリゲーションシステムへの組み込み 4. まとめ 開発中プロジェクトの内容となります。

    本番環境での運用を始めていないため、遭遇していない問題がある可能性があります。 4
  5. 内容 1. ヘッドレスChromeの検討を始めた経緯 2. ヘッドレスChromeの使い方 3. アグリゲーションシステムへの組み込み 4. まとめ 5

  6. マネーフォワードのアグリゲーションシステム • カスタマイズした HtmlUnit を利用したスクレイピング • OkHttp などを利用したAPI連携 • 様々な金融機関サイトに対応

    • 毎分4000セッション https://github.com/HtmlUnit/htmlunit 6
  7. 対応金融機関 • 銀行、など ・・・ 2279 サイト • カード ・・・ 141

    サイト • 証券、投信、FX・貴金属 ・・・ 87 サイト • ポイント ・・・ 41 サイト • 電子マネー・プリペイド ・・・ 37 サイト • 交通(JAL, ANA) ・・・ 12 サイト • 通販(Amazon, 楽天) ・・・ 10 サイト • 携帯 ・・・ 12 サイト • など ・・・ 計 2678 サイト 7
  8. 対応金融機関 • API連携でのアグリゲーションも増えている ◦ 仕様が決まっているので、問題なくデータが取得できる ◦ (基本的には)処理速度も速い ◦ ログインパスワードなども預からない •

    とはいえ大多数はスクレイピングを行っている ◦ 最近スクレイピングが難しいサイトが増えてきた 8
  9. スクレイピングの課題 例1) MF KESSAI (https://mfkessai.co.jp/) スクレイピングしようとしたらエラーになった 9

  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/resolvedOptions 2012年ごろ策定されたAPI (?) 10
  11. スクレイピングの課題 エラー内容 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/resolvedOptions 2012年ごろ策定されたAPI (?) 11 エラーになる箇所と同等の処理を実装して回避
  12. スクレイピングの課題 例2) HtmlUnitの改修 とあるサイトのリニューアル後にスクレイピングできなくなった com.gargoylesoftware.htmlunit.ScriptException: ReferenceError: Assignment to undefined "getHoge"

    in strict mode var getHoge = function getHoge() { alert ('foo') } 名前付き関数式でパースに 失敗するパターンが存在した 12
  13. スクレイピングの課題 例) HtmlUnitの改修 暫定対応後に、根本対応を行い、HtmlUnit に報告 13

  14. スクレイピングの課題 • HtmlUnit ではスクレイピングが難しいサイトが増えた • リニューアル等でこれまでできていたスクレイピングが できなくなることが増えた ◦ 解決策を模索するコストが増加 GUIなし(ヘッドレス)で動く本物のブラウザを使ったスクレイピング

    を 選択できるようにしておく 14
  15. ブラウザのヘッドレスモード • 2018年7月当時、ヘッドレスモードがあったのは ◦ Google Chrome v68 ◦ Mozilla Firefox

    v61 • とりあえず Chrome を使うことにした ◦ ブラウザシェアの多い方に 15
  16. 内容 1. ヘッドレスChromeの検討を始めた経緯 2. ヘッドレスChromeの使い方 3. アグリゲーションシステムへの組み込み 4. まとめ 16

  17. ヘッドレスChromeとは Google Chrome v59 から利用できるようになった GUIなしでブラウザを動作させる機能 https://developers.google.com/web/updates/2017/05/nic59 より 17

  18. 制御方法 • Chromeをプログラムから操作する手段はいくつか存在する ◦ puppeteer(パペティア) とかが有名 • Java アプリケーションから利用できるものは2つくらい ◦

    Chrome DevTools Protocol(CDP) ◦ Selenium/WebDriver 18
  19. 制御方法 構成 App App chromedriver WebSocket / CDP HTTP/JSON WebSocket

    / CDP 19 Selenium
  20. Chrome DevTools Protocol Chrome DevTools Protocol(CDP) を使って操作することができる • WebSocket で通信

    • JSON 形式のコマンド 20 https://chromedevtools.github.io/devtools-protocol/
  21. Chrome DevTools Protocol { "method": "Page.navigate", "params": { "url": "https://moneyforward.com/"

    } } ページ遷移コマンド DevTools Protocol エンドポイントに対して、コマンドを渡して操作する 21
  22. Chrome DevTools Protocol DevTools Protocol のみだとけっこう煩雑 22 - 要素を検索 (DOM.performSearch)

    - 検索結果を取得 (DOM.getSearchResults) - 検索結果のボックス情報を取得 (DOM.getBoxModel) - ボックス情報から要素の中心座標を計算 - 中心座標で mousePressed イベントを発火(Input.dispatchMouseEvent) - 中心座標で mouseReleased イベントを発火(Input.dispatchMouseEvent) (注: デバッガ経由での観察結果で、実装の一例です。) ある要素をクリックするために必要な処理
  23. Selenium WebDriver ブラウザ操作の自動化ツール • Chrome も含めた、ブラウザ共通の操作用API • 様々な言語での実装がある 23

  24. Selenium WebDriver App chromedriver ブラウザ固有の WebDriver実装 geckodriver edgedriver etc ...

    WebDriver クライアント JSON Wire Protocol (HTTP) 24
  25. chromedriver chrome/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 origins CDPコマンドを渡すAPIもある 25
  26. chromedriver 指定可能なオプションやAPIの使い方などは テストコードを見てみるとわかりやすい https://chromium.googlesource.com/chromium/src/+/master/chrome/test/chromedriver/test/run_py_tests.py 26

  27. chromedriver findElement というコマンドは最終的にJavaScript として実行されていた https://chromium.googlesource.com/chromium/+/trunk/third_party/webdriver/atoms.cc 27

  28. CDP / WebDriver の比較 機能(Chromeの場合) CDP WebDriver 多機能 (DevTools でできることができる)

    主にブラウザ操作関連 WebDriver でできることはCDPでもできる 28
  29. CDP / WebDriver の比較 実装(Chromeの場合) CDP WebDriver - ほぼ自前で実装する必要がある (ライブラリはあったが、ライセンスの関係で

    見送った) - 学習コストが高そう (puppetterなどのライブラリの実装を読んで 学んだ) - Selenium が提供するクラスが利用可能 - ドキュメントが豊富 - 事例も豊富 29
  30. スクレイピングの要件 アグリゲーションシステムに組み込むための最小限の要件 1. プロキシの利用 2. Cookie の利用 3. ファイルダウンロードの利用 2.

    と 3. については問題なく行えた。 4. も動作サンプルは実装できた。 30
  31. ファイルダウンロード HtmlUnit 31 • 同一プロセス内で動作しているため、アプリケーションから直接アクセス可能 ◦ ダウンロード完了イベントなどもわかる ヘッドレスChrome • 別プロセスのため、ファイルシステムを介して取得する必要がある

    ◦ ダウンロード完了イベントも明示的に取得しないといけない
  32. ファイルダウンロード CDP ダウンロードディレクトリの指定 Page.setDownloadBehavior コマンドで可能 ダウンロード完了判定 ディレクトリのファイル名判定(xxx.crdownload) または リクエスト完了イベントハンドリング(Network.loadingFinished) など

    32
  33. ファイルダウンロード WebDriver ダウンロードディレクトリの指定 chrome 起動時のオプションとして設定可能 (prefs.download.default_directory) ダウンロード完了判定 ディレクトリのファイル名判定(xxx.crdownload) または リクエスト完了イベントハンドリング(Network.loadingFinished)

    Performance Log を有効にするとCDPと同様のイベントログを取得できる 33
  34. どちらを使うか • CDPのほうがいろいろできるが、実装コストが高い • WebDriver は機能としてはCDPに劣るが、ライブラリの整備な どを考えると使いやすい 実験的な位置づけで、速くリリースに持っていけるWebDriverを採用 34

  35. 内容 1. ヘッドレスChromeの検討を始めた経緯 2. ヘッドレスChromeの使い方 3. アグリゲーションシステムへの組み込み 4. まとめ 35

  36. スクレイピング実装 HtmlUnit API WebDriver WebDriver を利用するサービスは、Chrome 及び chromedriver がインストール された特定のサーバーに振り分ける実装を追加

    36
  37. 構成 アグリゲーション chromedriver 外部サービス systemd 外部サービス 外部サービス server 37

  38. 構成 • chromedriver は daemon として起動しておく ◦ スクレイピングセッション毎にchromedriverとchromeが起 動するのは重そうだったので(感想) ◦

    同時スクレイピング数を絞った状態でリリースして調整す る予定 38
  39. 追加開発した機能 • Selenium 提供の機能でほぼ対応できた • chrome が起動しっぱなしにならないよう確実に閉じるようにし た • 既存の開発スタイルに合わせるため、明示的な待機の実装を

    ラップ ◦ 指定時間を過ぎても例外を投げないで戻す ◦ エラー画面になったことを検出できるようにした 39
  40. 使ってみた感想1 • スクレイピングできなかったサイトができるようになって 単純にうれしい ◦ スクレイピングできないサイトがなくなった感がある • HtmlUnit と比べてそこまで遅くなった感じはない ◦

    ページ遷移時などは速く感じることも ◦ 外部サイトとの通信にかかる時間のほうが大きい ◦ 単純な動作速度よりも、正しいデータを取得できるようになるこ とのほうが重要なため問題ない 40
  41. 使ってみた感想2 • けっこうリソースを消費する(CPU/メモリ) ◦ 同時接続数を増やした場合の負荷がどうなるか ◦ 計測した値を元に調整していく • chrome のバージョンアップなどへの追従はどうするか

    ◦ apt では基本的に最新バージョンしかインストールできない ◦ リポジトリをコピーしてくるなどが必要そう • 既存のライブラリ資産を使えるようにしたい ◦ HtmlUnit 提供のクラスのラッパーなど ◦ 便利メソッドを使えるように改修しないといけない 41
  42. 内容 1. ヘッドレスChromeの検討を始めた経緯 2. ヘッドレスChromeの使い方 3. アグリゲーションシステムへの組み込み 4. まとめ 42

  43. まとめ • HtmlUnit 以外のスクレイピングの選択肢を準備したい ◦ そのためにヘッドレス chrome を使う • Chrome

    DevTools Protocol は使えたら使いたい ◦ ライブラリ整備ができれば • WebDriver 便利 ◦ サクッと使い始められる ◦ chrome のバージョンアップ等の運用に課題 ◦ リソースは結構消費する模様 • 定量的な評価は今後の課題 43
  44. Thank you! 44

  45. Javaエンジニア募集中 • 扱うデータへ強い責任感をお持ちの方 • この世界に流れるデータを綺麗にしたい!というパッションをお持ちの方 • 大規模トラフィックを捌いてみたい方 • ミドルウェアを使い倒したい方 45