Selenium Webdriver + Chrome DevTools Protocolを使って情報を取得してみる

# はじめに　〜この記事でご紹介すること〜
本記事では「 **Selenium Webdriver Chrome DevTools Protocol** 」などでググるとパッとヒットするものがなかったので、それについてご紹介します。
Selenium Webdriverを使ってコードを書いたことがある方、Chrome開発者ツールやFirebugなどの解析ツールを使ったことがある方であれば
この記事を読んでいただくことで「な〜んだ！思ってたよりも簡単にいろいろなことできるようになるじゃん。」と思われるかもしれません。

# Chrome DevTools Protocolとは
Chrome DevTools Protocol(以下、CDP)を使うと、WebDriverでは定義されていなかったDevToolsの各種機能(JavaScrioptデバッガ、プロファイラ、パフォーマンスツール、などなど...)が利用できます。
公式ドキュメントは[こちら](https://chromedevtools.github.io/devtools-protocol/)


CDPを使ってどんなことができるのかは、 @d-dai さんが書かれている記事 [Chromeデベロッパーツールの使い方まとめ](https://qiita.com/d-dai/items/0b580b26bb1d1622eb46)にわかりやすく解説がされていましたのでここでは割愛します。

# Selenium WebdriverでCDPを使ってみる
まず、CDPには非推奨、実験的なものも含めて以下のようにたくさんのドメインがあります。
これらすべてのリファレンスを書いてみようと思いましたが、各ドメイン毎にもメソッドが定義されており多すぎるため、
ドメインの中で情報を取得するものをいくつかピックアップしてサンプルコードを書いてみました。

## Domains

* **Domains**
    * [Browser](https://chromedevtools.github.io/devtools-protocol/tot/Browser)
    * [Debugger](https://chromedevtools.github.io/devtools-protocol/tot/Debugger)
    * [DOM](https://chromedevtools.github.io/devtools-protocol/tot/DOM)
    * [DOMDebugger](https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger)
    * [Emulation](https://chromedevtools.github.io/devtools-protocol/tot/Emulation)
    * [Input](https://chromedevtools.github.io/devtools-protocol/tot/Input)
    * [IO](https://chromedevtools.github.io/devtools-protocol/tot/IO)
    * [Log](https://chromedevtools.github.io/devtools-protocol/tot/Log)
    * [Network](https://chromedevtools.github.io/devtools-protocol/tot/Network)
    * [Page](https://chromedevtools.github.io/devtools-protocol/tot/Page)
    * [Performance](https://chromedevtools.github.io/devtools-protocol/tot/Performance)
    * [Profiler](https://chromedevtools.github.io/devtools-protocol/tot/Profiler)
    * [Runtime](https://chromedevtools.github.io/devtools-protocol/tot/Runtime)
    * [Security](https://chromedevtools.github.io/devtools-protocol/tot/Security)
    * [Target](https://chromedevtools.github.io/devtools-protocol/tot/Target)
* **Deprecated Domains(非推奨)**
    * [Console](https://chromedevtools.github.io/devtools-protocol/tot/Console)
    * [Schema](https://chromedevtools.github.io/devtools-protocol/tot/Schema)
* **Experimental Domains(実験)**
    * [Accessibility](https://chromedevtools.github.io/devtools-protocol/tot/Accessibility)
    * [Animation](https://chromedevtools.github.io/devtools-protocol/tot/Animation)
    * [ApplicationCache](https://chromedevtools.github.io/devtools-protocol/tot/ApplicationCache)
    * [Audits](https://chromedevtools.github.io/devtools-protocol/tot/Audits)
    * [BackgroundService](https://chromedevtools.github.io/devtools-protocol/tot/BackgroundService)
    * [CacheStorage](https://chromedevtools.github.io/devtools-protocol/tot/CacheStorage)
    * [Cast](https://chromedevtools.github.io/devtools-protocol/tot/Cast)
    * [CSS](https://chromedevtools.github.io/devtools-protocol/tot/CSS)
    * [Database](https://chromedevtools.github.io/devtools-protocol/tot/Database)
    * [DeviceOrientation](https://chromedevtools.github.io/devtools-protocol/tot/DeviceOrientation)
    * [DOMSnapshot](https://chromedevtools.github.io/devtools-protocol/tot/DOMSnapshot)
    * [DOMStorage](https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage)
    * [Fetch](https://chromedevtools.github.io/devtools-protocol/tot/Fetch)
    * [HeadlessExperimental](https://chromedevtools.github.io/devtools-protocol/tot/HeadlessExperimental)
    * [HeapProfiler](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler)
    * [IndexedDB](https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB)
    * [Inspector](https://chromedevtools.github.io/devtools-protocol/tot/Inspector)
    * [LayerTree](https://chromedevtools.github.io/devtools-protocol/tot/LayerTree)
    * [Media](https://chromedevtools.github.io/devtools-protocol/tot/Media)
    * [Memory](https://chromedevtools.github.io/devtools-protocol/tot/Memory)
    * [Overlay](https://chromedevtools.github.io/devtools-protocol/tot/Overlay)
    * [ServiceWorker](https://chromedevtools.github.io/devtools-protocol/tot/ServiceWorker)
    * [Storage](https://chromedevtools.github.io/devtools-protocol/tot/Storage)
    * [SystemInfo](https://chromedevtools.github.io/devtools-protocol/tot/SystemInfo)
    * [Tethering](https://chromedevtools.github.io/devtools-protocol/tot/Tethering)
    * [Tracing](https://chromedevtools.github.io/devtools-protocol/tot/Tracing)
    * [WebAudio](https://chromedevtools.github.io/devtools-protocol/tot/WebAudio)
    * [WebAuthn](https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn)

## Rubyでの実装例

### 使用したメソッド

* [Page.navigate](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-navigate)
    * 指定したURLにページに遷移します
    * これはSeleniumWebdriverでおなじみの `driver.get 'https://google.com'` と同義です
* [Browser.getVersion](https://chromedevtools.github.io/devtools-protocol/tot/Browser/#method-getVersion)
    * ブラウザの情報を取得します
* [Network.getAllCookies](https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-getAllCookies)
    * すべてのクッキーを取得します
* [Performance.getMetrics](https://chromedevtools.github.io/devtools-protocol/tot/Performance/#method-getMetrics) 
    * 現在のパフォーマンスの値を取得します
    * [Performance.enable](https://chromedevtools.github.io/devtools-protocol/tot/Performance/#method-enable)に設定しておく必要があります

### ソースコード

```Ruby:Gemfile
source 'https://rubygems.org'

gem 'selenium-webdriver'
```

```Ruby:sample.rb
require 'selenium-webdriver'
  
driver = Selenium::WebDriver.for :chrome

puts '### Page.navigate ###'
driver.execute_cdp('Page.navigate', { url: 'https://google.com' })

puts "\n### Browser.getVersion ###"
puts driver.execute_cdp 'Browser.getVersion'

puts "\n### Network.getAllCookies ###"
cookies = driver.execute_cdp 'Network.getAllCookies'
puts cookies['cookies']

puts "\n### Performance.getMetrics ###"
driver.execute_cdp 'Performance.enable'
metrics = driver.execute_cdp 'Performance.getMetrics'
puts metrics['metrics']
```

### 実行結果
取得した一部情報はマスキングしています。

```Ruby
### Page.navigate ###

### Browser.getVersion ###
{"jsVersion"=>"8.8.278.17", "product"=>"Chrome/88.0.4324.192", "protocolVersion"=>"1.3", "revision"=>"@31b458a18f133db9203eb5a5dd6552de0716dda3", "userAgent"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36"}

### Network.getAllCookies ###
{"domain"=>".google.com", "expires"=>1616770705.154825, "httpOnly"=>false, "name"=>"1P_JAR", "path"=>"/", "priority"=>"Medium", "sameSite"=>"None", "secure"=>true, "session"=>false, "size"=>19, "value"=>"2021-02-24-14"}
{"domain"=>".google.com", "expires"=>1629989904.81409, "httpOnly"=>true, "name"=>"NID", "path"=>"/", "priority"=>"Medium", "sameSite"=>"None", "secure"=>true, "session"=>false, "size"=>178, "value"=>"XXXXXX"}

### Performance.getMetrics ###
{"name"=>"Timestamp", "value"=>655734.877406}
{"name"=>"AudioHandlers", "value"=>0}
{"name"=>"Documents", "value"=>2}
{"name"=>"Frames", "value"=>2}
{"name"=>"JSEventListeners", "value"=>173}
{"name"=>"LayoutObjects", "value"=>92}
{"name"=>"MediaKeySessions", "value"=>0}
{"name"=>"MediaKeys", "value"=>0}
{"name"=>"Nodes", "value"=>325}
{"name"=>"Resources", "value"=>17}
{"name"=>"ContextLifecycleStateObservers", "value"=>6}
{"name"=>"V8PerContextDatas", "value"=>1}
{"name"=>"WorkerGlobalScopes", "value"=>0}
{"name"=>"UACSSResources", "value"=>0}
{"name"=>"RTCPeerConnections", "value"=>0}
{"name"=>"ResourceFetchers", "value"=>2}
{"name"=>"AdSubframes", "value"=>0}
{"name"=>"DetachedScriptStates", "value"=>0}
{"name"=>"ArrayBufferContents", "value"=>0}
{"name"=>"LayoutCount", "value"=>0}
{"name"=>"RecalcStyleCount", "value"=>0}
{"name"=>"LayoutDuration", "value"=>0}
{"name"=>"RecalcStyleDuration", "value"=>0}
{"name"=>"DevToolsCommandDuration", "value"=>0.000104}
{"name"=>"ScriptDuration", "value"=>0}
{"name"=>"V8CompileDuration", "value"=>0}
{"name"=>"TaskDuration", "value"=>0.000131}
{"name"=>"TaskOtherDuration", "value"=>2.7e-05}
{"name"=>"ThreadTime", "value"=>0.000256}
{"name"=>"ProcessTime", "value"=>0.36707}
{"name"=>"JSHeapUsedSize", "value"=>7745352}
{"name"=>"JSHeapTotalSize", "value"=>10215424}
{"name"=>"FirstMeaningfulPaint", "value"=>0}
{"name"=>"DomContentLoaded", "value"=>655734.046827}
{"name"=>"NavigationStart", "value"=>655733.006387}
```