WebページをMIME HTMLに保存する話(CDPとKatalon Studioを使って)


この記事はQiita Advent Calendar 2020の Selenium/Chrome DevTools Protocol/Appium カレンダーの1日目の記事です。

はじめに

わたしはWebアプリケーションのUIをテストする作業をソフトウェアで自動化する技術に関心があります。2018年1月以来わたしは Katalon Studio というソフトウェアに注目しています。以前、Qiitaに「Katalon Studioとはどんなソフトウエアか」を投稿して概要を紹介しました。

今回、WebページのスナップショットをMIME HTMLファイルに保存すること、Chrome DevTools Protocolを呼び出すGroovyスクリプトをKatalon Studioで開発して自動化することを試みました。その内容を共有します。

Chrome DevToolsを使って手動でWebページをMIME HTMLファイルに保存できること

MIME HTML(またはMHTML)とは何でしょうか?Wikipediaによれば

MIME Encapsulation of Aggregate HTML (MHTML) は、MIMEのマルチパートを利用することで、元となるHTML文書と、それからリンクされる画像や動画などのリソースを1つのデータとしてまとめるフォーマットである。電子メールでの利用を想定しているが、ウェブページを一つのファイルとして保存する用途にも用いられている。

Google ChromeブラウザのDevToolsを使ってWebページを一つのMHTMLファイルに保存することができます。実際にやってみましょう。

Google Chromeブラウザを起動し適当なWebページを開きます。どのURLでもいいのですが一例として http://demoaut-mimic.kazurayam.com/ を開きましょう。ツールバーから 表示 > 開発・管理 > デベロッパーツール と手繰ると Chrome DevToolsが起動します。

Chrome DevToolsが起動できたら、Webページのうえどこかをマウスで右クリックしてメニューを開き 別名で保存 を選択します。

次に表示されるダイアログで出力先ディレクトリとファイル名を指定し、形式として ウエブページ、1つのファイル を選択しましょう。

すると出力先フォルダの中に .mht というファイル名拡張子を持ったファイルができます。これでWebページがMIME HTML形式のテキストファイルに保存されました。

実際作られた.mhtファイルの中身はどんなでしょうか?下記を参照願います。全体がプレーンなテキストであること、MIMEヘッダによって区切られて、HTMLとCSSとJavaScript、そしてBase64エンコードされたラスタ画像が一つに束ねられていることが見て取れます。

From: <Saved by Blink>
Snapshot-Content-Location: https://katalon-demo-cura.herokuapp.com/
Subject: CURA Healthcare Service
Date: Wed, 18 Nov 2020 14:11:01 -0000
MIME-Version: 1.0
Content-Type: multipart/related;
    type="text/html";
    boundary="----MultipartBoundary--JUGRHw9UUbWsvJo7x3kAf40eOZp8hHIdkhkdsrnWou----"


------MultipartBoundary--JUGRHw9UUbWsvJo7x3kAf40eOZp8hHIdkhkdsrnWou----
Content-Type: text/html
Content-ID: <[email protected]>
Content-Transfer-Encoding: quoted-printable
Content-Location: https://katalon-demo-cura.herokuapp.com/

<!DOCTYPE html><html lang=3D"en"><head><meta http-equiv=3D"Content-Type" co=
ntent=3D"text/html; charset=3DUTF-8">
   =20
    <meta http-equiv=3D"X-UA-Compatible" content=3D"IE=3Dedge">
    <meta name=3D"viewport" content=3D"width=3Ddevice-width, initial-scale=
=3D1">
    <meta name=3D"description" content=3D"CURA Healthcare Service; (678) 81=
3-1KMS">

    <title>CURA Healthcare Service</title>

    <!-- Bootstrap Core CSS -->

...

ちなみに.mhtファイルがどれぐらいのサイズかというと、例えば http://demoaut-mimic.kazurayam.com/ を保存して作った.mhtファイルは約3万行、全体のサイズが約740キロバイトでした。

.mhtファイルをChromeにドラッグ&ドロップすれば復元できること

.mhtファイルをMacのFinderで掴んでChromeブラウザのウインドにドラッグ&ドロップしましょう(Windowsならばエクスプローラから)。元とそっくり同じな画面が表示されます。ただしアドレスバーをみると http:// なURLではなくて /Users/username/... のようなファイルのパスが表示されている点が異なります。

以上で、Chrome DevToolsを手動操作すれば、WebページをMIME HTMLファイルに保存できるとわかりました。

さてここから先が本題です。

解決したい問題

WebページのスナップショットをMIME HTMLファイルに保存するのを自動化したい。Katalon StudioでWeb UIをテストする一連のスクリプトによる処理の一部としてページをMHTMLへ保存したい。Webページのフルサイズのスクリーンショットを撮ってpng画像ファイルに保存することはすでにできる。あれと同じ手軽さで(pngファイルではなく)mhtファイルを作成したい。

