SUSH-i LOG

名古屋のWeb制作会社につとめるプログラマーのつぶやき

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

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)楽しいです。