Puppeteerでヘッドレス Chromeのフルページスクリーンショットを撮る

Webページのスクリーンショットを大量に取りたいという時に、便利なのがヘッドレス Chrome。
ヘッドレスとは、GUIからではなくCUIからChromeを使用するモードのことで、Chrome 59以降から搭載されました。
ヘッドレス Chromeを使うと、ブラウザで行う様々な操作をコマンドラインから行い、自動化することもできるようになります。

今回はこのヘッドレス ChromeをNode.jsのプログラムで制御するためのライブラリである[Puppeteer](https://github.com/puppeteer/puppeteer)を使って、Webページ全体のスクロールショットを撮る方法を模索してみました。

まずはPuppeteerをインストール

```
npm i puppeteer
```

Puppeteerをインストールする際、自動でChromiumもインストールされますが、ローカルにあるChromeを使いたい場合には、puppeteer-coreのみでインストールすることもできます。

```
npm i puppeteer-core
```

puppeteer-coreを使用する場合、puppeteer.launchでブラウザを起動するときに、channel: 'chrome'というオプションを渡します。

```javascript
const browser = await puppeteer.launch({
    //ローカルのChromeを使う
    channel: 'chrome',
    //ヘッドレスモードで起動
    headless: true,
    //言語は日本語に
    args: ['--lang=ja']
});
```

ブラウザでスクリーンショットを撮りたいページへ移動します。
スクリーンショットを撮りたいサイズでviewportを指定することもでき、deviceScaleFactorに2を指定することで、Retinaのスクリーンショットを撮ることも可能です。

```javascript
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800, deviceScaleFactor: 2 });
await page.goto('https://qiita.com/');
```

スクリーンショットを撮る時には、fullPageをtrueにしておくと、Webサイト全体のスクリーンショットが撮れます。


```javascript
await page.screenshot({ path: './screenshot.png', fullPage: true });
```

が、これだけだと少し問題があって・・・・
スクロールに応じてコンテンツを読み込むlazyloadの部分があったりだとか、アニメーションなどが付いているときに、その部分がうまくスクリーンショットに反映されないという問題があります。

これに対処するためには、Webページに移動した後に、一旦ページの下までスクロールさせることが必要です。
少しややこしいので、今回は[puppeteer-autoscroll-down](https://www.npmjs.com/package/puppeteer-autoscroll-down)というライブラリの力を借ります。

```
npm i puppeteer-autoscroll-down
```

まずは念の為、スクロール位置を一番上に。
page.evaluateは、引数に実行したいスクリプトを渡すと、そのスクリプトをページ上で実行してくれます。

```javascript
await page.evaluate(_ => {
  window.scrollTo(0, 0);
});
```

そして、puppeteer-autoscroll-downを使って、ページの下までスクロールします。
渡せるオプションは、
size: 一回のスクロール量
delay: スクロールを実行する間隔
stepsLimit: スクロールする回数の最大値
です。
無限スクロールが実装されているページなどはかなり長くなってしまうので、stepsLimitを設定して、よい塩梅にしておくと良いと思います。

```javascript
const lastPosition = await scrollPageToBottom(page, {
  size: 400,
  delay: 250,
  stepsLimit: 50
})
```

最後にブラウザを閉じます。

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

コードの全体はこちら

```javascript
const puppeteer = require('puppeteer-core');
const { scrollPageToBottom } = require('puppeteer-autoscroll-down');
 
(async () => {
  const browser = await puppeteer.launch({
    channel: 'chrome',
    headless: true,
    args: ['--lang=ja']
  });
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 800, deviceScaleFactor: 2 });
  await page.goto('https://qiita.com/');

  await page.evaluate(_ => {
        window.scrollTo(0, 0);
  });
  const lastPosition = await scrollPageToBottom(page, {
    size: 400,
    delay: 250,
    stepsLimit: 50
  })

  await page.screenshot({ path: './screenshot.png', fullPage: true });
  await browser.close();

})();
```

参考にさせていただいた記事
[puppeteerで始めるブラウザ操作の自動化](https://www.cresco.co.jp/blog/entry/15215/)
[puppeteer-coreとMacにインストール済みのChromeを使って自動操作](https://qiita.com/YusukeIwaki/items/e8e197ddcdf7f7f3013b)
[Puppeteer with lazy loading images](https://stackoverflow.com/questions/57044231/puppeteer-with-lazy-loading-images)
[puppeteerで言語を日本語に設定する方法](https://qiita.com/DanballJP/items/fc3dd232edab741b9c29)