Puppeteerをnode.jsで動かし、Webページにjsの処理を加えたスクリーンショットを自動で連続撮影する

大量のスクリーンショットを撮影する必要があるときに、ヘッドレスブラウザを使うと楽ですが、撮影の際にjsで少し処理を加えたうえで撮影をするような場面で、Puppeteerを使ったら便利だったのでメモ。

# Puppeteer

[https://github.com/puppeteer/puppeteer](https://github.com/puppeteer/puppeteer) によると、以下のように謳われている。


> Puppeteerは、 DevToolsプロトコルを介してChromeまたはChromiumを制御するための高レベルAPIを提供するノードライブラリ。ブラウザにて手動で実行できるほとんどのことは、Puppeteerを使用して実行できます。


ヘッドレスChromeをシェルで扱うような方法より細かい調整が楽そうだったので今回使ってみた。

## スクリーンショットを撮影

まずは適当なページを開いてスクリーンショットを撮影するところまで。

適当なディレクトリにnodeの環境を用意し、index.jsを配置。

```zsh
% npm init -y
% npm i puppeteer
% touch index.js
```

demoとして[Yahooのトップページ](https://www.yahoo.co.jp/)を表示してみる。

```index.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://www.yahoo.co.jp/');
  await page.screenshot({ path: 'screenshot.png' });

  await browser.close();
})();
```

```zsh
# 実行
% node index.js
```

実行すると、800×600のpng画像が撮影できた。

縦は一番下まで、幅は1280pxの、おまけに@2xで撮影したい場合、以下のように設定を調整する。

```index.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setViewport({
    width: 1280,
    height: 900,
    deviceScaleFactor: 2
  })
  await page.goto('https://www.yahoo.co.jp/');
  await page.screenshot({ 
    path: 'screenshot@2x.png',
    fullPage: true,
  });

  await browser.close();
})();
```

## jsで処理を加える

要素を取得して処理を加えてみる。
わかりやすく、body要素を取り、**背景色を真っ赤**にしてみる。

```index.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setViewport({
    width: 1280,
    height: 900,
    deviceScaleFactor: 2
  })
  await page.goto('https://www.yahoo.co.jp/');

  // bodyタグの背景色をredにする
  const bodyHandle = await page.$('body');
  await page.evaluate(body => {
    body.style.backgroundColor = 'red';
  }, bodyHandle).catch(error => console.log(error));

  await page.screenshot({ 
    path: 'screenshot@2x.png',
    fullPage: true,
  });

  await browser.close();
})();
```

## リストからループ処理で撮影する

上記サンプルを応用して、 `red` 以外にも `blue`, `green`, `yellow`, `pink`, `purple` ... と色リストを用意し、自動的に撮影してみる。

```zsh
% touch list.txt
```

```list.txt
red
blue
green
yellow
pink
purple
```

```index.js
const puppeteer = require('puppeteer');
const fs = require('fs');
const listPath = 'list.txt';

fs.readFile(listPath, 'utf8', (err, data) => {
  const colors = data.split(/\n/);
  (async () => {
    for(const [index, color] of colors.entries()) {
      const browser = await puppeteer.launch();
      const page = await browser.newPage();
      await page.setViewport({
        width: 1280,
        height: 900,
        deviceScaleFactor: 2
      })
      await page.goto('https://www.yahoo.co.jp/');
      const bodyHandle = await page.$('body');
      await page.evaluate((body, color) => {
        body.style.backgroundColor = color;
      }, bodyHandle, color).catch(error => console.log(error));
      await page.screenshot({ 
        path: `${index}-${color}@2x.png`,
        fullPage: true,
      });
      await browser.close();
    }
  })();
});
```

これを実行すると、`0-red@2x.png`, `1-blue@2x.png`, ... `5-purple@2x.png` とスクリーンショットが撮影できた。

非常にざっくりとしたサンプルだが、これを応用することで幅広い要件に対応できそう。