Java+PhantomJSでサイトレスポンスを計測する

Java+PhantomJSでサイト監視

Java + PhantomJS を利用してサイトパフォーマンス計測を行う方法。
単純なHTTPレスポンスだけでなく、Webページのロード完了までにかかる時間も計測できます。

はじめに

サイトパフォーマンスを計測したいという話があったので、Javaを利用して計測する方法に関して試した事に関して軽くまとめてみます。

あなたの管理するWebサイトにも、パフォーマンスを監視する

"第三者の厳しい目" を導入してはいかがでしょうか?

※こちらの第三者の厳しい目は、きちんと客観的な数値を示してくれるはずです。
※写真 : 写真素材ぱくたそ

要件

要件は以下のような感じです。

  • 単純なHTTPレスポンスの計測だけでなく、Webページのロード完了にかかる時間も計測したい
  • Webページのキャプチャも取得したい
  • バッチ処理として実行可能
  • 動作環境は Windows & Linux
  • 計測処理は並列 ( マルチスレッドで ) 実行させたい ( この記事では触れません )

方針

上記要件を満たすために以下の方向で実装する事にしました。

  • Javaで実装
    私が比較的得意。Win/Linux対応。マルチスレッド処理も問題ない。
  • Webページのロード時間の計測には Navigation Timing API を利用 ( したい )
    Navigation Timing API に関しては HTML5で Speed Test, Navigation Timing APIによる性能データ収集 が判りやすい。
  • ヘッドレスブラウザを使う
    Navigation Timing API を利用するにはWebブラウザを利用する必要がありますが、バッチで実行するためにヘッドレスブラウザを使います。
    幾つかの実装がありますが、PhantomJS を利用する事にします。( PhantomJS は Navigation Timing API に対応している )

Java + PhantomJS で実装する方法に関しては Web上に色々と記事が載っているのでこれを参考にしました。

※Navigation Timing API の対応ブラウザに関しては Navigation Timing API を参照 ( ヘッドレスブラウザは記載外 )

環境

環境は以下

OS Windows 8.1
Java 1.8
PhantomJS 2.1.1

PhantomJS 導入

まず PhantomJS を導入し、簡単に動作を確認します。

インストール

PhantomJS のダウンロードページ からバイナリを取得してインストールします。
Windows の場合、Windows 用のバイナリパッケージ (zip) ファイルを取得して、適当なディレクトリに展開しましょう。

動作確認

zip ファイルを展開すると, 展開先の bin ディレクトリ下に PhantomJS の実行ファイル ( phantomjs.exe ) が確認できるはずです。この実行ファイルとサンプルスクリプトを利用して動作確認をしてみましょう。

PhantomJS ではスクリプトファイルを作成し、このパスをパラメータとして渡す事で、様々な処理を行わせる事が可能です。
スクリプトに関しては、パッケージに多数のサンプルスクリプトが含まれていますし、PhantomJS のサイト ( Documentation ページ ) にも説明があります。

※サンプルスクリプトは examples ディレクトリ下に含まれています。

  1. コマンドプロンプトを実行します。
  2. PhantomJS の展開先に移動します。
    >cd 展開先ディレクトリ
    
  3. ページのロード時間を計測するスクリプト ( loadspeed.js ) を実行してみます。
    >bin\phantomjs.exe examples\loadspeed.js テスト先URL
    
  4. 問題なければLoading Time が出力されます。
    Page title is IT (Innovation Technology) ...
    Loading time 1822 msec
    

    ※Windowsの場合タイトルの日本語が文字化けします。

上記のように Loading time が取得できれば PhantomJS の動作は問題ないと言えます。

※スクリーンショットをキャプチャするサンプルとしては responsive-screenshot.js があります。
loadspeed.js と同様に実行する事で、ブラウザ幅を変えた複数のスクリーンショットを取得できます。

※Windows のバッチスクリプトや Linux のシェルスクリプトから実行してロード時間を計測する場合、上記 loadspeed.js を値のみ出力する形に編集し、結果をファイルにリダイレクトするといった形でも計測可能かと思われます。( または、パイプでDBに書き込むプログラムに渡すとか )

Javaプログラムの実装

PhantomJSの動作が確認できたら Javaプログラムを実装します。

Javaの開発環境のインストールに関してはここでは触れません。適当なドキュメント等を参照して下さい。
参考 : Eclipseインストール01 ( Java開発環境構築 ) 

