Puppeteerは、Googleが開発したヘッドレスでブラウザ(Chrome または Chromium)を操作できるNode.jsのライブラリです
ノンヘッドレス(UIあり)でも動作可能です
ブラウザは「DevTools Protocol」経由で操作します
- ページのスクリーンショットやPDFの作成
- フォーム送信やキーボード入力などの自動化
- Webスクレイピングなど
インストール
Puppeteerをインストール(npm i puppeteer)した場合、最新バージョンのChromiumも一緒にダウンロードされるのでファイルサイズ容量が大きくなります
インストール時のChromiumはOSに依存しているので、開発時と違うOSではPuppeteerの再インストールが必要です
npm i puppeteer
ブラウザーを起動するときに渡すことができる一般的なオプション
headless:ノンヘッドレスブラウザにする場合true
slowMo:指定されたミリ秒単位でPuppeteer操作を遅くします(デバッグのためなど)
devTools : パネルを自動的に開くかどうか(ヘッドレスではfalse)
const browser = await puppeteer.launch(
{
headless: true,
slowMo: 500,
devTools: true
}
)
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch( {
headless: false,
slowMo: 500,
});
const page = await browser.newPage();
await page.goto('https://example.com/');
//3秒まつ
await new Promise((r) => setTimeout(r, 3000))
await browser.close();
})();
「puppeteer-core」はChromiumのダウンロードしません
既にPCにあるChromeまたはChromiumを使用する場合
npm i puppeteer-core
const browser = await puppeteer.launch({
executablePath : 'インストール済みのChromeやChromiumのパス'
});
待ち時間
//指定ミリ秒待つ
await new Promise((r) => setTimeout(r, milliseconds))
//特定の要素が存在するようになるまで待つ
await page.waitForSelector(セレクタ);
// 特定の要素が表示されるまで待つ
await page.waitForSelector(セレクタ, {visible: true})
//新しいURLを開いた後、リロード後ページが読み込まれるのを待つ
await page.waitForNavigation()
タイムアウトのデフォルトは30秒です
上書きすることができて、0にするとタイムアウトは無しになります
page.setDefaultTimeout(タイムアウトミリ秒単位の最大時間)
page.setDefaultNavigationTimeout(タイムアウトミリ秒単位の最大時間)
スクリーンショット・PDFを取得する
https://example.comの
スクリーンショットを「example.png 」
PDFを「example.pdf」でファイルと同じ階層に保存します
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/');
await page.screenshot({ path: 'example.png' });
await page.pdf({ path: 'example.pdf', format: 'a4' })
await browser.close();
})();
デフォルトで800×600pxですが、viewportの設定(setViewport)で変更できます
また、page.screenshot()
のオプションに、fullPage:true
を指定すると全体のスクリーンショットを取得できます
await page.setViewport({
width: 640,
height: 480
});
自動入力
type(セレクタ,テキスト,options)
:inputタグにテキストを入力するselect(セレクタ, optionのテキスト)
:セレクトボックスの選択click(セレクタ,options)
:クリックする*clickCountのデフォルトは1
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch({
headless: false,
slowMo: 50,
});
const page = await browser.newPage();
await page.goto("https://devexpress.github.io/testcafe/example/");
await page.type("#developer-name", "花子", { delay: 100 });
await page.select('#preferred-interface', 'JavaScript API');
await page.click("#tried-test-cafe", { clickCount: 1 });
await browser.close();
})();
キーボードクラス
await page.keyboard.press('Enter');
await page.keyboard.type('Hello World!');
await page.keyboard.down('Shift');
await page.keyboard.up('Shift');
クリックでページ遷移した場合など全て終わったてから次に進める場合
Promise.allを使います
await Promise.all([
page.waitForNavigation(), // ナビゲーションを待つ
page.click('#nextBtn') // クリック
])
情報の取得
タイトルを取得する:page.title()
URLを取得する:page.url()
テキストを取得する場合などpage.$eval()
はdocument.querySelectorを実行して、結果を第二引数の関数の引数に渡しますpage.$$eval()
はdocument.querySelectorAllを実行して、結果を関数の引数に渡します
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com/");
const title = await page.title();
const url = await page.url();
const text = await page.$eval("h1", (el) => el.textContent);
const pList = await page.$$eval("p", (arr) => arr.map((el)=> el.textContent));
console.log(title); //Example Domain
console.log(url); //https://example.com/
console.log(text); //Example Domain
console.log(pList); //['This domain is...', ''More information...']
await browser.close();
})();
page.$()
:document.querySelectorを実行
セレクターに一致する要素がない場合の戻り値nullpage.$$()
:document.querySelectorAllを実行
セレクターに一致する要素がない場合の戻り値[]
*取得したDom要素をブラウザ(Dom API)と同じように操作できないのでpage.evaluate()
を使います
関数をブラウザに送りその結果をreturnで受け取りたり、下記コードのように第二引数にDOM要素(ElementHandle)を指定して関数の引数にすることができます
import puppeteer from 'puppeteer';
import fs from 'fs/promises';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com/");
const bodyHandle = await page.$("body");
const html = await page.evaluate((body) => body.innerHTML, bodyHandle);
//html.txtファイルに出力
await fs.writeFile('html.txt', html, 'utf8');
await browser.close();
})();
page.evaluate()
で関数を直接ブラウザに送る場合
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com/");
const html = await page.evaluate(() => document.querySelector("body").innerHTML)
await fs.writeFile("html.txt", html, "utf8");
await browser.close();
})();
imgタグのsrc属性を取得して、その画像URLから画像ファイルをダウンロードします
画像ファイルはバイナリーデータです
import puppeteer from 'puppeteer';
import fs from 'fs/promises';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://next-dogs.vercel.app/list');
const arrayImage = await page.$$eval('img', arr => {
return arr.map(el => el.src);
});
// 最初の画像のURLに移動
const imageUrl = arrayImage[0];
const resource = await page.goto(imageUrl);
// 末尾を取得してファイル名に
const fileName = imageUrl.split('/').pop();
// バッファ取得(バイナリーデータ)
const buffer = await resource.buffer();
await fs.writeFile( fileName, buffer);
await browser.close();
})();