Puppeteer は、DevTools プロトコルを介してヘッドレス Chrome および Chromium を制御するための強力な API を提供する Node.js ライブラリです。最も便利な機能の 1 つは、Web ページと要素のスクリーンショットをプログラムでキャプチャできる機能です。
Web スクレイパーの場合、Puppeteer でスクリーンショットを撮ることができるため、さまざまな貴重な使用例が可能になります。
- スクレイピングの問題とテストの失敗を視覚的にデバッグします。
- 動的ページと SPA の状態をキャプチャします。
- 視覚的な低下と UI の変更を監視します。
- コンテキスト用のスクリーンショットを含むチュートリアルとドキュメントを作成します。
- Web ページから画像アセットを生成します。
この包括的なガイドでは、Puppeteer のスクリーンショットを活用して Web スクレイピング ワークフローを改善する方法を説明します。
Webスクレイピング用のPuppeteerの台頭
Puppeteer は 2017 年に初めてリリースされ、Web スクレイピング コミュニティで急速に採用されました。その人気を示すいくつかの統計を次に示します。
- Github には 52,000 を超えるスターがあり、トップの JS プロジェクトの XNUMX つとなっています。
- NPM では毎週 3 万を超えるダウンロードが行われています。
- 490 年の Google 検索における Puppeteer の検索数は前年比 2022% 増加。
では、Puppeteer が Web スクレイピングにおいて他と異なる点は何でしょうか?
ヘッドレスブラウザコントロール
Puppeteer は、Chrome DevTools プロトコルを介してヘッドレス ブラウザを完全に制御できます。これにより、ユーザー インタラクションを複製して自動化したり、動的コンテンツをスクレイピングしたりできるようになります。
軽量で高速
ヘッドレス専用であるということは、Puppeteer が Chromium を重量級にするすべての UI レンダリングをスキップすることを意味します。これにより、大規模なスクレイピングのパフォーマンスが高速になります。
アクティブ開発
Google の Chrome チームの支援により、Puppeteer は頻繁に更新され、自動化とスクレイピングのユースケースに合わせた新機能が提供されます。
Seleniumよりもシンプル
Selenium が複数のブラウザをサポートしているのに対し、Puppeteer は Chromium の制御のみに重点を置いています。 API ははるかにクリーンで慣用的なものになっているため、使いやすくなっています。
これらの理由から、多くの Web スクレイパーは、速度、信頼性、機能を向上させるために Selenium/WebDriver から Puppeteer に切り替えています。
次に、Puppeteer の強力なスクリーンショット機能を活用する方法を見てみましょう。
全ページのスクリーンショットのキャプチャ
ページ全体のスクリーンショットを撮る最も簡単な方法は、 page.screenshot()
方法:
// Launch browser
const browser = await puppeteer.launch();
// Open page
const page = await browser.newPage();
await page.goto(‘https://example.com‘);
// Screenshot
await page.screenshot({
path: ‘fullpage.png‘
});
これにより、現在表示されているビューポートがキャプチャされます。ページ全体の高さをスクリーンショットするには、 fullPage
オプション true
:
await page.screenshot({
path: ‘longpage.png‘,
fullPage: true
});
画像オプションの指定
screenshot()
このメソッドは、タイプ、品質などを制御するオプションを受け入れます。
type
– png、jpeg、または webp。デフォルトはpngです。quality
– jpeg/webp の場合、品質の範囲は 0 ~ 100 です。デフォルトは 80 です。omitBackground
– デフォルトの白い背景を非表示にし、透明化を許可します。encoding
– ファイルを保存する代わりに、base64 として出力できます。
たとえば、高品質の JPEG を保存するには:
await page.screenshot({
path: ‘page.jpeg‘,
type: ‘jpeg‘,
quality: 100
});
先端: 同等の品質で圧縮率を高めるには、webp を使用します。ただし、webp には互換性の問題がある可能性があります。
大きなスクリーンショットの処理
ページ全体のスクリーンショットのサイズは簡単に数メガバイトを超えることがあります。デフォルトでは、Puppeteer はスクリーンショットを保存する前にメモリにバッファリングするため、プロセスの制限を超える可能性があります。
大きなスクリーンショットを処理するには、オプションを渡します encoding: ‘base64‘
バッファの代わりにbase64文字列を取得します。次に、fs.writeFile() を使用して画像をメモリにバッファリングしないように保存します。
以下に例を示します。
const buffer = await page.screenshot({ encoding: ‘base64‘ });
fs.writeFile(‘screenshot.png‘, buffer, ‘base64‘, err => {
// handle error
});
全ページをキャプチャするために縦長のページをスクロールする
ビューポートよりも長いページの高さ全体をキャプチャするには、最初にページをスクロールする必要があります。
これを使用したアプローチの 1 つを次に示します。 page.evaluate()
:
// Scroll to bottom
await page.evaluate(() => {
window.scrollTo(0, document.body.scrollHeight);
});
// Screenshot full scrollable area
await page.screenshot({ path: ‘longpage.png‘, fullPage: true });
また、段階的にスクロールしてスクリーンショットを取得し、それらをつなぎ合わせて 1 つの縦長のスクリーンショットを作成することもできます。これにより、画像全体をメモリにバッファリングする必要がなくなります。
代替方法: PDF として保存
ページ全体のコンテンツをキャプチャするためのもう 1 つのオプション – PDF を生成します。
// Generates PDF and saves to disk
await page.pdf({
path: ‘page.pdf‘,
printBackground: true
});
PDF の長所:
- すぐに使える複数ページのコンテンツを処理します。
- 通常、ベクトル形式ではファイル サイズが小さくなります。
- 印刷フォーマットはそのまま残ります。
短所:
- プログラムによる処理には柔軟性が劣ります。
- 画像と比べてスタイルのオプションが限られています。
- 動的にレンダリングされたコンテンツをキャプチャできない可能性があります。
ビューポート サイズの設定
デフォルトでは、Puppeteer は 800px x 600px のビューポートを使用します。さまざまなデスクトップおよびモバイル サイズで正確な全ページのスクリーンショットを取得するには、ビューポートを明示的に設定できます。
// 1200px wide desktop
await page.setViewport({
width: 1200,
height: 800
});
// 400px wide mobile
await page.setViewport({
width: 400,
height: 1200
});
これにより、スクリーンショットが指定されたビューポート サイズと一致します。
要素のキャプチャ
ページ全体のスクリーンショットに加えて、次を使用して特定の要素のスクリーンショットをキャプチャできます。 element.screenshot()
.
// Get reference to element
const menu = await page.$(‘.main-menu‘);
// Screenshot just that element
await menu.screenshot({path: ‘menu.png‘});
スクリーンショットをキャプチャする前に、要素がスクロールして表示されます。これにより、スクロールせずに画面外にある可能性のある要素のショットをキャプチャできるようになります。
要素のスクリーンショットのいくつかの使用例:
- ティッカーやアニメーションなどの動的コンポーネントのスクリーンショットをキャプチャします。
- 個々の要素のショットを撮影して、レイアウトの問題をデバッグします。
- アイコンやイラストの画像アセットを取得します。
オフスクリーン要素のスクリーンショット
よくある問題は、インタラクション中にスクリーンショットをキャプチャしようとすると、要素が隠れたり移動したりすることです。
要素の自動スクロールを活用できます。 element.screenshot()
画面外であっても、あらゆる状態の要素を確実にキャプチャするには:
// Click button which hides the element
await page.click(‘.toggle-menu‘);
// Menu is now hidden but we can still screenshot it
await menu.screenshot({path: ‘hidden-menu.png‘});
これにより、ページの状態をリセットせずに簡単にスクリーンショットを撮ることができます。
動的コンテンツのロードを待機しています
動的ページを操作する場合は、コンテンツがレンダリングされるのを待ってから、スクリーンショットを撮って目的の状態をキャプチャする必要があります。
要素が表示されるのを待つ例を次に示します。
// Click button to trigger ajax call
await page.click(‘.load-content‘);
// Wait for new content to load
await page.waitForSelector(‘.loaded‘);
// Screenshot after loaded
await page.screenshot({path: ‘loaded.png‘});
page.waitForSelector()
セレクターが DOM に存在するまで待機してから続行します。
その他の有用な待機には次のようなものがあります。
page.waitFor()
– 指定された条件が true になるまで待ちます。page.waitForFunction()
– 非同期 DOM 更新が完了するまで待ちます。page.waitUntil()
– ナビゲーションが行われるまで待ちます。
重要なのは、スクリーンショットにキャプチャしたいページ更新の適切な待機条件を選択することです。
特定の DOM 変更を待機しています
より個別の DOM 変更と同期するには、ブランケット セレクターの代わりに属性が更新されるのを待つことができます。
// Wait for text content to change
await page.waitForFunction(() => {
return document.querySelector(‘.status‘).textContent === ‘Loaded‘;
});
// Element updated
await page.screenshot({/*...*/});
このアプローチは、静的な DOM 変更ではなく、主要なデータがロードされるのを待つ場合に適しています。
シングルページアプリ (SPA) の処理
リロードせずに状態を更新する複雑な JavaScript SPA では、DOM の変更を待つのが難しい場合があります。
これらに対処するためのいくつかのヒント:
- XHR が完了できるように、対話後にネットワークがアイドル状態になるまで待ちます。
- オーバーレイなどの特定のコンポーネントがブランケット セレクターではなく消えるまで待ちます。
- スクリーンショットを撮る前にレンダリングを強制するには、必要なセクションまでスクロールします。
- 固定タイムアウトの代わりに増分待機を使用します。
すべての SPA に完璧に機能する単一のアプローチはありません。問題のアプリを試してみる必要があります。
全ページのスクリーンショットを撮る前にページをスクロールする
スクロールが必要なページの場合は、完全なスクリーンショットを撮る前にプログラムでスクロールする必要があります。 fullPage: true
.
信頼できるアプローチは次のとおりです。
await page.evaluate(() => {
// Scroll to bottom
window.scrollTo(0, document.body.scrollHeight);
});
// Capture full scrolled screenshot
await page.screenshot({fullPage: true});
これにより、スクリーンショットを撮る前に、ページが最大スクロール位置まで下にスクロールされます。
別の方法は window.scrollBy()
一度に一定量ずつ段階的にスクロールします。これにより、ページ全体を下にスクロールしながら連続スクリーンショットを撮ることができます。
長いスクロール可能なページの処理
非常に長いページの場合、全体を一度にスクロールすると、メモリまたは時間の制限を超える可能性があります。
良い解決策は、セクションに分割し、一度に少しずつスクロールし、スクリーンショットを撮り、それらをつなぎ合わせることです。
const screenshots = [];
while (hasMoreContent()) {
await page.evaluate(scrollDown);
screenshots.push(await page.screenshot());
}
// Stitch screenshots together into one tall image
これにより、ページの高さ全体をメモリにバッファリングする必要がなくなります。
横にもスクロール
水平スクロールのあるページの場合、水平スクロールも行うようにスクロール シーケンスを調整できます。
await page.evaluate(() => {
window.scrollTo(
document.body.scrollWidth,
document.body.scrollHeight
);
});
await page.screenshot({fullPage: true});
これにより、ページ全体の幅と高さがキャプチャされます。
信頼性の高いスクリーンショットのためのベスト プラクティス
Puppeteer で一貫性のある信頼性の高いスクリーンショットを撮るための重要なヒントをいくつか紹介します。
ネットワークがアイドル状態になるまで待機する - 使用する page.waitForNetworkIdle()
インタラクションの後、状態をキャプチャする前にすべての非同期リクエストが完了していることを確認します。
適切な待機を使用する – 一括タイムアウトではなく、目的のページ状態と同期する条件付き待機を選択します。
ビューポートのサイズを設定する – 正確なデバイスのスクリーンショットをキャプチャするためにビューポートを明示的に設定します。
アニメーション/ポップアップからシールドする – 要素をホバーすると変更がトリガーされる – を使用 page.evaluate()
副作用を避けるため。
レンダリングに時間をかけてください – ページをスクロールした後、スクリーンショットを作成する前にレンダリングが完了するまで数百ミリ秒待ちます。
不安定なテストを安定化する – フレークを処理するために、スクリーンショットのステップの周りで待機する再試行ループを設定します。
既知の正常な製品と比較する – 視覚的な回帰テスト ツールを活用して、意図しない変更を検出します。
まとめ
このガイドが、Web スクレイピングのニーズに合わせて Puppeteer を使用してページ全体と要素のスクリーンショットを撮る方法の包括的な概要を提供できれば幸いです。
取り上げたいくつかの重要なトピック:
- page.screenshot() と element.screenshot() を使用してスクリーンショットをキャプチャする
- 画像の種類、品質、形式を制御するためのオプション
- ページをスクロールして動的コンテンツを待機しています
- レスポンシブ ページのビューポート サイズの設定
- 信頼性の高いスクリーンショット ワークフローのベスト プラクティス
自動化されたスクリーンショットは、スクレーパーのデバッグ、視覚的テスト、および動的状態のキャプチャに非常に役立ちます。 Puppeteer を使用して Web スクレイピング ツールキットに追加してください。