Maven ( pom.xml ) の設定

ビルドツールとして Maven を利用する方向で説明します。

pom.xml を作成します ( 以下 )。
selenium-java、及び、phantomjsdriver を dependency に追加して下さい。バージョンは適宜 ( 最新に変更する等 ) して下さい。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>agilegroup.co.jp</groupId>
  <artifactId>phantomjstest</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
  	<dependency>
  		<groupId>org.seleniumhq.selenium</groupId>
  		<artifactId>selenium-java</artifactId>
  		<version>2.53.0</version>
  	</dependency>
  	<dependency>
  		<groupId>com.github.detro.ghostdriver</groupId>
  		<artifactId>phantomjsdriver</artifactId>
  		<version>1.1.0</version>
  	</dependency>
  </dependencies>
</project>

クラスの作成

次に、パフォーマンス計測を行うためのプログラムを書きます。
今回はシンプルに単一のクラスのみで処理させる事にします。

以下クラス ( PhantomJSTest ) を作成します。

package jp.co.agilegroup.sample.phantomjs;

import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

public class PhantomJSTest {

	private static final String PHANTOMJS_PATH = "C:/phantomjs.exe";

	public static void main(String[] args) {
		if (args.length < 1) {
			System.out.println("URL parameter is required.");
			System.exit(0);
		} else {
			String url = args[0];
			System.out.format("URL   : %s\n", url);
			PhantomJSTest test = new PhantomJSTest();
			test.checkSitePerformance(url);
		}
	}

	public PhantomJSTest() {
	}
	
	/**
	 * Initialize PhantomJSDriver.
	 */
	private PhantomJSDriver initDriver() {
			// set Capabilities
			DesiredCapabilities capabilities = DesiredCapabilities.phantomjs();
			capabilities.setJavascriptEnabled(true);
			
			System.setProperty("phantomjs.binary.path", PHANTOMJS_PATH);
			PhantomJSDriver driver = new PhantomJSDriver(capabilities);
			return driver;
	}

	/**
	 * Check Performance.
	 */
	private void checkSitePerformance(String url) {

		PhantomJSDriver driver = null;

		try {
			driver = initDriver();
			driver.get(url);	// access to specified URL
			waitForLoad(driver);
			// Get values of Navigation Timing
			long startTime = (Long) driver.executeScript(
					"return window.performance.timing.navigationStart");
			long loadEndTime = (Long) driver.executeScript(
					"return window.performance.timing.loadEventEnd");
			long responseEndTime = (Long) driver.executeScript(
					"return window.performance.timing.responseEnd");

			System.out.format("Response Time : %d\n", responseEndTime - startTime);
			System.out.format("PageLoad Time : %d\n", loadEndTime - startTime);
		} finally {
			if (driver != null) {
				driver.quit();
			}
		}
	}

	/**
	 * Wait for page load.
	 */
	void waitForLoad(PhantomJSDriver driver) {
	    ExpectedCondition<Boolean> pageLoadCondition = new
	        ExpectedCondition<Boolean>() {
				public Boolean apply(WebDriver driver) {
	                return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
				}
	        };
	    WebDriverWait wait = new WebDriverWait(driver, 60);
	    wait.until(pageLoadCondition);
	}
}

処理内容に関して簡単に説明します。

main

Java アプリケーションでよく見る static void main メソッドです。
パラメータでチェック先のURLを受け取るようにしています。パラメータが指定されない場合、メッセージを出力して終了します。

initDriver

Selenium の WebDriver (PhantomJSDriver) を取得しています。

Capabilities で WebDriverの設定を行う事が可能です。
ここでは JavaScript を有効にしています。

個人的に利用する事が多い設定を以下に示します。

設定可能な値メソッド
JavaScriptの有効/無効setJavascriptEnabled(true)
スクリーンショットの取得有効setCapability("takesScreenshot", true)
タイムアウト設定setCapability("phantomjs.page.settings.resourceTimeout", ms) で設定可能らしい。 きちんと機能しているか確認できてないが ...
PhantomJSのオプション設定setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, phantomArgs) で PhanttomJS のオプションパラメータを設定可能。
例えば String[] phantomArgs = new String[] {"--webdriver-loglevel=NONE"}; とすると、phantomjs自体が出力するログを抑制できる。
PhantomJSのプロキシやディスクキャッシュの設定などもここで行う事が可能です。
※PhantomJSで指定可能なオプションは phantomjs.exe --help で確認可能。

