12
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

NIJIBOXAdvent Calendar 2022

Day 16

Puppeteerで表示できない画面を探す

Last updated at Posted at 2022-12-15

Puppeteerで表示できない画面を取得しよう

さて問題です。Puppeteer とはなんでしょう?
Puppeteer とは、以下のようなことができるNode.jsのライブラリーです。

Puppeteer でできること】
・ページのスクリーンショットやPDFの作成
・SPAをクロールしてプレレンダリングコンテンツを生成
・フォーム送信の自動化
などGoogle Chromeのブラウザでできることはほぼできるようです。

上記を駆使して、たとえば「ページ内の大量のデータから取得したいデータのみを抽出する」や「表示できないページを割り出す」などを行うことができ、普段の業務で自分で一つ一つ探して確認してたものを自動で行うことができるようになります。

※ 公式サイトは以下です

今回はこちらのライブラリーを使って、表示できない画面を探すツールを作りながら、
使用した各メソッドが何ができるメソッドなのかを説明していきたいと思います。

今回作ったもの

こちらのGitHubにて作成済みのものを公開しておりますので、よろしければクローンして動かしてみてください。

実装手順

それでは、実装を説明していきます。

要件

今回は、以下の要件でツールを作成しました。
① data.jsonに記載したwebページの中で表示できないものがあれば全部取得
② デバイスはSPでもPCでも選択して確認できるようにしたい
③ Puppeteerを使ってみる

①は、実務などで検証環境によってあるページとないページがある場合に、「どのページが存在するかを確認したいという場面で使用できたらいいな」という気持ちと、「コードがいまいちわからない人でも簡単に使えたらいいな」という気持ちから、JSONにURLを持たせるという要件を入れてます。
②は、サービスによってはスマホ画面とPC画面をサーバー側で出しわけしている場合に、URLだけでは対象のデバイスでの検証ができないことから要件に入れました。
③は完全にPuppeteerを習得して業務の中でもツールで楽できるようにしていきたいという気持ちがあり、筆者が使いたかったからという理由だけです。

手順① Puppeteerをインストールしよう

以下コマンドでPuppeteerをインストールします。

npm i Puppeteer

手順② 必要なファイルを作る

今回、処理はfind404.jsに記載し、URLなどはdata.jsonに記載しようと思いますので、
以下の階層構造になるように★マークがついているファイルを作成します。

find404 ┳ node_modules
        ┣ data.json ★
        ┣ find404.js ★
        ┣ package-lock.json
        ┗ package.json

手順③ data.jsonに引用したい情報を記載する

今回、JSONに持たせたいデータは対象画面のURL対象デバイスです。
今回は以下の様にします。

{
    "targetPageUrl": [
        "https://pptr.dev/next/",
        "https://pptr.dev/next/hogeFuga",
        "https://nodejs.org/ja/",
        "https://developer.mozilla.org/ja/"
    ],
    "targetDevice": {
        "sp": "iPhone 12"
    }
}

