コピペで簡単!puppeteerのこんなとき実例8件

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、このところNode.js(Express)での開発を続けていて色々と新しい技術や開発思想に触れる機会を多く持つことができました。

もちろんまだまだ学ぶことは多いのですが、Node.jsを使った開発はある程度理解が深まってきたので、この辺で、「これは今後も使いそう。でも、ちょっと複雑ですぐ忘れちゃいそう・・・😅」という内容を備忘録的にまとめておくことにしました。

ということで、今回は「実際のブラウザを使ってウェブサイトにアクセスできる」puppeteerを使った「こんなとき」をご紹介していきたいと思います。

puppeteerのインストール方法や基本的な使い方は、以前「JavaScriptでレンダリングしてるページをスクレイピングする方法」という記事を公開していますので、ぜひそちらをご覧ください。

ぜひ皆さんのお役に立てると嬉しいです😊✨

開発環境:Node.js 8

スタイルシートやJavaScript、画像などの外部ファイルを読み込まないようにする

大多数のウェブページでは、ひとつのページだけでなくスタイルシートやJavaScript、画像などを使って装飾や動きを加えています。さらにそれらは、.css.jsなど外部ファイル化している場合が多いですよね。

ただ、スクレイピングをする場合にはこういった「外部ファイル」が不要だったりする場合があったりします。

なによりpuppeteerchromeを使っているとはいえ、全ての外部ファイルを読み込んでしまうとサーバーに負荷がかかりすぎて動かなくなってしまう場合もあります。

そんな場合は、次のようにして不要なURLは事前に読み込みをキャンセルすることができます。

const puppeteer = require('puppeteer');

(async () => {

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.setRequestInterception(true);
    page.on('request', request => {

        const requestType = request.resourceType();

        if(requestType === 'document') {  // 文書ファイルのみOK

            request.continue(); // 続けて読み込む

        } else {

            request.abort();  // 外部ファイルは読み込まない

        }

    });
    await page.goto('https://www.yahoo.co.jp/');
    const headingText = await page.evaluate(() => {

        return document.querySelector('h1').innerText;  // h1のテキストを取得

    });
    await browser.close();

    console.log(headingText); // Yahoo! JAPAN

})();

流れとしては、Chromeが外部ファイルにリクエストをする直前にabort()を使ってキャンセルをしています。

なお、より細かくアクセスするURLを分けたい場合は以下がresourceType()で取得できるリソースタイプになりますので、if文などで制御するといいでしょう。

  • document
  • stylesheet
  • image
  • media
  • font
  • script
  • texttrack
  • xhr
  • fetch
  • eventsource
  • websocket
  • manifest
  • other

iFrameを読み込まないようにする

基本的に外部ファイルを遮断するには、前項目の「スタイルシートやJavaScript、画像などの外部ファイルを読み込まないようにする」で実現できますが、実はiFrameだけはリソースタイプが同じくdocumentになってしまうので、それだけでは遮断することができません。

そのため、iFrameの遮断をするには以下のようにframeattachedを使って「次読み込むURLはiFrameのものなのかどうか」で判別します。

const puppeteer = require('puppeteer');

(async () => {

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.setRequestInterception(true);

    let isIframe = false; // iFrameかどうかを判別する変数

    page.on('request', request => {

        const requestType = request.resourceType();

        if(requestType === 'document' && !isIframe) {  // 文書ファイルのみOK

            request.continue(); // 続けて読み込む

        } else {

            request.abort();  // iFrame、外部ファイルは読み込まない

        }

        isIframe = false; // iFrameの結果を初期化

    });
    page.on('frameattached', frame => {

        isIframe = true; // iFrameの読み込みの場合は "true" になる

    });
    await page.goto('https://www.w3schools.com/html/html_iframe.asp');
    const headingText = await page.evaluate(() => {

        return document.querySelector('h1').innerText;  // h1のテキストを取得

    });
    await browser.close();

    console.log(headingText); // HTML Iframes

})();

HTTPステータスコードを取得する

例えば、アクセスがうまくいったときの200やページが存在しないことを意味する404、エラーの500などのステータスコードを取得するには、responseを読み取ることで実現できます。

const puppeteer = require('puppeteer');

