スタイル・エッジ技術ブログ

士業集客支援/コンサルティングのスタイル・エッジのエンジニアによるブログです。

ブラウザ操作を「脚本家」に任せてみよう

はじめに

お初にお目にかかります。スタイル・エッジLABOの┣(読み: てぃー or てー)と申します。
今年からこちらにお世話になり、やっとのことDockerやLaravel等に慣れてきて、ただいまこうなっております↓

f:id:styleedge_tech:20200311075644j:plain
寝落ちカバ by フリー素材ぱくたそ(www.pakutaso.com)


ですが今回はそれらではなく、つい最近知ったブラウザテストツール「Playwright」について書き留めます。


Playwright #とは

前述のとおり、マイクロソフトが開発した「ブラウザ操作を自動化するためのNodeライブラリ」です。
ブラウザテストツールをご存じの方であれば「Puppeteerに近いもの」……という表現の方がわかりやすいかもしれません。
(そもそも「元Puppeteerの開発チームが開発した」とのことです)

Playwrightの利点

というわけで使用方法も内部的な構造もPuppeteerがベースになっているように見受けられますが、
Playwrightは下記のように「クロスブラウザ対応が容易」と感じました。
Webkitのパッケージ依存解決が大変すぎて心が折れました・・・

  • 標準でクロスブラウザ対応している
  • ブラウザを切り替えやすい
    • Puppeteer・・・モジュールごと切り替える

      const puppeteer = require('puppeteer');
      const pptrFirefox = require('puppeteer-firefox');
      
      (async () => {
        for (const pptr in [puppeteer, pptrFirefox]) {
          const browser = await pptr.launch();
          // 省略
        }
      })();
      


    • Playwright・・・パラメータ指定で切り替える

      const playwright = require('playwright');
      
      (async () => {
        for (const browserType of ['chromium', 'firefox']) {
          const browser = await playwright[browserType].launch();
          // 省略
        }
      })();
      


概要は以上です。
以下、学習用に実際にやってみた内容となります。

実際にやってみた

お題

今日のニュースをGoogle検索し、1ページ目の内容をCSVにまとめる

前提
  1. Docker for Windows(Docker Desktop 2.1.0.5) を使用する想定です。
    他の環境では未検証のためご了承ください。
  2. ゲストOSは┣がDebian系のディストリビュージョンに不得手なため、CentOSとさせて頂きます。
  3. 主な流れはコメントとして記載致しますが、各メソッドの挙動等につきましては特に触れません。
事前準備

  • docker-compose.yml

    version: "3.3"
    
    services:
        playwright-sample:
            container_name: playwright-sample
            build: .
            tty: true
            volumes:
                - .:/src
    


  • Dockerfile

    # 最新版のブラウザが動作しなくなるので常に最新イメージとする
    FROM centos:latest
    LABEL  maintainer "├"
    
    # 各種パッケージ インストール本体
    RUN curl -sL https://rpm.nodesource.com/setup_12.x | bash \
        && dnf install -y \
            # Node.js、日本語対応に必要なパッケージ
            nodejs-12.14.1-1nodesource.x86_64 glibc-locale-source \
            glibc-langpack-en glibc-langpack-ja wget unzip fontconfig \
            # ブラウザの動作に必要なパッケージ
            libX11 libXcomposite libXcursor libXdamage libXext \
            libXi libXtst cups-libs libXScrnSaver libXrandr \
            alsa-lib pango atk at-spi2-atk gtk3 \
            firefox \
        && rm -rf /var/cache/dnf/* \
        && dnf clean all
    
    # playwright インストール
    RUN npm i --no-save playwright
    
    # 日本語対応・・・キャプチャを取得する場合は必須
    # (最新版のCentOSはdnf installできない模様、今回はキャプチャを取得しないためコメントアウト)
    # RUN localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
    # ENV LANG="ja_JP.UTF-8" \
    #     LANGUAGE="ja_JP:ja" \
    #     LC_ALL="ja_JP.UTF-8"
    # RUN wget https://ipafont.ipa.go.jp/IPAfont/IPAfont00303.zip \
    #     && unzip IPAfont00303.zip \
    #     && mv IPAfont00303 ipafont \
    #     && rm -f ipafont/*.txt \
    #     && cp -r ipafont/ /usr/share/fonts/  \
    #     && fc-cache -f \
    #     && rm -rf ipafont IPAfont00303.zip
    
    # 作業ディレクトリ
    WORKDIR /src


スクリプト本体

  • example.js

    // ブラウザの種類とオプション
    const browsers = {
        chromium: {
            args: [
                // root権限で実行するために付与
                '--no-sandbox',
                '--disable-setuid-sandbox',
            ]
        },
        // firefox: {},     // firefoxで実行する場合はこちら
    };
    
    // 待ち合わせの設定(ドキュメント読み込みまで待つ)
    const waitOption = { waitUntil: 'domcontentloaded' };
    
    // ヘッダ行
    const header = '"タイトル","内容","URL"\r\n';
    
    // 検索ワード
    const word = '今日のニュース';
    
    // playwright 実行
    const playwright = require('playwright');
    const fs = require('fs');
    (async () => {
        for (const browserType in browsers) {
            // 1. ブラウザ起動
            const browser = await playwright[browserType].launch(browsers[browserType]);
            const context = await browser.newContext();
    
            // 2. googleへ遷移
            const page = await context.newPage();
            await page.goto('https://www.google.com/', waitOption);
    
            // 3. 検索
            await page.type('[title="検索"]', word);
            await Promise.all([
                page.keyboard.press('Enter'),
                page.waitForNavigation(waitOption),
            ]);
    
            // 4. ニュースタブを開く
            await Promise.all([
                page.click('[href*="tbm=nws"]'),
                page.waitForNavigation(waitOption),
            ]);
            await page.waitFor(1000);       // ページが描画されるまで適当に待つ
    
            // 5. 検索結果を取得
            const result = await page.$$eval('.ts > div', els => {
                return els.map((el) => {
                    const label = el.querySelector('a');
                    const content = el.querySelector('.st');
                    return `"${label.innerText}","${content.innerText}","${label.href}"`
                }).join('\r\n');
            });
    
            // 6. 検索結果をファイル出力
            fs.writeFileSync(`${word}.csv`, `${header}${result}`);
    
            // 7. 後始末
            await browser.close();
        }
    })();
    


実行手順
  1. 上記3ファイルを用意したフォルダ内でdocker-compose buildを実行してコンテナを作成
    • 「Successfully built」と表示されれば完了です。
  2. docker-compose up -dでコンテナを起動
    • -dをつけなければ起動ログが出るのでデバッグに役立ちます。
  3. docker exec -it playwright-sample bashでコンテナ内に入る
  4. node exampleスクリプトを実行
実行結果(出力ファイル一部分)

f:id:styleedge_tech:20200311075435p:plain

総括
  • サンプルではスクリーンショットもブラウザの切り替えも行っておりませんが、
    これらを併用すればデザインの確認にも一役買ってくれると思います。
  • 関連するものとして、弊社TMがSeleniumに関する記事を上げております。
    こちらも是非ともご覧ください。(リンク)

最後に

ご感想やご指摘等ございましたら後学のためにコメント頂けますと幸いです。


スタイルエッジ・LABOでは一緒に働く仲間を募集しております。
興味をお持ち頂けましたら是非とも下記をクリックしてください↓↓
recruit.styleedge-labo.co.jp