targetPageUrlには配列にしたURLを記載します。
上記サンプルは2つ目のURL(https://pptr.dev/next/hogeFuga)が、実際には存在しないページです。
targetDevice.spには対象デバイスを指定しています。
指定できるデバイスの値は以下ページを参照してください。今回はiPhone 12を指定しています。

手順④ found404.jsに処理を書く

今回の完成系のコードは以下です。以下コードに沿って、Puppeteerで用意されているメソッドを解説していきます。

const puppeteer = require('puppeteer');
const fs = require('fs');
const jsonPath = './data.json';
// 検索条件やURLをdata.jsonから取得する
const data = JSON.parse(fs.readFileSync(jsonPath));
// 使用するデバイスを取得
const device = data.targetDevice.sp ? puppeteer.KnownDevices[data.targetDevice.sp] : false;
// data.jsonカラ取得したURLのリスト
const targetUrl = data.targetPageUrl;
// 対象urlが指定されていなかったら処理を中止
const noTargetUrl = !targetUrl.length || targetUrl.some((url) => !url.length);
if (noTargetUrl) {
    console.log('data.jsonのtargetPageUrlにurlを指定してください><');
    return;
}

(async() => {
    // 新規ブラウザをシークレットブラウザで立ち上げる
    const browser = await puppeteer.launch({
        headless: false,
        timeout: 10000,
        ignoreDefaultArgs: ['--disable-extensions'],
    });
    const context = await browser.createIncognitoBrowserContext();
    // 新規タブを開く
    const page = await context.newPage();
    if (device) {
        // 開くデバイスを指定
        await page.emulate(device);
    }

    try {
        const notFound = [];
        for (url of targetUrl) {
            // ページにアクセスする
            const response = await page.goto(url);
            // ページにアクセスできないあるいは404が帰ってきた時
            if (!response || response.status() >= 400) {
                notFound.push(url);
            }
        }

        // 400番台のページをログ出力
        console.log('==============================');
        if (!notFound.length) {
            console.log('すべてのページが存在します');
        } else {
            console.log('以下のページが表示できません。');
            console.log('==============================');
            notFound.forEach((url) => {
                console.log(url)
            });
        }
        console.log('==============================');
    } catch(e) {
        if (e instanceof Error) {
            console.log(e.message);
        } else {
            console.log('予期せぬエラー');
        }
    } finally {
        // チェックが終了したらブラウザーを閉じる
        await browser.close();
    }

})();

クラス

Puppeteerには以下のようなクラスが用意されており、各Classに用意されたメソッドを用いて処理を実現します。
実際には以下の記載よりも多くのクラスが存在しますが、今回は本記事のコードで使用しているものをピックアップして記載しています。

Class名 概要 コード上での使用箇所
Puppeteer Puppeteerのメインクラス このクラスから直接生えているメソッドは今回は使用していません
PuppeteerNode ブラウザのfetchとダウンロードのためのクラスでrequireで取得されます 1行目のpuppeteerで取得し、const browser = await puppeteer.launch({})でブランチを立ち上げるのに使用しています
Browser PuppeteerNode.launch()Puppeteer.connect()で生成されるクラスで、シークレットブラウザを指定するためのメソッドやブラウザを閉じるためのメソッドが用意されています 本記事のコードではconst context = await browser.createIncognitoBrowserContext();の行で使用しています
Page Browser.newPage()で生成されるクラスで、デバイスの指定やページ内の要素の取得やイベント実行などページとの対話を可能にするメソッドが用意されています 本記事のコードでは、await page.emulate(device);でのデバイス指定や、const response = await page.goto(url);でのページへのアクセスに使用しています
HTTPResponse Pageクラスが受信するHTTPリクエストの応答です 本記事のコードではif (!response ||response.status() >= 400) {}でページが存在するかどうかの判定に使用しています

KnownDevices

次の項で紹介するPage.emulate()の引数として使用できる、Puppeteerが用意しているデバイスのリストです。
コード一行目のpuppeteerに用意されてますので以下のようにして使用可能です。

const device = puppeteer.KnownDevices['iPhone 12'];

今回のコードでは、以下の行で設定を行っています。

// 使用するデバイスを取得
const device = data.targetDevice.sp ? puppeteer.KnownDevices[data.targetDevice.sp] : false;

Page.emulate()

上記で説明したKnownDevicesを使用して、デバイスを指定します。
Pageクラスのメソッドです。使い方は以下の通りで、ページを開いてから使います。

// 新規タブを開く
const page = await context.newPage();
await page.emulate(device);

上記で指定をすると以下のようにスマホ画面での実行がされます。(deviceには今回iPhone 12を指定しています)
ダウンロード.gif

PuppeteerNode.launch()

PuppeteerNodeクラスのメソッドで、オプションなどを指定してブラウザのインスタンスの起動を行います。
以下は今回のコードですが、headlessオプションをfalseに設定することでPage.emulate()の項目に載せたGIFのように、ブラウザが開かれて動いている様子が確認できます。デフォルトはtrue(headlessモード)です。timeoutオプションはブラウザ起動までの待機時間の最大をms単位で指定が可能で、0を渡すとtimeoutの無効化ができます。
※ Windows実行時、Puppeteerでは、デフォルトで--disable-extensionsフラグが有効かどうかを確認し、有効な場合に起動に失敗するのですが、ignoreDefaultArgs: ['--disable-extensions']を指定することで、回避して実行することが可能になります。

// 新規ブラウザをシークレットブラウザで立ち上げる
const browser = await puppeteer.launch({
    headless: false,
    timeout: 10000,
    ignoreDefaultArgs: ['--disable-extensions'],
});

他にも指定できるoptionはあるのですが、今回は使っていないため割愛させていただきます。

Page.goto()

Pageクラスのメソッドで、以下のようにURLを指定してあげることで、指定したページに遷移します。

// ページにアクセスする
const response = await page.goto(url);

url以外にもオプションを引数に取ることができ、最大待機時間などを指定ができます。
また、本メソッドはPromise<HTTPResponse | null>を返却するメソッドとなっており、
以下のようにページアクセス時のステータスを取得することが可能です。

// ページにアクセスできないあるいは404が帰ってきた時
if (!response || response.status() >= 400) {
    notFound.push(url);
}

ステータス以外にも、レスポンスに関連するHTTPヘッダーを持つオブジェクトやレスポンスに対するHTTPリクエストなどの取得も可能です。
ページ内で使っている非同期通信のエラー検知にも役立ちそうかと思います。

まとめ

今回はページの存在チェックをして存在しない、あるいは見つからないページのURLをログで返してくれるツールを作りつつ、Puppeteerのクラスやメソッドについてツール内で使用したもの中心に説明をしてきました。
この記事をきっかけに、「Puppeteer試しにいじってみよう!」「意外と簡単に始められるね」と気軽に挑戦するきっかけにしていただけると嬉しいです。
今回ご紹介したクラスやメソッドだけでも、便利なツールの作成ができると思うのですが、他にも多くのクラスやメソッドが用意されてますので、ぜひ調べてお役立ちツールを作ってみてください!

参考

12
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?