System.setProperty("phantomjs.binary.path", ...) では PhantomJS の実行ファイルのパスを設定しています。
先に展開した phantomjs.exe のパスを設定しましょう。

checkSitePerformance

url パラメータで指定されたページのパフォーマンスを計測する。処理の概要は以下。

  1. WebDriver を取得し、指定されたURLにアクセスする。
  2. ページロード完了まで待つ (waitForLoad)。
  3. Navigation Timing API を利用して各種タイミング値を取得する。
  4. タイミング値を利用してパフォーマンス値を算出し、値を出力。
  5. PhantomJSDriverの終了(quit)。

動作確認

上記コードを実行すると以下のようになります。

URL   : http://www.agilegroup.co.jp/
6 09, 2016 1:26:15 午後 org.openqa.selenium.phantomjs.PhantomJSDriverService <init>
情報: executable: C:\Software\Network\phantomjs-2.1.1-windows\phantomjs-2.1.1-windows\bin\phantomjs.exe
6 09, 2016 1:26:15 午後 org.openqa.selenium.phantomjs.PhantomJSDriverService <init>
情報: port: 39065
6 09, 2016 1:26:15 午後 org.openqa.selenium.phantomjs.PhantomJSDriverService <init>
情報: arguments: [--webdriver=39065, --webdriver-logfile=C:\Eclipse\workspace\phantomjstest\phantomjsdriver.log]
6 09, 2016 1:26:15 午後 org.openqa.selenium.phantomjs.PhantomJSDriverService <init>
情報: environment: {}
[INFO  - 2016-06-09T04:26:17.750Z] GhostDriver - Main - running on port 39065
[INFO  - 2016-06-09T04:26:17.942Z] Session [4f72b410-2dfa-11e6-a691-810a6a8304e6] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1","webSecurityEnabled":true}
[INFO  - 2016-06-09T04:26:17.943Z] Session [4f72b410-2dfa-11e6-a691-810a6a8304e6] - page.customHeaders:  - {}
[INFO  - 2016-06-09T04:26:17.943Z] Session [4f72b410-2dfa-11e6-a691-810a6a8304e6] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"2.1.1","driverName":"ghostdriver","driverVersion":"1.2.0","platform":"windows-8.1-32bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}}
[INFO  - 2016-06-09T04:26:17.944Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: 4f72b410-2dfa-11e6-a691-810a6a8304e6
Response Time : 84
PageLoad Time : 2372
[INFO  - 2016-06-09T04:26:21.387Z] ShutdownReqHand - _handle - About to shutdown

※上記コードで出力しているページロードタイムの値ですが、上述の loadspeed.js スクリプトで取得した値と同じ位の値が取得される事を確認しています。

スクリーンショットの取得

ついでに、スクリーンショットを取得するためのコードも書いておきます。

以下のコードでスクリーンショットを取得可能です。
checkSitePerformance メソッドの waitForLoad(driver); の後に記述すればOKです。
driver.getScreenShotAs ではテンポラリファイルとして保存され、プログラム終了時に削除されてしまうため、Files.copy で別ファイルにコピーしています。

			File tmpFile = driver.getScreenshotAs(OutputType.FILE);
			File scnFile = new File("C:/tmp/scr.png");
			try {
				Files.copy(tmpFile.toPath(), scnFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
			} catch (IOException e) {
				e.printStackTrace();
			}

まとめ

HAR Viewer
拡大
HAR Viewer

という事で Java + PhantomJS で無事Webサイトのパフォーマンス計測を行う事ができました。

より詳細なパフォーマンスデータを取得したい場合には、PhantomJS の examples にある netsniff.js を利用して HAR ( HTTP ARchive ) ファイルを作成するといった事も可能です。

HARファイルはそのままでは 「何書いてあんの?」 な感じですが、以下のサイトを利用する事でグラフ化する事が可能です ( 何れも作成した har ファイルをドラッグ&ドロップすればOK )。

サイトレスポンスを計測したい、これまでも計測しているがより詳細な情報が欲しいと言った場合に利用してみるとよいかもしれません。

※HARファイルはChromeのデベロッパーツールのNetworkタブで見れる情報と同じ情報を記録したもの。デベロッパーツールからも保存する事が可能です。

※一応 driver.executePhantomJS(script) とする事で、PhantomJSスクリプトを実行する事も可能。