何のために?

Visual Testingのために

Web UIテスト自動化ツールのサイトで Visual Testing という言葉が使われています。たとえばpercy.ioとか。Visual Testingとは 二つの画面を見比べて見た目の食い違いを発見しそれに基づいてソフトウェアの品質を判定する というテスト手法を意味します。開発者が予期していなかったプログラムの不具合に気づくことを目標とするテスト手法です。

わたしは以前 VisualTestingInKatalonStudio を開発し公開しました。そしてQiitaに「Katalon StudioでVisual Testingを実現した」「あなたのWebサイトをVisual Testingする方法」の2本の記事を書いた。VisualTestingInKatalonStudioはKatalon Studioのプロジェクトです。Webサイトのページをブラウザで表示しスクリーンショットをpng形式の画像ファイルに保存し、開発環境と本番環境のようにひとつのWebシステムの二つの環境の画面を見比べたり、本番環境のソフトウェアを入れ替える作業の前と後のように2時点の画面を見比べて、見た目の食い違いをツールが見つけ出す、それを自動化することができる。例えば1000画面を50分間で見比べた実績があります。1画面あたり約3秒。人間が1000画面を目視で確認するなんてやる気にもならないが、このツールがあれば毎日でも繰り返し実行可能になります。

さて、自分の職場でVisual Testingの手法を応用しそれなりの成果をあげて喜んでいたら、同僚がこんなことを言いました。

"このWebシステムはレスポンシブ・デザインを採用している。パソコン、タブレット、スマホでも表示できるように。複数の画面サイズ(LandscapeとPortraitどちらも)について目視確認をしなければならない。画面の数が多いから作業が大変。サボりがちです。目視作業をこのツールで自動化できたらすっごく嬉しい。できますか?"

PCとタブレットとスマホ、垂直と水平と

Katalon StudioにはWebUI.setViewPortSize(width,height)コマンドがあって、ブラウザの画面サイズを任意に設定することができる。だから一つのWeb画面をPC向けの画面サイズだけでなくiPadやスマホ向けの画面サイズで表示するスクリプトを書くことはできる。そしてスクリーンショットを撮ることもできる。だが今のVisualTestingInKatalonStudioでは、もしも5通りの画面サイズをテストしたければpng画像ファイルを5倍多く作るしかない。処理時間が5倍かかりディスクの所要量も5倍かかるだろう。重すぎる。軽くする工夫はないものか?

PNG画像のかわりにMIME HTMLでWebページの状態を記録するのはどうか。WebページのスナップショットをMIME HTMLファイルに保存し、後でMHTMLファイルをブラウザにロードして画面を復元しよう。復元した画面をWebUI.setViewPortSize(width,height)コマンドでサイズ切り替えしながら見比べよう。MHTMLファイル一つを元にして5通りでも10通りでも複数の画面サイズを再現できるはず。処理時間を短縮しディスク所要量をスリムにできるのではないか?と思った。WebページをMIME HTMLファイルに保存する処理をスクリプトで自動化したい。Katalon Studioのスクリプトからやりたい。どうすれば実装できるか?これがわたしの課題となりました。

解決方法

前述したように、手動でChromeブラウザのDevToolsでWebページのスナップショットをMIME HTMLファイルに保存することができる。それは実証済みだ。あれを手動でする代わりにスクリプトから実行できればいい。調べたら下記のことがわかりました。

  1. Chrome DevTools Protocol(以下、CDP と略記)というプロジェクトがある。CDPがPage.captureSnapshot()コマンドを定義している。このコマンドがWebページからMIME HTMLを生成してくれる。
  2. この記事 https://github.com/puppeteer/puppeteer/issues/3575 によればChromeブラウザがPage.captureSnapshot()を実装済み。
  3. Javaベースのクライアント・プログラムがCDPのプロトコルを介してブラウザとやりとりするためのライブラリ Chrome DevTools Java Client, by Kenan Klisura があった。
  4. Katalon Studioのplug-inとしてChrome DevTools Protocol Integrationがリリース済みだった。

このplug-inをKatalon StudioにインストールすればCDPプロトコルを介してChromeブラウザにMHTMLファイルを作らせるようなスクリプトを書いて実行できるはずだ。

作ってみたら動きました。

コードの説明

サンプルのありか

GitHubにレポジトリを作り公開しました。

このレポジトリのReleasesページからプロジェクト一式をまとめたZIPファイルをダウンロードすることができます。

サンプルの説明

動作環境

わたしは下記の環境でサンプルを開発しました。

Katalon StudioはJavaVMベースのソフトウェアでWindowsでもLinuxでも動きます。今回のサンプルコードはKatalon Studioの古いバージョン(ただし7.0.x以降)でも動くはずです。

動かしてみよう

あなたがKatalon Studioを自分のPCにインストール済みであると前提します(インストール方法については後述)。

