Help us understand the problem. What is going on with this article?

Puppeteer を使って web ページの画像差分を作成する

ブラウザのオートメーションを使って 2 つの web ページの差分画像を作成してみます。 JavaScript のライブラリだけを使ってこれを実現します。

ライブラリのインストール

ヘッドレス Chrome (Chromium) を制御するライブラリ Puppeteerを使います。
yarn または npm を使い、Puppeteer と他に必要なライブラリをインストールします。

$ mkdir my-project
$ cd my-project
$ yarn init # or npm init
$ yarn add puppeteer looks-same # or npm install puppeteer looks-same

looks-same は、2 つの画像を比較して同じ画像なのかを判定したり、差分画像を生成することができるライブラリです。

ここでは、プログラムを作成する前に、生成する画像を保存するディレクトリを作成しておきます。

$ mkdir dist

 Puppetter を制御する JavaScript を作成する

Puppetter の API を使い、ブラウザを制御するプログラムを作成します。ここでは URL_AURL_B という変数名で比較する 2 つの web ページを指定します。
作成するプログラムでは以下の操作を行います。

  1. ヘッドレス Chrome を起動する
  2. URL_A に遷移する
  3. URL_A のスクリーンショットを作成する
  4. URL_B に遷移する
  5. URL_B のスクリーンショットを作成する
  6. 作成した 2 個のスクリーンショットから差分画像を生成する
// index.js
const puppeteer = require('puppeteer')
const looksSame = require('looks-same')

const URL_A = 'https://example.com/a'
const URL_B = 'https://example.com/b'

;(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.setViewport({ width: 512, height: 100 })

  const pathA = 'dist/a.png'
  const pathB = 'dist/b.png'
  const pathDiff = 'dist/diff.png'
  await page.goto(URL_A)
  await page.screenshot({ path: pathA })
  await page.goto(URL_B)
  await page.screenshot({ path: pathB })

  await new Promise((resolve, reject) =>
    looksSame.createDiff(
      {
        reference: pathA,
        current: pathB,
        diff: pathDiff,
        highlightColor: '#ff00ff'
      },
      error => (error ? reject() : resolve())
    )
  )

  await browser.close()
})()

プログラムを実行します。

$ node index.js

プログラムを実行すると、 dist ディレクトリの中にスクリーンショットとその差分が生成されているのがわかります。 なお、画像のうちの異なる部分はピンク色(#ff00ff)でハイライトされます。

実際の例

以下の微妙に内容が異なる 2 つの web ページのスクリーンショットの差分画像を実際に作成してみます。

<!-- a.html -->
<!DOCTYPE html>
<html>
  <body>
    <h1 class="title">Helloo!</h1>
    <p class="content">This is page A!</p>
  </body>
</html>
<!-- b.html -->
<!DOCTYPE html>
<html>
  <body>
    <h1 class="title">Hello!</h1>
    <p class="content">This is page B!</p>
  </body>
</html>

テキストの差分はこんな感じ。

$ diff a.html b.html
1c1
< <!-- a.html -->
---
> <!-- b.html -->
5,6c5,6
<     <h1 class="title">Helloo!</h1>
<     <p class="content">This is page A!</p>
---
>     <h1 class="title">Hello!</h1>
>     <p class="content">This is page B!</p>

前述のプログラムにハードコードされている URL を適切に変更します。
別途ウェブサーバーを立ち上げるなどして、上記の HTML に下記の URL でアクセスできるようにしておいてください。

// index.js
// ...
const URL_A = 'http://127.0.0.1:8080/a.html'
const URL_B = 'http://127.0.0.1:8080/b.html'
// ...

プログラムを実行します。

node index.js

以下のように画像が生成されます。

a.png
a.png

b.png
b.png

差分画像
diff.png

まとめ

Node.js を使って2つの web ページのスクリーンショットの差分画像を作成し、コンテンツの違いやスタイルの違いなどを検出できることが分かりました。Production と 開発環境の間でどのような見た目の変化が生じているかを可視化したり、 CI などで回してチャットツールに差分画像をポストするなども簡単にできそうですね。

tilecloud
地図専用クラウドサービスを提供するスタートアップ
https://geolonia.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away