Puppeteer(パペティア)は、ChromeとFirefoxをプログラムから操作するNode.jsのライブラリです。開発はGoogleのChromeチームで、ページを開く・クリックする・入力する・スクリーンショットやPDFを保存する、といったブラウザ操作をJavaScriptで書けます。2026年10月28日時点の最新版は、同月22日公開の25.16.0で、5月の25.0.0からはESMのみの配布となり、Node.js 22.12以上が必要です。この記事では、仕組みと読み方、インストール時にChromeが入らない問題、要素の取り方、Playwright・Seleniumとの選び方、古いコードが動かなくなる版ごとの変更を、25.16.0を手元で動かした結果をもとに整理します。
まとめ:Puppeteerの要点
- PuppeteerはChrome・FirefoxをChrome DevTools Protocol(CDP)かWebDriver BiDiで操るNode.jsライブラリ。ライセンスはApache-2.0で、商用でも無料。
- 読み方はパペティア。英語で「人形遣い」の意味。
npm i puppeteerで本体とChrome for Testingが入る。pnpmなどでinstall scriptが止まるとChromeが入らず、実行時にCould not find Chromeが出るのでnpx puppeteer browsers installを実行する。- 要素の操作は
page.locator()が推奨。表示・有効化・位置の安定を自動で待つ。 - 公式サポート対象はChromeとFirefox。WebKitでの検証にはPlaywrightを選べますが、Safari製品版そのものは操作できません。
- v25からESMのみ・Node.js 22.12以上・
executablePath()がPromiseを返す。page.waitForTimeout()はv22で削除済み。
Puppeteerの定義と仕組み
公式サイトpptr.devは、Puppeteerを「Chrome or FirefoxをDevTools ProtocolかWebDriver BiDi経由で操作する高水準APIを提供するJavaScriptライブラリ」と定義し、既定ではヘッドレス(画面を出さない状態)で動くと説明しています。実際のブラウザエンジンでページを描画してから操作するので、JavaScriptが後から作る要素も扱えます。GitHubのリポジトリ(puppeteer/puppeteer)のスター数は約9.6万(2026年10月28日時点)です。
Puppeteerの読み方と英語の意味
読み方はパペティアです。英語の puppeteer は「人形遣い」の意味で、ブラウザを人形に見立てて糸で操る、という命名です。Chrome for Developersの日本語ページでは見出しが「操り人形師」と訳されています。実験器具のピペッター(pipette)とは関係ありません。
CDPとWebDriver BiDi:2つのプロトコル
Puppeteerは既定ではブラウザとWebSocketでつながり、制御用のプロトコルで命令を送ります。使われるプロトコルはブラウザで決まります。公式のWebDriver BiDiの解説によると、Firefoxは既定でWebDriver BiDi、Chromeは既定でCDPです。ChromeがBiDiに切り替わっていないのは、CDPの機能がまだBiDiで全部使えるわけではないためです。
| 起動のしかた | browser.protocol の値(実測) |
|---|---|
launch()(Chrome) |
cdp |
launch({ browser: 'firefox' }) |
webDriverBiDi |
launch({ protocol: 'webDriverBiDi' })(Chrome) |
webDriverBiDi |
BiDiでは、エミュレーション系(page.emulate() など)、カバレッジ、トレース、page.createCDPSession() などが使えません。Firefoxで createCDPSession() を呼ぶと、UnsupportedOperation という例外が出ました(25.16.0・Firefox 158.0.1で確認)。CDPの細かい機能を使うコードは、Chromeで動かす前提で書くことになります。
Puppeteerでできること・向かない用途
- スクレイピング:SPAのように、JavaScriptの実行後にしか中身が揃わないページからデータを取る。
- E2Eテスト:ログインから操作、結果の確認までを実際のブラウザで再現する。JestやMochaなどと組み合わせる。
- スクリーンショット・PDF生成:請求書や帳票のPDF化、画面の見た目の差分確認に使う。PDFの設定とAWS Lambdaでの動かし方はPuppeteerでPDFを生成する方法で詳しく扱っています。
- ネットワークの傍受と性能計測:リクエストを止める・書き換える、パフォーマンストレースを取る。
向かないのは、静的なHTMLを取るだけの用途です。ブラウザを起動すると、ディスクに数百MBのブラウザを置いたうえでプロセスを立ち上げることになります。fetchで同じHTMLが取れるなら、それで十分です。また、PuppeteerはSafari(WebKit)を操作できません。対応ブラウザの一覧にあるのはChromeとFirefoxだけです。
Puppeteerをインストールする
前提はNode.js 22.12以上です(TypeScriptを使う場合は5.0.1以上)。Node.jsのメンテナンス中のLTSのうち最も新しい版に合わせる方針なので、Node 18や20の環境は25系の動作対象外です。
npm i puppeteer
npm i puppeteer で入るブラウザと容量
本体に加えて、そのPuppeteerの版で動作が保証されたChrome for Testingと、軽量版の chrome-headless-shell がダウンロードされます。保存先はプロジェクトの中ではなく ~/.cache/puppeteer(v19.0.0から)です。公式のインストールガイドはmacOSのダウンロード量を約170MBとしていますが、25.16.0をmacOS(x64)に入れて展開後のディスク使用量を測ると、Chrome for Testing 156.0.8078.47が391MB、chrome-headless-shellが216MBでした。node_modules自体は32MBです。DockerイメージやCIのキャッシュの容量は、この展開後のサイズで見積もってください。
pnpm・Bunで「Could not find Chrome」になる原因と直し方
ブラウザのダウンロードは、依存パッケージのinstall script(postinstall)で行われます。パッケージマネージャの種類・バージョン・設定によっては、このスクリプトが止められ、Chromeが未取得のまま起動に失敗します。pnpm 11.15.1で pnpm add [email protected] を実行すると ERR_PNPM_IGNORED_BUILDS が出てスクリプトは動きませんでした。npm 11.17.0ではスクリプトは動きましたが、allowScripts の対象外だという警告が出ます。ブラウザが無い状態で起動すると、次のエラーになります。
Could not find Chrome (ver. 156.0.8078.47). This can occur if either
1. you did not perform an installation before running the script (e.g. `npx puppeteer browsers install chrome`) or
2. your cache path is incorrectly configured (which is: ...).
直し方は2つです。インストール後に npx puppeteer browsers install を実行してブラウザを入れるか、パッケージマネージャにpuppeteerのスクリプト実行を許可します(npmなら package.json の allowScripts に "puppeteer": true を書く、pnpmなら pnpm approve-builds)。CIでは前者のコマンドをビルド手順に1行入れておくと、パッケージマネージャの設定に左右されません。
puppeteerとpuppeteer-coreの違い
| 項目 | puppeteer | puppeteer-core |
|---|---|---|
| ブラウザ | Chrome for Testingを自動で入れる | 入れない |
| 起動時の指定 | 不要 | executablePath か channel |
| 設定ファイル・環境変数 | 読む | 読まない |
| 向く場面 | 開発・学習・一般の自動化 | リモートのブラウザに接続/ブラウザを自前で管理 |
インストール済みのChromeを使うなら puppeteer-core で launch({ channel: 'chrome' }) と書けます。通常の puppeteer パッケージでは、保存先や取得するブラウザを .puppeteerrc.cjs などの設定ファイルか、PUPPETEER_CACHE_DIR・PUPPETEER_SKIP_DOWNLOAD などの環境変数で変えます(設定の解説)。ブラウザのダウンロードに関する設定を変えた場合は、npx puppeteer browsers install を実行し直して適用します。
Puppeteerの使い方:最小コードと基本操作
ページを開いてスクリーンショットとPDFを保存する
25系はESMのみの配布なので、import で書くのが基本です。拡張子を .mjs にするか、package.jsonに "type": "module" を書けば、トップレベルで await が使えます。
// shot.mjs
import puppeteer from 'puppeteer';
import { writeFile } from 'node:fs/promises';
const browser = await puppeteer.launch(); // 既定はヘッドレス
try {
const page = await browser.newPage();
await page.goto('https://example.com/');
console.log(await page.title()); // Example Domain
await page.screenshot({ path: 'example.png' });
const pdf = await page.pdf(); // 戻り値は Uint8Array
await writeFile('example.pdf', pdf);
const base64 = Buffer.from(pdf).toString('base64');
} finally {
await browser.close(); // 途中で失敗してもブラウザを閉じる
}
screenshot() と pdf() の戻り値は、v23からBufferではなくUint8Arrayです。古い記事の pdf.toString('base64') はエラーにならず、37,80,68,70,... という数字の列を返します(実測)。API Gatewayなどに返すコードでは Buffer.from() で包んでください。try〜finally で閉じておくと、goto() が失敗したときにもChromeのプロセスが残りません。なお、従来の require('puppeteer') もNode.js 26.5.0では動きました。Node.js側の機能でESMのパッケージをrequireしているためです。
要素を取る:page.$・page.$$とLocatorの使い分け
| API | 返すもの | 待つか |
|---|---|---|
page.$(sel) |
最初の1件の ElementHandle か null | 待たない |
page.$$(sel) |
全件の ElementHandle の配列 | 待たない |
page.$eval(sel, fn) |
1件に fn を当てた結果 | 待たない |
page.$$eval(sel, fn) |
全件の配列に fn を当てた結果 | 待たない |
page.waitForSelector(sel) |
現れた要素 | DOMに現れるまで |
page.locator(sel) |
操作の予約 | 操作できる状態まで |
差が出るのは、JavaScriptが後から要素を作るページです。0.8秒後にリストとボタンを描画するページで試すと、読み込み直後の page.$$eval('#list li', els => els.length) は0でした。一方 page.locator('#more').click() はボタンが現れるまで約1秒待ってからクリックし、成功しています。公式のページ操作ガイドによると、Locatorはクリックや入力の前に、要素が画面内にあること・表示されていること・有効であること・2フレーム続けて位置が動かないことを確かめます。
// 操作は Locator(自動で待つ)
await page.locator('input[name="q"]').fill('puppeteer');
await page.locator('button[type="submit"]').click();
// 取得は、待ってから $$eval でまとめて読む
await page.waitForSelector('.result');
const titles = await page.$$eval('.result h3', els => els.map(e => e.textContent));
クリックや入力はLocator、データの読み出しは waitForSelector で待ってから $$eval、と分けるとタイムアウトに悩まされにくくなります。固定時間の待機は、v22で削除された page.waitForTimeout() の代わりに new Promise(r => setTimeout(r, 1000)) と書けば済みますが、待つ時間をいくら長くしても遅い回線では外れることがあるので、条件で待つ方法を優先してください。
ヘッドレスモードを切り替える
await puppeteer.launch(); // 新ヘッドレス(v22から既定)
await puppeteer.launch({ headless: 'shell' }); // chrome-headless-shell(軽量)
await puppeteer.launch({ headless: false }); // 画面を表示(デバッグ用)
v22から既定になった新ヘッドレスは、通常のChromeと同じ実装で動きます。公式のヘッドレスモードの解説は、Chromeの全機能が要らず速度を優先する場面に 'shell' を勧めています。どちらのモードでも、User-Agentには HeadlessChrome という文字列が入りました(新ヘッドレスは HeadlessChrome/156.0.0.0、shellは HeadlessChrome/156.0.8078.47)。サイト側はこの文字列だけでヘッドレスと判定できるので、自社サイトのテストでボット対策に弾かれる場合は、許可リストやテスト環境で対応するのが筋です。
Playwright・Seleniumとの違いと選び方
| 観点 | Puppeteer | Playwright | Selenium |
|---|---|---|---|
| 開発 | Google(Chromeチーム) | Microsoft | Seleniumプロジェクト |
| ブラウザ | Chrome・Firefox | Chromium・Firefox・WebKit | 主要ブラウザ全般 |
| 言語 | JavaScript・TypeScript | JS・Python・Java・.NET | Java・Python・C#ほか |
| テストランナー | なし(Jest等と組む) | あり(@playwright/test) | なし |
| CDPの直接利用 | 可(Chrome) | 可(Chromium) | 一部 |
新しくE2Eテストを作るなら、Chromium・Firefox・WebKitで検証でき、テストランナーも付くPlaywrightを第一候補にします。テスト資産がJavaやPythonにある、あるいは社内の検証環境がSelenium Gridで組まれているならSeleniumです。Puppeteerを選ぶのは、Node.jsの小さなスクリプトでChromeを動かしたい場合と、CDPを直接使ってネットワークやトレースを細かく扱いたい場合です。PDFやスクリーンショットの生成だけなら、テストランナーの無いPuppeteerの方が依存は少なくて済みます。E2Eテストを CI/CD に組み込む段階で体制づくりに迷う場合は、DevOps・CI/CD導入支援でもご相談を受けています。コードを書かない方式も含めた全体像はBrowser Automationの4つの方式にまとめています。
v20〜v25の変更点:古いコードが動かない理由
Puppeteerはメジャー版のたびに既定の挙動が変わります。検索で見つかるコードがどの版のものかを確かめるのに、次の表を使ってください(日付はCHANGELOGの記載)。
| 版 | 主な破壊的変更 |
|---|---|
| v20.0.0(2023-05-02) | 同梱ブラウザがChromiumからChrome for Testingへ |
| v22.0.0(2024-02-05) | 新ヘッドレスが既定に/page.waitForTimeout() 削除 |
| v23.0.0(2024-08-07) | FirefoxはBiDiが既定に/戻り値がUint8Arrayへ |
| v24.0.0(2025-01-09) | CDP経由のFirefox操作を削除(FirefoxはBiDiのみ) |
| v25.0.0(2026-05-12) | ESMのみ/Node.js 22以上/executablePath() がPromiseに |
v25ではほかに、Browser.isConnected()、Puppeteer.product、マウス操作の clickCount オプションも削除されました。手元で console.log(puppeteer.executablePath()) を実行すると、パスではなく Promise { <pending> } が表示されます。await を付け忘れると、そのまま文字列として扱う処理が壊れます。isConnected() を使っていたコードは browser.connected プロパティに書き換えます。
Python・Ruby・GoでPuppeteer相当を使う方法
Puppeteer本体はNode.js専用で、公式の他言語版はありません。Python向けの非公式移植「pyppeteer」は、最後のリリースが2024年2月の2.0.0で、リポジトリのREADMEには「メンテナンスされていない、playwright-pythonを検討してほしい」と書かれています。PythonでChromeを操作するなら、公式にPython版があるPlaywrightを使います。Ruby向けの「puppeteer-ruby」は個人が保守する非公式Gemで、2026年7月に0.53.0が出ています。GoならChromeをCDPで操作するchromedpが同じ位置づけです。
AIエージェントからPuppeteerを使う:chrome-devtools-mcp
pptr.devのトップには、Puppeteerを土台にしたMCPサーバー chrome-devtools-mcp の案内があります。chrome-devtools-mcpのREADMEは、Claude CodeやCursor、Copilotなどのコーディングエージェントを利用先に挙げています。エージェントは、このサーバー経由でChromeを開き、クリックやスクリーンショット、コンソールやネットワークの確認を行います。自分でPuppeteerのコードを書かなくても、エージェントにブラウザを操作させられる形です。導入と設定はChrome DevTools MCPの導入手順と設定で解説しています。
よくある質問
Puppeteerの読み方は?
パペティアです。英語で「人形遣い」を意味する単語で、実験器具のピペッターとは別物です。
Puppeteerは無料で使えますか?
はい。Apache-2.0ライセンスのオープンソースで、商用利用も無料です。
PuppeteerでSafariを操作できますか?
できません。対応はChromeとFirefoxです。WebKitでの表示確認にはPlaywrightを使えます。ただし、Safari製品版そのものの動作確認は別途必要です。
PuppeteerはPythonで使えますか?
公式のPython版はありません。非公式のpyppeteerは保守されていないとREADMEに明記されているため、PythonならPlaywright for Pythonを選びます。
Puppeteerで動かすとボットだと分かりますか?
分かる場合があります。25.16.0では、新ヘッドレスでもUser-Agentに HeadlessChrome が入りました。アクセス先の利用規約やrobots.txtを守り、自社サイトのテストなら許可リストで通すのが確実です。