ウェブブラウザを用いたウェブアプリケーションの自動操作は、昔と違って妙に需要があります。これまでのローカルアプリケーションの操作は、VBAなどがDLLを介して行う事ができました。ウェブブラウザの場合、Selenuim Basicを使って操作することも可能です。
一方で現代の多くのウェブアプリケーションはREST APIを装備しているので、人間が操作するような操作方法を持ってして、作業を再現するような事も不要になりつつありますが、コードの記述を必要とするため、RPAなどを用いて作ってるのが現状です。
今回、GoogleのChromeチーム謹製のChromeを操作するNode.jsライブラリ「Puppeteer」を用いて、ブラウザ操作の自動化と、ついでに単独実行ファイル化をやってみたいと思います。
※Microsoft Playwrightと呼ばれるPuppeteerフォークがあり、こちらはSafariやFirefoxも同様に操作が可能みたい。ただまだリリースしたばかりで、APIが変更される可能性もあるので、注意が必要です。
難易度:
目次
今回使用するライブラリ等
puppeteerというモジュールもありますが、coreと違いChromiumを別途ダウンロードしてしまい、ファイルサイズが大きくなります。coreはすでにインストール済みのChromeを使う場合に利用します。今回は後者のケースで作ってみたいと思います。
インストールは以下のコマンド一発で終了です
1 |
npm i puppeteer-core |
pkgした実行ファイルは、ダブルクリックするだけで、Node.jsが入っていない環境でもPuppeteerが動き、Chromeを自動操縦します。
ソースコード
今回Googleを開き、「三峯神社」を検索し、スクリーンショットを取るというところまでを色々無駄にオプション設定して動かすコードをindex.jsに作り、node.jsで実行してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
const puppeteer = require('puppeteer-core'); (async () => { //option var option = { headless : false, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', slowMo:500, args: [ // ゲストセッションで操作する。 "--guest", // ウインドウサイズをデフォルトより大きめに。 '--window-size=1280,800', //最大化で表示 '--start-fullscreen', //情報バーの非表示 '--disable-infobars', //シークレットモード '--incognito', ], } const browser = await puppeteer.launch(option) const page = await browser.newPage() await page.setViewport({ width: 1280, height: 700, deviceScaleFactor: 1, }); await page.goto('https://www.google.com/') await page.type('input[name=q]', '三峯神社', { delay: 100 }) await page.click('input[type="submit"]') await page.waitForSelector('h3 a') await page.screenshot({ path: 'screenshot/result.png', fullPage: true}) await browser.close() })() |
- 予め、index.jsのあるフォルダ内にscreenshotというフォルダを作成しておく必要があります。
- optionとしてexecutablePathでChromeアプリがある場所までフルパスで指定してあります。
- headlessをfalseにすると、headlessモードではないseleniumのようにChromeを表示して動きを確認可能です。
- slowMoでミリセカンドを指定すると、動作を遅くすることが可能です。
- page.setViewportでサイズを指定しないと、chrome内でやけに狭い範囲で表示されてしまいます。
- screenshotでfullPageをtrueにすると現在表示されてる全部のスクリーンショットを取れます。
- Microsoft Edgeの最新版は中身がChromeなので、Puppeteerでコントロールが可能です。その場合のexecutablePathは「C:\\Program Files (x86)\\Microsoft\\Edge Dev\\Application\\msedge.exe」になります。macOS版だと「http://applications/Microsoft/ Edge/ Canary.app/」になるようです
図:こんな感じでresult.pngが出来ます。
Puppeteerが起動しない
オプションの追加が必要なケース
自宅のPCやmacOSでは問題なく動作するのに、会社のPCでは以下のようなエラーが出て起動しない!!というケースがあります。こちらでも言及されていますが、これに対する対応策は公式サイトにて、掲示されています。以下のようなoptionを追加する事で、起動するようになります。
1 2 3 4 |
//オプションを1個追加するだけ const browser = await puppeteer.launch({ ignoreDefaultArgs: ['--disable-extensions'], }); |
エラーは以下のようなもの
1 2 3 4 5 6 7 8 9 10 11 |
(node:888) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process! TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md at onClose (C:\Users\user\Documents\puppeteer\node_modules\puppeteer-core\lib\Launcher.js:750:14) at ChildProcess.<anonymous> (C:\Users\user\Documents\puppeteer\node_modules\puppeteer-core\lib\Launcher.js:740:61) at ChildProcess.emit (events.js:228:7) at Process.ChildProcess._handle.onexit (internal/child_process.js:272:12) (node:888) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:888) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. |
Failed to launch the browser processというエラーです。puppeteerでchromiumを使っても同様にエラーがでます。必ず、ignoreDefaultArgs: [‘–disable-extensions’]のオプションを追加するようにしましょう。
自分が利用してるオプションは以下の通り。こちらにリストアップされてるので、参考にしましょう。
- guest – アカウント無しの状態で起動させます
- disable-extensions – 拡張機能をオフの状態で起動させます
- start-fullscreen – フルスクリーンで起動。但し、viewportを指定すると無効化されます。
- incognito – 新しいコンテキストを生成して起動させる
- proxy-server=’direct:// – プロキシーサーバを指定する。例は無しでダイレクト接続の場合。
- proxy-bypass-list=* – プロキシーサーバ経由の場合、バイパスするアドレスの指定
- disable-infobars – Chromeの通知バーを消します。
Chromeの場所が特定できないケース
PuppeteerはChromeが必要です。特に今回はpuppeteer-coreなので、Chromeの別途インストールは必須ですが、入っていないケースも多いでしょう。また、管理者権限でインストールした先と、ユーザ権限でインストールした先が異なる為、入ってるのに使えないというケースもありえます。
そこで、これら3つのケースに対応したコードを冒頭で追加しておき、PuppetterのexecutablePathで確定した場所を指定するようにしてあげれば、対応が可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//使用するモジュール var shell = require('child_process').exec; var fs = require('fs'); const path = require("path"); //Chromeのパスを取得(ユーザ権限インストール時) const userHome = process.env[process.platform == "win32" ? "USERPROFILE" : "HOME"]; var kiteipath = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"; var temppath = path.join(userHome, "AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"); //chrome場所判定 if(fs.existsSync(kiteipath)){ var chromepath = kiteipath console.log("プログラムフォルダにChromeみつかったよ"); }else{ if(fs.existsSync(temppath)){ var chromepath = temppath; console.log("ユーザディレクトリにChrome見つかったよ"); }else{ console.log("chromeのインストールが必要です。"); //IEを起動してChromeのインストールを促す shell('start "" "iexplore" "https://www.google.co.jp/chrome/"') return; } } |
- kiteipathに本来管理者権限で入る先を指定する
- temppathにユーザ権限で入る先を指定する
- fs.existsSyncでファイルの有無を調べられます。あればtrue, なければfalseが返ってきます。
- いずれの場所にも入っていない場合にはインストール必要と判定し、child_processでInternet Explorerを起動し、Chromeのダウンロードページを開くようにする
- chromepathをexecutablePathに指定する
Chrome拡張機能で操作をコード化
今回のコードは非常に単純な作業ですが、それでもこれだけのコードを記述する必要があります。しかし、このPuppeteerの凄い点は、Google公式がメンテナンスしてるという点です。さらに、Chrome用の拡張機能として、Puppeteer Recorderがこれもまた公式からリリースされています。
selenium ideのように、Chrome上で録画スタートし撮影終了をすると、自動で上記のようなコードが生成される仕組みになっています。あとは生成済みコードを手直しすれば、ウェブ操作の自動化があっという間に完了するというわけです。慣れは必要ですが、高価なアプリを購入してRPAをやるより、まずこちらでチャレンジしてみる価値は十分にあると思います。
使い方はひどく簡単で
- 拡張機能をインストールする
- 右上の拡張機能からPuppeteer Recorderをクリックする
- Recordボタンをクリック
- Chromeで色々操作する
- Stopボタンを押すとPuppeteer用のNode.jsコードを生成してくれる
- コードを手直しして、Node.jsで実行するだけ
図:コードの手直しは必要だけれど、最低限の手直しで色々実現可能
pkgを利用してNode.jsを単一実行ファイル化
Node.jsがなければ、Puppeteerは動作させる事が出来ません。しかし、例えば社内で配布するとなると、Node.jsをインストールして実行用のスクリプトを配置してといった事は難しいです。
そこで利用したいのが、Node.jsおよびnode-module、index.jsを始めとするモジュール類を全部パッケージにして、実行ファイル化してくれるのがpkg。作成対象は、Windows, Linux, macOSの3環境用にジェネレートしてくれるので、コードの中でうまく動作環境に合わせた条件分岐を入れておけば、Puppeteerを動かす材料としては優れています。
インストール
インストールは簡単。グローバルモジュールとしてインストールします。
1 |
npm i -g pkg |
パッケージ作成
パッケージを作成するには、以下の手順で作成が可能です。Chromiumは含めていないので、別途事前にChromeがインストールされている必要があります。コードは上記の検索結果をpng化する操作になります。
- ターミナルを起動する
- プロジェクトのフォルダ内へ移動する
- index.jsがあると思うので、ターミナルでpkg index.jsと実行する
- そのままですと、プロジェクトフォルダ内に、3環境用に実行ファイルが生成されます。
- 特定のOS用にするならば、オプション引数をつければ良い(Windows用ならば–target=node10-win-x64をつける)
- コードの中でディレクトリの位置であったり、PuppetterならばChromeのファイルのパスなどは、OS毎に違うので要注意。
- ファイルサイズは今回のケースならば45MBほど。Electronで構築するよりかはずっとコンパクトです。
- EXE化できるという事は、例えばVBAから仕込んだPuppeteerを実行するであったり、Electronから呼び出すといった用途を実現する事が可能になります。
- macOS環境でwindows用のexeを作ったら動かず・・・なので、Windows環境でpkgを実行したら無事に動きました。
- pkgで作成できるバイナリは64bit環境でしか動作しません。
図:pkgにて実行ファイルを生成してみた
主に使うメソッド
公式リファレンスを見てみると膨大な量の様々な操作メソッドが列挙されています。また、Node.jsですのでその他のNode.jsモジュールを組み合わせてみたり、コードを追記すればより高度なChrome自動化が実現できるでしょう。ここでは、その中で非常によく使うであろうメソッドを列挙してみたいと思います。
launch
PuppeteerからChromeを起動します。この時、引数optionに色々と指定する事でヘッドレスChromeの挙動をコントロールする事が可能です。主なオプションは以下の通りです。全ての基本になるものです。
- headless – trueならばヘッドレスモードとなり、Chromeを見せずにバックグラウンドで起動します。
- executablePath – Chromeの実行ファイルのパスを直接指定する。Chromiumを同梱していない場合には必須です。Windows10 64bitなら「‘C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe’」等を指定する(管理者権限インストールの場合)。
- slowMo – Puppeteerの動作に掛かる時間を指定します。1個の動作に掛かるスピードが高速すぎる場合には指定すると上手くいくかも。ミリセカンド単位で指定する。
- args – 配列で指定する細かなオプション。フルスクリーンにしたり、画面のサイズを指定したり、シークレットモードで起動したりといったものを担当。
1 2 |
//Puppeteerをオプション付きで起動する const browser = await puppeteer.launch(options); |
Browserメソッド
launchで起動したPuppeteerは、Browser変数に入れてから色々と指示を出してあげます。
newPage
新しいタブを追加して、その中でChromeのインスタンスを実行します。。
close
起動したChromeを閉じます。操作が完了したら速やかに起動したChromeを終了させるべきです。でないと、何かエラーがあった場合、これらのインスタンスがゾンビ化して残るとも限りません。
1 2 3 4 5 |
//新しいタブでChromeに追加する const page = await browser.newPage(); //Puppeteerで起動したChromeを終了させる browser.close(); |
pageメソッド
Browserを起動したらpageに格納し、いよいよ細かなメソッドをここに順番に割り当てて、操作をしていきます。
goto(url, option)
指定したURLを開きます。この時OptionでwaitUntilを指定すると、例えばドキュメントの読み込みが完了するまでWaitといった指定をする事が可能です。指定しなかった場合、待たずに次の処理に進んでしまいます。
1 2 |
//googleを開き読み込み完了まで待機 page.goto("https://www.google.com/", {waitUntil: "domcontentloaded"}); |
関連リンク
- Puppeteer API Tip-Of-Tree
- PuppeteerでヘッドレスChromeを操ってみる
- puppeteer-coreとMacにインストール済みのChromeを使って自動操作
- ヘッドレス Chrome Node API 「Puppeteer」
- Puppeteerの使い方(スクレイピング, フロントテストで活用)
- puppeteerでクリックしてCSVをダウンロードする
- nexeでNode.jsのアプリをパッケージングしてみた
- node.js pkgで1ファイル実行ファイル化
- Chrome拡張「Puppeteer Recorder」を使って簡単にブラウザの自動操作を行う
- Python & Selenium のプログラムを実行形式(.exe)化する(webdriverも.exeに含める方法)
- 「Puppeteer Recorder」を試してみた
- Puppeteerでできることまとめ
- 【puppeteer】基本情報&逆引き
- Cloud Functions with Puppeteer + Google Apps Script でスクレイピングサーバーをサクッと作る
- cloud functions × puppeteer × Google Apps Scriptで超低コスト定期実行クローラを作って金曜ロードショーを毎週slackに通知させる
- microsoft/playwright – github
- Question: How do I get puppeteer to download a file?
- prompts : node.js用のコマンドラインからの値を受け取る時に便利なライブラリ
- Puppeteerでファイルダウンロード
- puppeteerでよく使うであろう処理の書き方
- Chrome操作ライブラリ chromy と puppeteer を比較
- Playwrightも知らないで開発してる君たちへ
[…] 動かせるのも魅力ですね。Puppeteerに関する記事はこちらからどうぞ。 […]
[…] Puppeteer – Googleが提供してるChromeの自動化を実現するNode.js用フレームワーク。Seleniumよりも遥かに扱いやすい。自分も実際に業務で利用。Chrome操作以外の部分についてはNode.jsにやらせ […]