はじめに
こんにちは。スタイル・エッジ・グループに新卒入社し2年目のK2です!9月半ばに本社の拠点がJR新宿ミライナタワーに移転し、一段と広く綺麗なオフィスになったので日々ワクワクしながら業務に励んでおります。
また、エンジニアとしても日々成長できるよう毎日楽しんで業務に取り組んでおります。
つい最近ですが、アカウント業務の半自動化を行うために、ブラウザの自動操作を行う機会がありました。
その際に Puppeteer というライブラリを使用したので、今回は Puppeteer についてまとめてみようと思います。
「Puppeteer」とは…
簡潔に説明すると、Puppeteerとは、DevToolsプロトコル上でChromiumやChromeを制御するためのNodeライブラリです。具体的にPuppeteerで出来ることの一例を挙げてみると、下記の通りです。
- ページのスクリーンショット、PDFを生成
- Webページ内での手動操作全般を自動化(フォーム送信、キーボード入力)
- E2Eテストの実行
- Webスクレイピングやクロール
今回は自分が実際に行っていた、Webページ内での手動操作の自動化や、自動化時に詰まったポイントなどをまとめようと思います。
それでは早速、Puppeteerの環境構築から行っていきましょう。
想定する構築環境
下記は実際にローカルで動作した際の環境です。※Windows上での環境です
Node v16.13.1 Npm 8.1.2 Puppeteer 18.0.3
環境構築手順
公式リファレンスはこちらをご覧ください1. Node.jsのインストール
Puppeteerの動作環境を作るにあたって、Node.jsのインストールが必要となります。
Node.jsのインストールがお済みでない方はこちらからインストールしてください。(推奨版のダウンロードをお勧めします)
2. 作業用ディレクトリ作成
Puppeteerで作業する用のディレクトリも作成しておきましょう。今回は下記コマンドにて「Puppeteer_PRJ」とい名前でディレクトリを作成しました。
mkdir Puppeteer_PRJ
ディレクトリ作成後、作成したディレクトリに移動しておきましょう。
cd Puppeteer_PRJ
3. Puppeteerのインストール
Node.jsのインストールが完了しましたら、先ほど作成したディレクトリ配下で、下記コマンドにてPuppeteerをインストールしてください。
npm install puppeteer
上記手順より、Puppeteerが動作する環境が構築できました。
それでは実際にソースを記述し、ブラウザを自動で動かしてみましょう。
サンプルコードでブラウザ操作自動化
今回は試しに、Googleにアクセス後、「Puppeteer」というキーワードで検索し、スクリーンショットを撮るまでの操作を自動化してみたいと思います。1.サンプルコード作成
Puppeteerをインストールしたディレクトリ階層以下(本記事では「Puppeteer_PRJ」のディレクトリ階層直下)にjsファイルを作成してください。今回はsample.jsを作成し、下記内容を記載しました。
※サンプルコードで使用している主な処理の詳細については、後述します。
sample.js
const puppeteer = require('puppeteer'); const path = require('path'); const { link } = require('fs'); const URL = 'https://www.google.co.jp/'; const PATH = '.'; (async () => { const browser = await puppeteer.launch({ headless: false, slowMo: 50, }); //ブラウザ立ち上げ const page = await browser.newPage(); await page.setViewport({ width: 1200, height: 800, }); //URLのページに移動 await page.goto(URL, { waitUntil: 'domcontentloaded' }); //検索欄に入力 await page.$eval('input[name="q"]', el => el.value = ''); await page.type('input[name="q"]', 'puppeteer'); //検索ボタンをクリック await Promise.all([page.waitForNavigation({ waitUntil: 'load' }), page.click('input[value^="Google"]')]); //現在表示されているページのスクリーンショットを指定のPATHに保存 await page.screenshot({ path: path.join(PATH, 'sample.png'), fullPage: true, }); await browser.close(); })();
2.コマンド実行
上記jsファイルを作成後にnodeコマンドを実行して、実際にブラウザの自動操作ができることを確認しましょう。
node sample.js
3.スクリーンショット確認
コマンド実行後、sample.jsを作成したディレクトリ階層と同じ階層に、下記sample.pngが作成できていることを確認できれば、自動化完了です。
Puppeteerで自動化する際によく使う処理
続いて、今回のサンプルコードでも利用した、Puppeteerで自動化する際によく使う処理もまとめてみました。ページ遷移
指定したURLにページ遷移します。waitUntilオプションを指定することで、ナビゲーション終了までの間待機します。
page.goto('https://www.google.co.jp/', {waitUntil: 'domcontentloaded'})
今回の例の場合、https://www.google.co.jp/'に遷移後、DOMツリーの構築完了まで待機します。
テキスト入力
特定のDOM要素に対して、文字を入力します。
主にテキスト欄に入力する際に使用します。
page.type('input[name="q"]', 'puppeteer')
上記の場合、name属性にqを持つinput要素に対して、「puppeteer」という文字列を入力します。
クリック
特定のDOM要素をクリックします。
主にページ遷移などで使用されますが、ラジオボタン、チェックボックスの要素に対してクリックを行う事もできます。
page.click('input[value^="Google"]')
セレクトボックス選択
select要素を参照し、指定されたvalue属性に一致する要素を選択します。
プルダウンを操作する際に使用されます。
page.select('select[name="sample"]', '1');
上記の場合「sample」というname属性を持つselect要素について、value値が「1」であるoption要素を選択します。
待機時間系
ページ遷移や、自動化する際の各ステップに応じて待機時間を設けることで、Puppeteerでの自動操作が安定します。
Puppeteer動作時に待機時間を設けない場合、処理の遷移時にDOMツリーの構築が完了していない可能性があります。
待機時間を適切に設けることで処理の遷移時にDOM要素が参照できないことを防ぐことができるので、自動化の際は重要なポイントです。
1.waitForSelector
指定したDOM要素が出現するまで待機します。
page.waitForSelector('input[name="sample"]'),
上記の場合、name属性にsampleを持つinput要素が出現するまで待機します。
2.waitForNavigation
ページが新しいURL に移動するか、ページが再読み込みされるまで待機します。
page.waitForNavigation({ waitUntil: 'load' })
3.各DOM要素指定のオプションにdelayを追加
前述した、テキスト入力、セレクトボックス指定、クリック等のDOM要素を指定するメソッドにはオプションとしてdelayをミリ秒単位で指定することができます。
特定の時間を予め待機させておきたい場合に使用します。
Puppeteerで自動化する際に詰まったポイント
続いて、Puppeteerで自動化する際に詰まったポイントを2つ紹介いたします。
要素が読み込まれる前に処理が実行されてしまう
1点目ですがブラウザ操作の自動化をする場合、DOM要素が取得可能なように待機時間の設定をすることがとても重要でした。
理由としては待機時間の設定をしない場合、DOM要素指定の処理を記述した際に、PCの動作状況(重い、軽い)ブラウザの動作によっては上手くDOM要素を参照できない場合があるためです。
クリック処理などだけではなく、自動操作したいブラウザの動作状況に応じて待機時間を正しく設けておくことで、スムーズに自動操作が進みます。
今回紹介した待機時間の処理は一例なので、自動化したい処理に応じた待機時間を設定することをお勧めします。
一意となるDOM要素の参照が難しい
2点目、一意となるDOM要素の参照が難しく個人的には詰まりポイントでした。
理由としては、DOM要素を参照する際にclassやnameなどの属性を元に参照するのですが、それらの単一属性の指定だけでは上手く参照できないケースがあるためです。
例として挙げると、下記の様なDOM要素(name属性にsampleを持つinput要素)を指定した場合
page.type('input[name="sample"]', 'test')
上記の様に「sample」というname属性を持つinput要素が1つであれば良いのですが、Webページによっては複数該当する場合もあります。
そうした場合、DOM要素は複数あるものの内1番最初の要素を取得するので、上記記述では複数該当するDOM要素の2番目の要素を指定できません。
取得する要素の、属性を変更する等で一部対応できましたが、そもそも指定したいDOM要素中の属性が少ない場合等も多くありました。
そこで解決策として、DOM要素の指定を一意にするために、取得したい要素の親要素で一意に絞り込める要素があるか探し、親要素も含め下記の様な形式で絞り込む方法で対応しました。
page.type('#inputWrapper input[name="sample"]', 'test')
上記例の様に親要素含めることで、取得したい要素を絞り込むことができます。
ただ、要素を細かく指定するデメリットとして、サイト側でコードの変更(コード変更があり、DOM要素の内容に変更がかかった場合)があった際に、上記の様な要素指定をするコード内容は都度変更しなければならない点が挙げられます。
サイト側でコードの変更が起こった場合は、自動操作が今まで通りできるか確認し、できない場合はPuppeteerで記述したコードの修正、若しくは自動操作の方法にキーボード操作を含めてみたりと工夫することも大切です。
最後に
いかがでしたでしょうか。
Puppeteerを利用すれば、上記の様に比較的簡単にブラウザ自動操作することができます。
ブラウザ上での定型的な手動作業を自動化することは、業務改善にもつながると思うので、一度試してはいかがでしょうか。
☆スタイル・エッジ・グループでは、一緒に働く仲間を募集しています☆
もし興味を持っていただけましたら、採用サイト↓も覗いてみてください!
recruit.styleedge-labo.co.jp