Puppeteer使いがPlaywrightを使ってみて感じた利点

公開:2021/02/02
更新:2021/02/04
5 min読了の目安(約5100字TECH技術記事 2

ブラウザの自動操作にPuppeteerを利用しているが、試しにPlaywrightを使ってみたら良いと思う点が多かったのでまとめた。正直な感想を言うと、「ほぼ上位互換では?」と思うくらいには良い点が多かったし、悪い点は見つからなかった。同じ作者の後発なだけはある。

なお、Puppeteer歴1年、Playwright歴1日で書いているので、変な箇所があればご指摘ください。

利用バージョン

  • Puppeteer : 5.5.0 5.4.1
  • Playwright : 1.8.0

便利だと思った点

とても柔軟なselector

Puppeteerはpage.$x()など一部でXPath selector が利用できるものの、page.click()page.$eval()など多くの関数ではCSS selectorしか利用できなかった。
しかし、Playwrightでは、selectorを利用するすべての関数で、CSS selectorもXPath selectorも利用できる。
それだけではなく、表示上のテキストでマッチさせるselectorがあったり、CSS selectorに便利な擬似クラスが追加してあったり、複数種類のSelectorを組み合わせるなど、かなり柔軟に要素が特定できる。

参照 : https://playwright.dev/docs/selectors

なお、独自のselectorを作成/登録することは、Puppeteer(5.4以降)でもPlaywrightでも可能。これを使うと、Puppeteerでも結構柔軟なselectorが作れると思う。

参照 : https://playwright.dev/docs/api/class-selectors
参照 : https://pptr.dev/#?product=Puppeteer&version=v5.5.0&show=api-interface-customqueryhandler

Auto-waiting

Puppeteerは要素が存在しない状態でelement.click()などをするとエラーとなってしまうため、page.waitForSelector()などで要素の出現を待ってから要素を操作する場面が多かった。
Playwrightでは、多くの操作でAuto waitingが採用されており、自動で一定時間要素の出現[1]を待ってくれる。これにより、page.waitForSelector()を使わずに簡潔に書ける場面がかなり増えた。

参照 : https://playwright.dev/docs/actionability

innerHTML / isCheckedなど、地味に便利なメソッド

内部テキストの取得や、チェックボックスにチェックが入っているか、など状態の取得は、Puppeteerではpage.$eval()などでブラウザ上でJavaScriptを走らせて取得していたが、Playwrightでは、element.innerText()element.isChecked()などの関数がデフォルトで用意されているため、書くときに時短になる。

ファイルダウンロード

Puppeteerはファイルのダウンロード処理に特化した処理がなく、ダウンロード完了を検知することができなかった。[2]

Playwrightでは、ダウンロードの開始をイベントとして知ることができ、完了までawaitすることもできるため、簡潔に書くことができる。

参照 : https://playwright.dev/docs/downloads

1行でファイル選択

<input type="file">でファイルを選択する際に、page.setInputFiles()element.setInputFiles()を利用すると、1行でファイルアップロード処理が可能
(Puppeteerと似たように書ける、FileChooserクラスも用意されている。)

参照 : https://playwright.dev/docs/input#upload-files

fill()でinputのテキストを上書き

<input type="text">や<textarea>にテキストがすでに入っている場合、PuppeteerやPlaywrightのelement.type()は、既存のテキストに追記する。
Playwrigtは、element.type()とは別にelement.fill()が用意されており、簡単にテキストの上書きができる。[3] Puppeteerでこれをやろうとすると、element.$eval()を使う(changeイベントが発火しない)、トリプルクリックやCtrl+aの後Deleteボタン(何やっているのかわかりにくい)などイマイチだった。

参照 : https://playwright.dev/docs/input#text-input
参照 : https://playwright.dev/docs/api/class-elementhandle#elementhandlefillvalue-options

selectのoption指定にlabelやElemenHandleが使える

puppeteerでは、<select>の<option>指定はpage.select()などでvalueを指定する必要があるが、value指定は結構面倒なことも多い。
playwrightでは、valueのほか、labelやindex(何番目のオプションか)も使えるし、なんならElementHandleの指定もできる。

参照 : https://playwright.dev/docs/api/class-elementhandle#elementhandleselectoptionvalues-options

コード自動生成

Puppeteerには、Puppeteer RecorderというChrome拡張のコードジェネレータがあるが、使ってみれば分かるが正直出来はあまりよろしくない。(イマイチの部分の多くは、ツール自体というよりはselectorの柔軟さのなさだが、それ以外の部分のコードも結構変。)[4]
Playwrightはコードジェネレータが付属しており、CLIからブラウザを起動して記録できるが、こちらはselectorの柔軟さも相まってそこそこ使えるコードを吐いてくれる。

参照 : https://playwright.dev/docs/intro#record-scripts

録画

browser.newPage()などの引数に、recordVideo オプションをつけることで録画が可能。
適当なページで試してみたら2分程のwebm動画(1280x720)で7MB程度[5]だった。

参照 : https://playwright.dev/docs/videos

多言語対応

PupetterはPythonバインディングであるPypetterがあるが、Playwrightは多言語対応が簡単になるような仕組みが用意されているようで、公式にPython/C#(Preview)/Java(Preview)に対応していほか、Rubyもそのうち対応するような書き方をしているし、独自に作っている人もいるようだ。
公式のRuby版が出たら試してみよう。

参照 : https://playwright.dev/docs/languages

クロスブラウザ対応

Playwrightのウリなので、一応触れておくと、Puppeteerよりも多くのブラウザに対応している。ただ、状況は少し複雑で単純な利点というわけではない。

ブラウザ Puppeteer Playwright
Chrome/Chromium
Edge(Blink)
Firefox △(Nightly) △(独自改造版)
Webkit × △(独自改造版)
Edge(EdgeHTML) × ×
IE × ×

Playwrightをインストールするときに、ブラウザを3つもダウンロードしてきて時間がかかると思っていたが、ブラウザを1つだけダウンロードするnpmパッケージが用意されているため、普段はこっちを使うと良さそう。
環境変数を設定することで、ブラウザのダウンロードをスキップすることも可能(Puppeteerでも可能)。

その他

ざっくりとしか使えていないので、あまり調査ができていない部分やマニュアルを見ただけで試していないものについて、ひとまずココにまとめる。
気になる機能をここに追記したり、試したものは別項目にしたりします。

  • ViewPortを設定しなくてもブラウザいっぱいに表示される?
  • keyboard.press()がPuppeteerよりも高機能なのでは?
  • ドキュメントのNight Mode が目に優しい
  • page.waitForLoadState()はどういうとき使うのかよく分かっていない
脚注
  1. 何を持って「出現」とするかは、操作により異なる。ドキュメント参照。 ↩︎

  2. ダウンロードフォルダに.crdownloadファイルがなくなるのを定期的にチェックする、などしていた。 ↩︎

  3. ドキュメントを読む限り、keydownなど一部イベントが発火しないようなので、状況によっては利用できない。 changeイベントは発火すると思っているが未確認。 ソースを見たら、ブラウザ側でJS走らせて、valueに入れている模様。changeinputイベントはdispatchEventで発火させている。やはりkeydownなどは発火しない。 ↩︎

  4. Chrome Canaryではrecorderを使えるらしいが試していない。 ↩︎

  5. 当然だが、録画内容に大きく依存する ↩︎