Puppeteer で Web サイトのスクリーンショットを自動化する方法
Puppeteer と JavaScript で Web サイトのスクリーンショットを自動化します。単一ページ、ページ全体、URL の一括処理、モバイル表示を再利用できるスクリプトで撮影します。

この記事では、Puppeteer のスクリーンショットスクリプトを段階的に組み立てます。まず 1 画面分を撮影し、次にページ全体、再利用できる URL 処理、モバイルのビューポートへと進みます。
Puppeteer は、最初のスクリプトを小さく保てる点で、この用途に向いています。ブラウザを起動し、URL を開き、page.screenshot() を呼び出すだけです。公式の Puppeteer 概要では、Puppeteer は Chrome または Firefox を操作する JavaScript API であり、既定ではヘッドレスで動くと説明されています。
例では Shotomatic のツールページ https://www.shotomatic.com/tools を使います。同じページについて、ビューポート、ページ全体、一括処理向け、モバイルの各撮影結果を比べられます。
要点: スクリーンショットの自動化をコードで管理するなら Puppeteer を使います。ビューポートを設定し、必要なページ状態まで待って
page.screenshot()を呼び出します。その後、ページ全体の撮影、一括処理、再試行、レポートを追加します。スクリプトを管理せず公開 URL のリストだけを撮影したい場合は、Shotomatic の Website Captureのようなノーコードの手順が簡単です。
Puppeteer が適している場面
スクリーンショットの手順をコードとして管理し、主な作業がブラウザの操作である場合は、Puppeteer が適しています。
たとえば、次の用途です。
- Node.js スクリプトから公開ページを撮影する
- スクリーンショットに Chrome のレンダリング動作を利用する
- 撮影前にログインする、またはポップアップを閉じる
- ビューポート、ページ全体、PNG、JPEG、WebP の各スクリーンショットを保存する
スクリーンショットは画像ライブラリではなく、実際のヘッドレスブラウザから生成されます。Puppeteer がこの用途で役立つ最大の理由です。
撮影スクリプト用に Puppeteer をインストールする
通常の Node.js プロジェクトでは、Puppeteer をインストールします。
$ mkdir website-screenshots-puppeteer
$ cd website-screenshots-puppeteer
$ npm init -y
$ npm i puppeteer
$ mkdir scripts screenshots
標準の puppeteer パッケージは、インストール時に互換性のある Chrome for Testing をダウンロードします。Puppeteer の公式インストールガイドによると、chrome-headless-shell バイナリもダウンロードされます。ブラウザを自分で管理している場合や、リモートのブラウザへ接続する場合は、代わりに puppeteer-core を使います。
現代のパッケージマネージャーでは、インストールスクリプトがブロックされることがあります。Puppeteer はインストールされているのにブラウザがない場合、公式の設定ガイドに従ってブラウザを別途インストールできます。
$ npx puppeteer browsers install
Web サイトを 1 ページ撮影する
scripts/capture-one.mjs を作成します。
import puppeteer from "puppeteer";
import { mkdir } from "node:fs/promises";
const url = "https://www.shotomatic.com/tools";
const outputPath = "screenshots/shotomatic-tools.png";
const browser = await puppeteer.launch();
const page = await browser.newPage();
try {
await mkdir("screenshots", { recursive: true });
await page.setViewport({
width: 1440,
height: 1000,
deviceScaleFactor: 1,
});
await page.goto(url, {
waitUntil: "networkidle2",
timeout: 45_000,
});
await page.screenshot({ path: outputPath });
console.log(`Saved ${outputPath}`);
} finally {
await browser.close();
}
実行します。
$ node scripts/capture-one.mjs
このスクリプトを実行すると、screenshots/shotomatic-tools.png が作成されます。
Puppeteer の公式スクリーンショットガイドも、ブラウザを起動し、ページを開き、URL へ移動して page.screenshot() を呼び出すという同じ基本構成を使っています。type オプションを直接渡さない限り、path の拡張子で出力形式が決まります。Puppeteer の ScreenshotOptions と ImageFormat のドキュメントでは、fullPage、path、quality と、対応する png、jpeg、webp 形式を確認できます。
ビューポートはページを開く前に設定します。Puppeteer の page.setViewport() に関するドキュメントでも、ページの読み込み後にサイズを変えると、特にモバイル向けレイアウトでサイトの動作が変わることがあると説明しています。
ページ全体のスクリーンショットを撮る
ページ全体のスクリーンショットを撮るには、fullPage: true を渡します。Puppeteer の ScreenshotOptions では、fullPage をページ全体のスクリーンショットを撮るためのオプションとして定義しています。
await page.screenshot({
path: "screenshots/shotomatic-tools-full-page.png",
fullPage: true,
});
スクロール可能な文書全体が必要なら、ページ全体を撮影します。特定の画面サイズでページがどう見えるかを確認したい場合は、ビューポートのスクリーンショットを使います。
ページ全体の撮影でも、結果はページの状態に左右されます。fullPage: true は撮影範囲を変えるだけで、遅延読み込みの画像、アニメーション、画面内に入ったときに表示されるセクションを強制的にレンダリングするわけではありません。後ほど、ページ状態が重要になる理由を遅延読み込みの項目で説明します。
再利用できる URL 処理スクリプトを作る
1 ページの撮影が動いたら、URL のリストを受け取れるスクリプトへ対象を移します。例では引き続き /tools だけを使うため、出力を追いやすくなっています。後から同じ配列に URL を追加できます。
scripts/capture-batch.mjs を作成します。
import puppeteer from "puppeteer";
import { mkdir } from "node:fs/promises";
const urls = ["https://www.shotomatic.com/tools"];
const outputDir = "screenshots";
function filenameForUrl(url, index) {
const parsed = new URL(url);
const rawName = `${parsed.hostname}${parsed.pathname}`;
const slug = rawName
.replace(/^www\./, "")
.replace(/\/$/, "")
.replace(/[^a-z0-9]+/gi, "-")
.replace(/^-+|-+$/g, "")
.toLowerCase();
return `${String(index + 1).padStart(2, "0")}-${slug || "home"}.png`;
}
async function captureUrl(browser, url, index) {
const context = await browser.createBrowserContext();
const page = await context.newPage();
const outputPath = `${outputDir}/${filenameForUrl(url, index)}`;
try {
await page.setViewport({
width: 1440,
height: 1000,
deviceScaleFactor: 1,
});
await page.goto(url, {
waitUntil: "networkidle2",
timeout: 45_000,
});
await page.waitForSelector("body", {
visible: true,
timeout: 15_000,
});
await page.screenshot({
path: outputPath,
fullPage: true,
});
console.log(`Captured ${url} -> ${outputPath}`);
return { url, ok: true, outputPath };
} catch (error) {
console.error(`Failed ${url}: ${error.message}`);
return { url, ok: false, error: error.message };
} finally {
await context.close();
}
}
await mkdir(outputDir, { recursive: true });
const browser = await puppeteer.launch();
try {
const results = [];
for (const [index, url] of urls.entries()) {
results.push(await captureUrl(browser, url, index));
}
const failed = results.filter((result) => !result.ok);
if (failed.length > 0) {
process.exitCode = 1;
console.error(`${failed.length} screenshot(s) failed.`);
}
} finally {
await browser.close();
}
この構成では 1 つのブラウザを開いたまま、URL ごとに別のブラウザコンテキストを作成します。Puppeteer の BrowserContext に関するドキュメントでは、コンテキストを Cookie とストレージが分離されたユーザーセッションとして説明しています。撮影中にページが Cookie やローカルストレージを設定する場合に役立ちます。
最初は、意図的に 1 件ずつ実行します。速度は遅いものの、デバッグは簡単です。
件数が多いときは同時実行を加える
URL が数件なら 1 件ずつの撮影で十分です。リストが大きくなったら、少数ずつ同時に処理するよう制限します。
まず 2〜4 件の同時撮影から始めます。ヘッドレスブラウザも CPU とメモリを使います。短時間に大量のアクセスを送ると、レート制限や不安定なページ読み込みの原因になります。
次の補助関数を追加します。
async function runWithConcurrency(items, limit, worker) {
const results = new Array(items.length);
let nextIndex = 0;
async function runNext() {
while (nextIndex < items.length) {
const currentIndex = nextIndex;
nextIndex += 1;
results[currentIndex] = await worker(items[currentIndex], currentIndex);
}
}
const workers = Array.from(
{ length: Math.min(limit, items.length) },
() => runNext(),
);
await Promise.all(workers);
return results;
}
続いて、1 件ずつ実行するループを置き換えます。
const results = await runWithConcurrency(urls, 3, (url, index) =>
captureUrl(browser, url, index),
);
これで、スクリプトが際限なくページを開くことなく、一括処理の速度を上げられます。
モバイル表示を撮影する
モバイルサイズのスクリーンショットを撮るには、page.goto() の前にビューポートを設定します。
await page.setViewport({
width: 390,
height: 844,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
});
await page.goto("https://www.shotomatic.com/tools", {
waitUntil: "networkidle2",
});
await page.screenshot({
path: "screenshots/shotomatic-tools-mobile-viewport.png",
});
おおよそのモバイル表示で十分な場合に使います。同じツールページをこのビューポートで撮影すると、次のようになります。
さらに詳しいデバイス設定が必要なら、Puppeteer の既知のデバイスも利用できます。
import puppeteer, { KnownDevices } from "puppeteer";
const iPhone = KnownDevices["iPhone 15"];
await page.emulate(iPhone);
await page.goto("https://www.shotomatic.com/tools", {
waitUntil: "networkidle2",
});
await page.screenshot({
path: "screenshots/shotomatic-tools-iphone-15.png",
fullPage: true,
});
ビューポート、ユーザーエージェント、タッチ動作、デバイスピクセル比のすべてが重要なら、デバイスのエミュレーションが便利です。Puppeteer の page.emulate() に関するドキュメントによると、このメソッドはユーザーエージェントとビューポートの両方を設定するため、ページを開く前に使います。
遅延読み込みのコンテンツに対応する
多くのページでは、画像が画面内に入ってから読み込まれたり、セクションが表示されたりします。ページ全体を早く撮りすぎると、画面外のコンテンツがまだ表示されず、空白のまま残ることがあります。
ページ全体を撮影する前に文書を下までスクロールし、一般的な遅延読み込みを動かします。
async function triggerLazyLoading(page) {
await page.evaluate(async () => {
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const viewportHeight = window.innerHeight;
const scrollHeight = document.body.scrollHeight;
for (let y = 0; y < scrollHeight; y += viewportHeight) {
window.scrollTo(0, y);
await delay(150);
}
window.scrollTo(0, 0);
});
}
page.screenshot() の前に実行します。
await page.goto(url, {
waitUntil: "networkidle2",
timeout: 45_000,
});
await triggerLazyLoading(page);
await page
.waitForFunction(
() => Array.from(document.images).every((image) => image.complete),
{ timeout: 15_000 },
)
.catch(() => {});
await page.screenshot({
path: outputPath,
fullPage: true,
});
これだけでは足りないページもあります。独自のスクロール領域、アニメーション、遅れて実行される API 呼び出し、仮想化されたコンテンツでは、ページ固有のセレクターや状態を待つ必要があります。
適切な待機方法を選ぶ
スクリーンショットの安定性に関する問題は、多くがタイミングに行き着きます。
多くの撮影スクリプトでは networkidle2 が出発点になり、Puppeteer のスクリーンショットガイドの基本例でも使われています。ただし、ページの準備完了を保証する条件ではありません。バックグラウンド通信を開いたままにするサイトもあれば、通信が落ち着いてから主要コンテンツを描画するサイトもあります。
よく使う待機方法は次のとおりです。
- 一般的な公開ページでは
waitUntil: "networkidle2"を使う - 特定の要素がページの準備完了を示す場合は
page.waitForSelector()を使う - アプリの状態によって準備完了を判断する場合は
page.waitForFunction()を使う - 画像が遅延読み込みされる場合は撮影前にスクロールする
- 固定時間の待機を主な待機条件にしない
例は次のとおりです。
await page.goto(url, {
waitUntil: "networkidle2",
timeout: 45_000,
});
await page.waitForSelector("main", {
visible: true,
timeout: 15_000,
});
await page.screenshot({
path: outputPath,
fullPage: true,
});
対象サイトを自分で管理しているなら、一般的なネットワーク状態を待つより、セレクターを待つほうが安定します。
再試行とレポートを追加する
実際の一括撮影では、一時的なネットワークエラー、遅いページ、リダイレクト、レート制限、Cookie バナー、外部スクリプトなど、通常起こり得る理由で失敗します。
各 URL の処理に再試行を追加します。
async function withRetries(task, retries = 2) {
let lastError;
for (let attempt = 0; attempt <= retries; attempt += 1) {
try {
return await task();
} catch (error) {
lastError = error;
console.warn(`Attempt ${attempt + 1} failed: ${error.message}`);
}
}
throw lastError;
}
撮影処理をこの関数で囲みます。
await withRetries(async () => {
await page.goto(url, {
waitUntil: "networkidle2",
timeout: 45_000,
});
await page.waitForSelector("body", {
visible: true,
timeout: 15_000,
});
await page.screenshot({
path: outputPath,
fullPage: true,
});
});
繰り返し実行するジョブでは、URL、出力先、成功状態、エラーメッセージ、時刻を含む JSON レポートも保存します。スクリーンショットはページを示し、レポートは実行内容を説明します。
実用的なフォルダ構成
繰り返し実行するジョブでは、入力、出力、レポートを分けておきます。
website-screenshots-puppeteer/
scripts/
capture-batch.mjs
urls/
product-pages.txt
competitor-pages.txt
screenshots/
2026-06-23-product-pages/
2026-06-23-competitors/
reports/
2026-06-23-product-pages.json
この構成なら再実行とデバッグがしやすく、スクリーンショット一式と、その元になった URL リストの関係も保てます。
よくある間違い
- ページの準備ができる前に撮影する:
page.goto()が完了しても、必要なコンテンツが表示されたとは限りません。セレクターまたはアプリの状態を待ちます。 - ビューポートの設定が遅すぎる:最初から正しいレイアウトでページを読み込めるよう、ページを開く前に設定します。
fullPageを遅延読み込みへの対応だと考える:ページ全体の撮影は範囲を変えるだけで、遅れて読み込まれるすべての画像が表示されるとは限りません。- 一度に開くページが多すぎる:同時実行数を少なくして始めます。ブラウザの自動操作は、端末や対象サイトに過度な負荷をかけることがあります。
- ファイル名を使い回す:URL は末尾のスラッシュ、クエリ文字列、パスの大文字・小文字だけが違う場合もあります。連番を加え、ファイル名を安全な形に変換します。
- 終了処理を省く:コンテキストとブラウザを閉じます。ページやセッションが残ると、長時間動くジョブが不安定になります。
Puppeteer スクリプト以外の選択肢
Web サイトのスクリーンショットを自動化する方法は Puppeteer だけではありません。
コード内で完結させるなら、Playwrightが最も近い代替手段です。Playwright で Web サイトのスクリーンショットを自動化する方法で手順を紹介しています。スクリーンショットを大規模なテストスイートに組み込む場合、Chromium、Firefox、WebKit を対象にする場合、撮影処理とともに Playwright Test のテストランナー、トレース、レポートを使いたい場合に適しています。
スクリーンショット撮影を製品に組み込むなら、自分でブラウザを保守するよりスクリーンショット APIを使うほうが手間を減らせる場合があります。ほかの人が安定して実行できる状態にするには、ブラウザのバイナリ、CI のメモリ、再試行、終了処理、ログイン状態の管理も必要になります。
スクリプトの作成や保守に時間をかけたくない場合は、コードを書かずに Web サイトのスクリーンショットを自動化する方法をご覧ください。URL リストからの撮影、ページごとの設定、結果の確認、書き出しを Puppeteer スクリプトなしで行うなら、Shotomatic の Website Captureを使えます。
よくある質問
Puppeteer で Web サイトのスクリーンショットを自動化できますか?
はい。Puppeteer ではブラウザを起動し、URL を開き、ビューポートを設定して、page.screenshot() でスクリーンショットを保存できます。
Puppeteer でページ全体のスクリーンショットを撮れますか?
はい。page.screenshot() に fullPage: true を渡すと、表示中のビューポートだけでなく、スクロール可能なページ全体を撮影できます。
Puppeteer で複数の URL を撮影できますか?
はい。1 つのブラウザを開いたまま、URL ごとにページまたはブラウザコンテキストを作成します。件数が多い場合は同時実行数も制限してください。
スクリーンショットには Puppeteer のほうが Playwright より優れていますか?
Chrome 中心の撮影スクリプトなら、Puppeteer のほうが簡潔な場合があります。Chromium、Firefox、WebKit への対応や、より大きなテスト工程が必要なら、通常は Playwright が適しています。
Puppeteer の代わりに Shotomatic を使うべきなのはどんな場合ですか?
JavaScript スクリプトを管理せずに、誰かが URL リストの撮影、確認、書き出しを行うノーコードの手順が必要な場合は Shotomatic が適しています。
参考資料
上の例は、公式ドキュメントと照合しています。
- Puppeteer の概要、インストール、設定
- Puppeteer のスクリーンショットガイド、
Page.screenshot()、ScreenshotOptions - Puppeteer の
BrowserContext、page.setViewport()、KnownDevices、page.emulate() - Puppeteer の
page.waitForSelector()とpage.waitForFunction() - Playwright のブラウザに関するドキュメントとライブラリに関するドキュメント
関連記事
ほかの記事を見るPlaywright で Web サイトのスクリーンショットを自動化する方法
Playwright と JavaScript で Web サイトのスクリーンショットを自動化します。単一ページ、ページ全体、モバイル表示、URL の一括撮影まで、再試行を含めて解説します。

コードを書かずに Web サイトのスクリーンショットを自動化する方法
URL 一覧の撮影、スクリーンショット API と自動化サービス、監視ツール、ブラウザ補助機能から、ノーコードの Web サイト撮影方法を選びます。

MacでWebサイトのスクリーンショットを一括撮影する方法
ShotomaticにURLリストを貼り付け、デスクトップ・タブレット・モバイルのプリセットを選び、MacでWebページを並列キャプチャして監査やレポート用に書き出す方法を解説します。

オフライン学習用の電子教科書を買う前のチェックリスト
購入前に、オフライン対応デバイス、持ち運べるファイル、印刷上限、利用期間、ノート書き出し、アクセシビリティを確認するためのチェックリストです。

コードを書かずにWebサイトのスクリーンショットを自動化
Website Captureなら、URLリストをページごとの設定で撮影し、結果を確認して書き出せます。スクリプトの管理は不要です。