Networkタブの情報(ajax通信の内容)をスクレイピングする方法


概要

多くの場合、サイトを表示した場合にそのサイト内で(クライアントサイド)外部サーバへリクエストを行い、レスポンスされたデータを非同期で埋め込みながらページがレンダリングされますが、この時のクライアントサイドでリクエストした情報をスクレイピングしたくなったため、まとめます。

簡潔に方法をまとめると

  1. selenium + 無理やりスクリプトでごにゃって情報取得
  2. selenium + browsermob-proxyでproxy経由するようにして情報取得

の2パターンが存在します。
1番目の方法は手頃に実現できますが、全ての情報が取得できるわけではなく、その場合は2番目のproxy経由での方法を使用すると自由度があがります。

手順

seleniumを導入

これはいろいろなサイトで説明があるため省略しますが、macの方はbrewで導入可能です。

$ brew install chromedriver
$ pip install selenium

スクリプトでごにゃる方法

以下のスクリプトを実行します。
※自分の場合、「"chromedriver"は開発元を検証できないため開けません。」と表示されたため、「キャンセル」ボタンを押して「システム環境設定」をクリックし、「セキュリティとプライバシー」を選択して許可しました。

import json
from selenium import webdriver

driver = webdriver.Chrome("/hoge/hoge/chromedriver") 
driver.get("{スクレイピングしたい先のサイトのURL}")
scriptToExecute = "var performance = window.performance || window.mozPerformance || window.msPerformance || window.webkitPerformance || {}; var network = performance.getEntries() || {}; return JSON.stringify(network);"
data = driver.execute_script(scriptToExecute)
json = json.loads(str(data))

あとはjsonの中にobjectが配列で入っているためいい感じに取り出します。

proxy経由する場合

流れとしては以下です。
1. http://bmp.lightbody.net/ にアクセスし、zipをダウンロード
2. 解凍し、chmod 755 などで実行権限を渡す
3. pip install browsermob-proxy する
4. 以下のスクリプトを書く

from browsermobproxy import Server
from selenium import webdriver

# このdictの中に"User-Agent":"hogehoge"や"referer":"hoge"などを追記することでヘッダー変更可能です。
proxy_ooptions = {'port': 50000}
# pathは、解答したフォルダのpath/bin/browsermob-proxy を設定します
path = "hoge/bin/browsermob-proxy"

server = Server(path, options=proxy_ooptions)
server.start()
proxy = server.create_proxy()

chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--proxy-server={0}".format(proxy.proxy))
browser = webdriver.Chrome(options = chrome_options)

#optionは必要に合わせて変更すればよいですが、この場合はヘッダーとレスポンスのcontentが返ってくるようになります。
proxy.new_har("hogehoge", options={'captureHeaders': True, 'captureContent': True})

browser.get("http://www.google.co.in")

print(proxy.har)

# 終了時はproxyも終了すること
server.stop()
browser.quit()

この時、jdkが入ってない場合はエラーが出ます。
(java -version で確認したら入っていないか確認できます。)
その場合は、https://www.oracle.com/java/technologies/javase-downloads.html からjdkを入れて再チャレンジしてください。

また、SSL通信が必要な場合は、さっき解凍したフォルダ内にあるssl-support/ca-certificate-rsa.cer をキーチェーンアクセスのシステム欄の証明書の中にぶちこむとうまくいくはずです。
(やり方はこちらを参考に : https://qiita.com/suin/items/be87a7a581f30b38c5f7)

proxy.har["log"]["entries"]配下の"request"や"response"あたりに欲しい値が入りますので、あとはharの内容を焼いたり煮たりぐつぐつして終わりです。(ちなみにHARはjsonで書かれたHTTP通信ログのことです。)

ちょっとした蛇足

いろいろな事情でUser-Agentを偽装する必要がありました。
当たり前ですが、上に書いたようにproxy経由時にheaderを書き換えても、クライアントサイドでリクエストを送る場合には当然そのブラウザのUser-Agent情報が上書きされるため、場合に合わせて以下の設定を入れて「selenium側で」書き換える必要があります。

options.add_argument('--user-agent=hogehoge')