上記のReleasesページからZIPファイルをダウンロードしましょう。ZIPを展開してSaveWebPageAsMHTフォルダを任意の場所に位置付けます。Katalon Studioをstartしてプロジェクトをopenしましょう。

Katalon Studioのウインドウの左がわに Tests Explorer とラベルされた四角い面があってその中をマウスでいじると Test Cases/Save web page as MHTML を選択することができます。

Test Cases/Save web page as MHTMLをマウスで選択したあと、GUIの右上の方にある緑色の三角形のボタン をクリックするとスクリプトが実行されます。

なおスクリプトを実行するときにGoogle Chromeブラウザを起動することが必要です。FireFoxなどの他のブラウザを指定するとエラーが発生します。

Test Cases/Save web page as MHTMLは何をするか

Test Cases/Save web pages as MHTMLのソースコードは下記の通り。

import com.github.kklisura.cdt.protocol.commands.Emulation as Emulation
import com.github.kklisura.cdt.protocol.commands.Page as Page
import com.github.kklisura.cdt.services.ChromeDevToolsService as ChromeDevToolsService
import com.katalon.cdp.CdpUtils
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

WebUI.openBrowser('')
WebUI.setViewPortSize(1024,768)
WebUI.navigateToUrl('http://demoaut.katalon.com/')

ChromeDevToolsService cdts = CdpUtils.getService()

saveWebPageAsMHTML(cdts, 'tmp/snapshot.mht')

WebUI.closeBrowser()

def saveWebPageAsMHTML(ChromeDevToolsService devToolsService, String outputFileName) {
    Page page = devToolsService.getPage()
    double width = 960
    double height = WebUI.executeJavaScript('return document.body.scrollHeight', null)
    Emulation emulation = devToolsService.getEmulation()
    emulation.setDeviceMetricsOverride(Double.valueOf(width).intValue(), Double.valueOf(height).intValue(), 1.0, Boolean.FALSE)
    emulation.setScrollbarsHidden(Boolean.TRUE)
    // convert the loaded web page into an instance of MHTML
    String mhtml = page.captureSnapshot()
    save(outputFileName, mhtml)
    emulation.setScrollbarsHidden(Boolean.FALSE)
}

def save(String filename, String data) {
    FileOutputStream fos = null
    try {
        File file = new File(filename)
        fos = new FileOutputStream(file)
        fos.write(data.getBytes())
    }
    catch (IOException e) {
        e.printStackTrace()
    }
    finally {
        if (fos != null) {
            try {
                fos.flush()
                fos.close()
            }
            catch (IOException e) {
                e.printStackTrace()
            }
        }
    }   
}
  • このスクリプトはKatalon Studioの WebUI.openBrowser('') メソッドによってブラウザを起動し、WebUI.navigateTo(url)メソッドによってWebページ http://demaut-katalon.kazurayam.com を開きます。ページが完全にロードされるまで待ちます。
  • CdpUtils.getService() を呼び出すことによりChrome DevTools Protocolを介してブラウザと通信するクライアントを生成します。
  • Chrome DevTools Protocolの Page.captureSnapshot をcallします。するとブラウザがWebページをMIME HTML形式のテキストに変換して応答します。
  • 応答されたテキストをローカルなファイルに保存します。

出力例

サンプルのスクリプトを実行すると下記のファイルが出力されました。MIME HTMLです。

CDPって面白い

Katalon StudioがWebブラウザを遠隔操作してUIをテストするにはSelenium WebDriverを使います。Katalon Studioでできるのは基本的にWebDriverでできることの範囲にとどまる。ところがCDPを使えばWebDriverでできなかったことがやれる。MIME HTMLへの保存はほんの一例です。下記のコード群を読むと驚きます。

これによればHTMLからリンクしているリソース(cssやjsや画像など)へのリクエストに介入してHTTP通信の内容を傍受するようなことがCDPを使えばできるらしい。こういうブラウザの内部動作に関してはWebDriverは全く手出しできない。Katalon StudioのなかでCDPを使えば何か面白いことができそうです。

補足説明

Katalon Studioのインストール、チュートリアル

Katalon Studioをどうやってインストールするか、最初の一歩を学ぶにはどこから行けばいいか? 公式ドキュメントは下記にあります。

充実していますが全部英語です。ちょっと英語は...という方は下記の記事も参考にしてください。

Proxyのこと

Katalon Studio ver7.xはインターネット上のKatalon社のサーバと通信してライセンスを認証しようとします。もしもあなたが職場のイントラネット上にあるPCを使っていて会社のProxyサーバを経由してインターネットにアクセスする必要があるならば、Katalon StudioにProxyの設定をする必要があります。Proxyの設定ができていなくてライセンスの認証が失敗するとKatalon Studioを使い始めることすらできません。下記の公式ドキュメントの説明を参考にしてください。