(async () => {

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.setRequestInterception(true);
    page.on('request', request => {

        const requestType = request.resourceType();

        if(requestType === 'document') {

            request.continue();

        } else {

            request.abort();

        }

    });

    let statusCode = 0;

    page.on('response', response => {

        statusCode = response.status(); // HTTPステータスコードを取得する

    });
    await page.goto('https://www.yahoo.co.jp/');
    const headingText = await page.evaluate(() => {

        return document.querySelector('h1').innerText;  // h1のテキストを取得

    });
    await browser.close();

    console.log(statusCode); // 200

})();

なお、レスポンスヘッダー全てを取得するには、response.headers()を使ってください。

以下は、取得できるデータのサンプルです。

{
    status: '200',
    'content-encoding': 'gzip',
    'accept-ranges': 'bytes',
    'cache-control': 'max-age=604800',
    'content-type': 'text/html; charset=UTF-8',
    date: 'Thu, 13 Feb 2020 10:19:08 GMT',
    etag: '"3147526947+gzip"',
    expires: 'Thu, 20 Feb 2020 10:19:08 GMT',
    'last-modified': 'Thu, 17 Oct 2019 07:18:26 GMT',
    server: 'EOS (vny/0452)',
    vary: 'Accept-Encoding'
}

jQueryを挿入する

例えば、Yahoo! JAPANにはjQueryは使われていませんが、強制的にcdnjQueryを読み込ませて$()を使ってみます。

const puppeteer = require('puppeteer');
const JQUERY_URL = 'https://code.jquery.com/jquery-3.4.1.min.js';

(async () => {

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.setRequestInterception(true);
    page.on('request', request => {

        const requestType = request.resourceType();
        const requestUrl = request.url();

        if(requestType === 'document' || requestUrl === JQUERY_URL) {

            // 通常ページとjQueryだけは読み込む
            request.continue();

        } else {

            // それ以外は読み込みをキャンセル
            request.abort();

        }

    });
    await page.goto('https://www.yahoo.co.jp/');
    await page.addScriptTag({ url: JQUERY_URL }); // jQueryを強制的に読み込ませる
    const headingText = await page.evaluate(() => {

        return $('title').text();  // jQueryを使ってページタイトルを取得

    });
    await browser.close();

    console.log(headingText); // Yahoo! JAPAN

})();

POST送信する

POSTメソッドでアクセスするには、以下のようにcontinue()でメソッドを指定します。

const puppeteer = require('puppeteer');

(async () => {

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.setRequestInterception(true);
    page.on('request', request => {

        const requestType = request.resourceType();

        if(requestType === 'document') {

            request.continue({ method: 'POST' }); // POSTメソッドでアクセス

        } else {

            request.abort();

        }

    });
    await page.goto('https://example.com/');
    const headingText = await page.evaluate(() => {

        return document.querySelector('h1').innerText;

    });
    await browser.close();

    console.log(headingText); // Example Domain

})();

ユーザーエージェントをセットする

ユーザーエージェントとは、ブラウザやアクセス環境を識別するためなどにつかわれている文字列のことで、簡単にいうと「私はGoogle Chromeです!」などと名乗ることができる文字列です。(つまり偽装はとても簡単ということです)

【ユーザーエージェントの例】

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36

ユーザーエージェントをセットするには以下のようにします。

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36');

// 省略

※ちなみにcnetの記事によると、Google Chromeではこのユーザーエージェントの機能を今後縮小していく方針のようです。

Cookieを追加する

特定のCookieをセットしてウェブサイトにアクセスするには、setCookie()を使います。

const cookies = [
    {
        'name': 'YOUR-COOKIE-NAME',
        'value': 'YOUR-COOKIE-VALUE',
        'domain': 'www.example.com', // ドメイン(省略可)
        'path': '/' // パス(省略可)
    }
];

const browser = await puppeteer.launch();
const page = await browser.newPage();
page.setCookie(...cookies);

Cookieの内容は配列でいくつでも追加できます。

スクリーンショットをとる

ウェブサイトのスクリーンショットをとるには以下のようにします。

const puppeteer = require('puppeteer');

(async () => {

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('http://example.com/');
    await page.screenshot({path: './screenshot.png'});

})();
開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回はNode.jsの強力な武器といってもいいpuppeteerの使い方をケースごとに紹介しました。

ただ、puppeteerは今回紹介した以外にも色々なことができますので、今後何か「おっ!」というものがありましたら、その都度このページに追加していきたいと思います。

ぜひご期待下さいね。

ではでは〜!

このエントリーをはてなブックマークに追加       follow us in feedly