はじめに
ブラウザの自動化、やってますか。
Webスクレイピングやテスト自動化、PDF生成など、ブラウザを操作したい場面は意外と多いですよね。個人的には「このサイトのデータを定期的に取得したい」とか「レポートを自動でPDF化したい」という場面で何度も助けられてきました。
そんなときに真っ先に候補に上がるのがPuppeteerです。Google製のブラウザ自動化ライブラリで、GitHubのスター数は9万超え、月間ダウンロード数は2500万回以上。これ、ブラウザ自動化の分野では圧倒的な数字なんですよね。
正直なところ、Puppeteerを使い始めてから「ブラウザ操作の自動化」に対するハードルがかなり下がりました。今回はその理由を含めて、Puppeteerの魅力を紹介していきます。
Puppeteerとは
Puppeteerは、ChromeやFirefoxを自動操作するためのNode.jsライブラリです。2017年にGoogleがリリースして以来、ブラウザ自動化の定番として広く使われています。
主な特徴は以下の通りです。
- 高レベルAPI: DevTools ProtocolやWebDriver BiDiを抽象化した使いやすいAPI
- Chrome/Firefox対応: ChromiumベースのブラウザとFirefoxに対応
- ヘッドレス実行: 画面表示なしでバックグラウンド実行が可能
- 多機能: スクレイピング、PDF生成、スクリーンショット、フォーム操作など
特に嬉しいのは、npm installするだけでChromiumが自動的にダウンロードされるという点です。環境構築の手間がほとんどありません。
特徴・メリット
1. 直感的なAPIで学習コストが低い
PuppeteerのAPIは非常に分かりやすく設計されています。
「ページを開く」「要素をクリックする」「テキストを入力する」といった操作が、そのままメソッド名になっているので、ドキュメントを見なくても何となく書けてしまう。これが地味に大きいんですよね。
await page.goto('https://example.com'); // ページに移動
await page.click('#button'); // クリック
await page.type('#input', 'テキスト'); // テキスト入力
JavaScriptをある程度書ける人なら、1時間もあれば基本的な操作はマスターできると思います。
2. ヘッドレスモードで効率的な実行
デフォルトでヘッドレスモード(画面表示なし)で動作するので、サーバーサイドでの実行に最適です。
CI/CDパイプラインでのテスト実行や、定期的なスクレイピングジョブなど、GUIが不要な場面では処理速度も速くなります。もちろんデバッグ時には画面表示ありのモードに切り替えられます。
3. スクリーンショットとPDF生成が優秀
個人的に重宝しているのがこの機能です。
await page.screenshot({ path: 'screenshot.png', fullPage: true });
await page.pdf({ path: 'document.pdf', format: 'A4' });
WebページをそのままPDFに変換できるので、レポート生成の自動化に使っています。フルページのスクリーンショットも一発で撮れる。これだけでも導入する価値があると思います。
4. ネットワークリクエストの操作
リクエストのインターセプトや改変ができます。
await page.setRequestInterception(true);
page.on('request', request => {
if (request.resourceType() === 'image') {
request.abort(); // 画像の読み込みをスキップ
} else {
request.continue();
}
});
スクレイピング時に画像をスキップして処理を高速化したり、特定のAPIレスポンスをモックしたりできます。
インストール方法
Node.jsがインストールされている環境であれば、すぐに始められます。
基本インストール
npm install puppeteer
これだけで、Chromiumも一緒にダウンロードされます。容量は300MB程度ありますが、面倒な設定は一切不要です。
ライブラリのみインストール
既存のChrome/Chromiumを使いたい場合は、軽量版をインストールできます。
npm install puppeteer-core
こちらはブラウザが付属しないので、別途ブラウザのパスを指定する必要があります。Dockerなどで軽量なイメージを作りたいときに便利ですね。
基本的な使い方
シンプルな例
まずは基本中の基本から。
const puppeteer = require('puppeteer');
(async () => {
// ブラウザを起動
const browser = await puppeteer.launch();
const page = await browser.newPage();
// ページに移動
await page.goto('https://example.com');
// タイトルを取得
const title = await page.title();
console.log('ページタイトル:', title);
// スクリーンショットを撮影
await page.screenshot({ path: 'example.png' });
// ブラウザを閉じる
await browser.close();
})();
async/awaitで書けるので、非同期処理が直感的に書けます。
要素の操作
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: false }); // 画面表示あり
const page = await browser.newPage();
await page.goto('https://example.com/login');
// フォームに入力
await page.type('#email', 'user@example.com');
await page.type('#password', 'password123');
// ボタンをクリック
await page.click('#login-button');
// ページ遷移を待つ
await page.waitForNavigation();
console.log('ログイン後のURL:', page.url());
await browser.close();
})();
要素の待機
動的なページでは、要素が表示されるのを待つ必要があります。
// セレクタで要素を待つ
await page.waitForSelector('.result-item');
// 特定のテキストを含む要素を待つ
await page.waitForFunction(
() => document.body.innerText.includes('読み込み完了')
);
// ネットワークが落ち着くまで待つ
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
データの抽出
スクレイピングでよく使うパターンです。
// 単一要素のテキスト取得
const title = await page.$eval('h1', el => el.textContent);
// 複数要素のデータ取得
const items = await page.$$eval('.item', elements =>
elements.map(el => ({
title: el.querySelector('.title').textContent,
price: el.querySelector('.price').textContent,
}))
);
console.log(items);
実践的なユースケース
Webスクレイピング
定期的にニュースサイトから記事を取得する例です。
const puppeteer = require('puppeteer');
async function scrapeNews() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// ユーザーエージェントを設定
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
await page.goto('https://news.example.com', { waitUntil: 'networkidle2' });
// 記事一覧を取得
const articles = await page.$$eval('.article', elements =>
elements.map(el => ({
title: el.querySelector('.article-title')?.textContent?.trim(),
url: el.querySelector('a')?.href,
date: el.querySelector('.article-date')?.textContent?.trim(),
}))
);
await browser.close();
return articles;
}
scrapeNews().then(articles => {
console.log(JSON.stringify(articles, null, 2));
});
PDF生成
Webページをレポートとして保存する例です。
const puppeteer = require('puppeteer');
async function generateReport(url, outputPath) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
// 印刷用CSSを適用してPDF生成
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true,
margin: {
top: '20mm',
right: '20mm',
bottom: '20mm',
left: '20mm',
},
});
await browser.close();
console.log(`PDF生成完了: ${outputPath}`);
}
generateReport('https://example.com/report', 'report.pdf');
フォーム自動入力
面倒な入力作業を自動化する例です。
const puppeteer = require('puppeteer');
async function fillForm(data) {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/form');
// テキスト入力
await page.type('#name', data.name);
await page.type('#email', data.email);
// セレクトボックス
await page.select('#category', data.category);
// チェックボックス
if (data.newsletter) {
await page.click('#newsletter');
}
// テキストエリア
await page.type('#message', data.message);
// 送信前の確認用にスクリーンショット
await page.screenshot({ path: 'form-preview.png' });
// 送信
await page.click('#submit');
await page.waitForNavigation();
console.log('フォーム送信完了');
await browser.close();
}
fillForm({
name: '山田太郎',
email: 'yamada@example.com',
category: 'inquiry',
newsletter: true,
message: 'お問い合わせ内容です。',
});
パフォーマンス計測
ページの読み込み時間を計測する例です。
const puppeteer = require('puppeteer');
async function measurePerformance(url) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// パフォーマンスメトリクスを有効化
await page.setCacheEnabled(false);
const startTime = Date.now();
await page.goto(url, { waitUntil: 'networkidle2' });
const loadTime = Date.now() - startTime;
// Core Web Vitals風のメトリクス取得
const metrics = await page.evaluate(() => {
const timing = performance.timing;
return {
domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart,
loadComplete: timing.loadEventEnd - timing.navigationStart,
};
});
console.log(`URL: ${url}`);
console.log(`読み込み時間: ${loadTime}ms`);
console.log(`DOMContentLoaded: ${metrics.domContentLoaded}ms`);
console.log(`Load Complete: ${metrics.loadComplete}ms`);
await browser.close();
}
measurePerformance('https://example.com');
まとめ
Puppeteerを使ってみて感じたのは、ブラウザ自動化がここまで手軽にできるのかという驚きです。
- インストールが簡単(npm一発)
- APIが直感的で学習コストが低い
- スクレイピング、PDF生成、テストなど用途が広い
- ヘッドレス実行でサーバーサイドでも使いやすい
正直なところ、ちょっとしたスクレイピングやPDF生成なら、Puppeteer一択ですね。もっと本格的なE2Eテストが必要なら同系統のPlaywrightも選択肢に入りますが、単純なブラウザ操作ならPuppeteerの方がシンプルで扱いやすいと思います。
公式ドキュメントも充実しているので、まずはnpm install puppeteerで触ってみてください。