Herokuで日本語を含むページのスクリーンショットを撮ってみた(その2)

前回、node-webshotを利用してスクリーンショットを撮っていましたが、PhantomJSが開発終了していることに気づいたため、node-webshotからPuppeteerに変更してみました。(よくよく見ると、node-webshotも2016年から更新されていないんですね…)

Puppeteerを使ってみる

いつものごとくnpm、またはyarnでPuppeteerをインストールすると利用できます。

npm i puppeteer
# または
yarn add puppeteer

それぞれの関数はPromiseで返ってくることが多いため、サンプルを見てもawaitを多用しているのが印象的でした。 取り敢えずスクリーンショットを取るには、こんな感じでOKです。

import * as puppeteer from 'puppeteer'

const url = 'http://example.com'

(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.goto(url)

  // スクリーンショットを保存
  await page.screenshot({path: 'example.png'})

  await browser.close()
})()

ウィンドウサイズを設定する

キャプチャするウィンドウサイズを指定する際は、setViewport関数が用意されています。

// 省略
const width = 1024
const height = 768

(async () => {
  // 省略
  const page = await browser.newPage()
  await page.setViewport({
    width,
    height
  })
  // 省略
})()

deviceScaleFactorで、高解像度端末をシミュレーションすることも出来るみたいですね。

Basic認証下のページにアクセスする

Puppeteerには、authenticateメソッドがありました。

// 省略
const userId = 'user_id'
const passWord = 'password'

(async () => {
  // 省略
  await page.authenticate(userId, passWord)
  // 省略
})()

~~node-webshotと同様に、setExtraHTTPHeadersによってheaderを追加することで対応が可能です。 (前回に引き続きCryptoJSでBase64エンコードしてます)~~

/* 下記の様な記述はしなくてもOK
import * as CryptoJS from 'crypto-js';

// 省略
const userId = 'user_id'
const passWord = 'password'

(async () => {
  // 省略
  const wordArray = CryptoJS.enc.Utf8.parse(userId + ':' + passWord)
  await page.setExtraHTTPHeaders({
    Authorization: 'Basic ' + CryptoJS.enc.Base64.stringify(wordArray)
  })
  // 省略
})()
*/

ページ全体を撮影する

ページ全体なのか、ウィンドウ範囲内だけなのかも、screenshotのオプションで制御が可能です。 その他ちょっとしたオプションも併せてご紹介。

// 省略
(async () => {
  // 省略
  await page.screenshot({
    path: 'example.jpg', // example.jpg に保存
    type: 'jpeg',        // JPEG形式で保存
    quality: 80,         // 品質を0-100で指定
    fullPage: true       // trueにするとページ全体を保存
  })
  // 省略
})()

また、clipで切り抜きも出来るようですね。

Herokuで動かしてみる

次に、Herokuで動かしてみようとすると、そのままでは動かないことが分かりました。 そのまま動かそうとして発生したエラーのURLに、まさにHerokuの場合の解決方法が記載されていました。

Buildpackを追加する

(多分)初期状態では必要なライブラリが揃っていない様子なので、有志の方が作ってくれたBuildpackを追加して、Puppeteer(Chrome)が動く環境を整える、ということだと思います。

Herokuのダッシュボードからの場合は、Settings -> BuildpacksでGitHubのURLを入力します。 入力するURLはこちらか、日本語・中国語・韓国語に対応させたい場合はこちらです。

ちなみに、後者のBuildpackを利用する場合は、前回追加したフォントは不要なので、容量を削減する上でも削除した方が良いでしょう。 ~~というか、無料枠の500MB超えちゃってので~~

もしくは、Heroku CLIをインストールしているのであれば下記コマンドでも大丈夫です。

heroku buildpacks:add https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack

Puppeteerの起動オプションを追加

Buildpackを追加しただけでは、まだエラーが出てしまうので、Puppeteerの起動オプションを追加します。

import * as puppeteer from 'puppeteer'

const url = 'http://example.com'

(async () => {
  // 起動オプションを追加
  const browser = await puppeteer.launch({
    args: [
      '--enable-font-antialiasing',
      '--no-sandbox',
      '--disable-setuid-sandbox'
    ]
  })
  const page = await browser.newPage()
  await page.goto(url)

  await page.screenshot({path: 'example.png'})

  await browser.close()
})()

(Herokuじゃない時と振り分けておいた方が良いかも?)

雑感

前回のnode-webshotに比べると、Heroku上での起動に少しつまづいてしまいましたが、node-webshotで気になっていた点が解消されたので、変更して良かったと思いました。

また、Puppeteerに乗り換えるちょっと前にawait、asyncを勉強していてタイムリーでした。TypeScript(JavaScript)楽しいです。