Puppeteer でログインが必要なページのスクリーンショットを取得する

Puppeteer は node.js で Chromium (または Google Chrome) を利用してページを操作したりページ情報を取得するためのライブラリです。
このページでは、Puppeteer を使用して、ログインが必要なページのスクリーンショット (ページ全体のスクリーンショット) を取得する例を記載します。

前提

この手順で前提としているライブラリ等のバージョンは下記のとおりです。

ライブラリ等 バージョン
node.js v10.13.0 / v12.0.0
puppeteer 1.15.0

プロジェクトフォルダ上で下記のようなコマンドを使用して puppeteer を導入してください。

npm init -y
npm install puppeteer

注意

  • コードのライセンスは、CC0 (クレジット表示不要、改変可、商用可) です。
  • 以降に記載するコードは単純なログイン画面用 (1つのログイン画面にユーザーID、パスワード欄、ログインボタンがある) のものです。
    ソーシャルログイン (Google 認証, Facebook 認証 など) を使用するものや、IDとパスワードの入力が分かれているものなどはこのページに記載した処理ではうまく動作しません。(この処理をベースにカスタマイズしてみてください)
  • 実際に使用する場合には下記のような検討も必要になるはずですが、試験を目的としたコードとして記載しているためそれらは省いています。
    • パスワードの管理 (dotenv など)
    • アクセス負荷軽減やキャッシュ

コード

const puppeteer = require('puppeteer');

// 定数 (後述)
const LOGIN_URL = '';
const LOGIN_USER = '';
const LOGIN_PASS = '';
const TARGET_URL = '';
const LOGIN_USER_SELECTOR = '';
const LOGIN_PASS_SELECTOR = '';
const LOGIN_SUBMIT_SELECTOR = '';

/**
 * スクリーンショットのファイル名を取得します。
 * @returns YYYYMMDD-HHMMSS.png 形式の文字列
 */
function getFilename() {
    // タイムゾーンを調整して文字列化します。
    const offset = (new Date()).getTimezoneOffset() * 60000;
    const iso = (new Date(Date.now() - offset)).toISOString();
    const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/);
    return `${m[1]}${m[2]}${m[3]}-${m[4]}${m[5]}${m[6]}.png`;
}

/**
 * メイン処理です。
 */
(async () => {
    const browser = await puppeteer.launch({ // ブラウザを開く
        headless: true, // ブラウザを表示するか (デバッグの時は false にしたほうが画面が見えてわかりやすいです)
    });
    const page = await browser.newPage(); // 新規ページ
    await page.setViewport({ width: 1440, height: 900 }); // ビューポート (ウィンドウサイズ)
    await page.setExtraHTTPHeaders({ // 必要な場合、HTTPヘッダを追加
        'Accept-Language': 'ja'
    });

    // ログイン画面でログイン
    await page.goto(LOGIN_URL, { waitUntil: 'domcontentloaded' });
    await page.type(LOGIN_USER_SELECTOR, LOGIN_USER); // ユーザー名入力
    await page.type(LOGIN_PASS_SELECTOR, LOGIN_PASS); // パスワード入力
    await Promise.all([ // ログインボタンクリック
        // クリック後ページ遷移後通信が完了するまで待つ (ページによっては 'domcontentloaded' 等でも可)
        page.waitForNavigation({ waitUntil: 'networkidle0' }),
        page.click(LOGIN_SUBMIT_SELECTOR),
    ]);
    // ログイン後の画面に移動
    await page.goto(TARGET_URL);
    // スクリーンショット (フルページ)
    const filename = getFilename();
    await page.screenshot({ path: filename, fullPage: true });
    // ブラウザを閉じる
    await browser.close();
})();

定数

定数は下記のような意味です。

項目 概要
LOGIN_URL ログインURL
LOGIN_USER ログインユーザーID
LOGIN_PASS ログインパスワード
TARGET_URL ログイン後にスクリーンショットを撮影したい画面URL
LOGIN_USER_SELECTOR ログインユーザーIDの入力欄を特定するCSSセレクタ
(input[name=username] など。入力欄に id があれば #xxx などのほうが簡単)
LOGIN_PASS_SELECTOR ログインパスワードの入力欄を特定するCSSセレクタ
(input[type=password] など。同上)
LOGIN_SUBMIT_SELECTOR ログインボタンの場所を特定するCSSセレクタ
(input[type=submit], button[type=submit] など。同上)

定数の例: (楽天市場 - ポイントのスクリーンショット)

// 2019年4月時点
const LOGIN_URL = 'https://grp01.id.rakuten.co.jp/rms/nid/vc?__event=login&service_id=top';
const LOGIN_USER = ''; // 使用したいユーザーID
const LOGIN_PASS = ''; // 使用したいユーザーIDのパスワード
const TARGET_URL = 'https://point.rakuten.co.jp/history/?l-id=point_top_history_pc';
const LOGIN_USER_SELECTOR = 'input[name=u]';
const LOGIN_PASS_SELECTOR = 'input[type=password]';
const LOGIN_SUBMIT_SELECTOR = 'input[type=submit]';

定数の例: (SBI証券 - ポートフォリオのスクリーンショット)

// 2019年4月時点
const LOGIN_URL = 'https://www.sbisec.co.jp/ETGate/';
const LOGIN_USER = ''; // 使用したいユーザーID
const LOGIN_PASS = ''; // 使用したいユーザーIDのパスワード
const TARGET_URL = 'https://site1.sbisec.co.jp/ETGate/?_ControlID=WPLETpfR001Control&_PageID=DefaultPID&_DataStoreID=DSWPLETpfR001Control&_ActionID=DefaultAID&getFlg=on';
const LOGIN_USER_SELECTOR = 'input[name=user_id]';
const LOGIN_PASS_SELECTOR = 'input[type=password]';
const LOGIN_SUBMIT_SELECTOR = 'input[name=ACT_login]';

参考