created: 2024-08-13T04:52:55.654Z
puppeteer で、スクレイピングでシンプルに GET で html を取得する処理
「JS や画像など、GET でとれた html 以外はいらない」という実装をした時のメモ。
全体ではだいたいこんな実装になった。
async get(url: string) {
const browser = await this.getBrowser();
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on("request", (request) => {
if (
request.isNavigationRequest() &&
request.resourceType() === "document" &&
request.frame() == page.mainFrame()
) {
return request.continue();
} else {
return request.abort();
}
});
const result = await page.goto(url, { waitUntil: "domcontentloaded" });
await page.close();
await browser.close();
return result;
}
無用なリクエストを取りやめるための実装
この部分。page.on("request")
で abort する。
page.on("request", (request) => {
if (...) {
return request.continue();
} else {
return request.abort();
}
});
余談だが、いろんなメディアで試してみると目当てのリソース以外にすごい量のリクエストが飛んでることがわかっておもしろい。
豆知識
base64 でエンコードされた src="data:..."
みたいな img
タグも page.on("request")
をトリガーする。
request.isNavigationRequest()
NavigationRequest
とは puppeteer 用語でページ遷移を発生させるリクエストのこと。
リダイレクトや、フォームの submit は isNavigationRequest=true
CSS や JS、画像を読み込むときは isNavigationRequest=false
になる
request.resourceType() === "document"
これも img タグや favicon をリクエストをしないための設定。
document
, script
などのラベルは Chrome Dev Tool と一緒。
isNavigationRequest
だけでまかなえてそうだが、isNavigationRequest
の挙動が完全にわかってるわけではないのと、雰囲気が気に入ったので条件に入れた。
request.frame() == page.mainFrame()
ページ内の iframe などから発行されているリクエストを弾くための設定。
!!request.frame()?.parentFrame()
というのも検討したけど、parentFrame
の仕様がよくわからなくて、null とか空の配列とかややこしいとイヤなので見送った。
その他
page.setRequestInterception
ちゃんと見てないがこれを設定しないとエラーとなる。
Error: Request Interception is not enabled
close
後始末。
const result = await page.goto(url, { waitUntil: "domcontentloaded" });
// puppeteer は 1 年に 1 回くらい触るけど、毎年忘れていて毎年 jest が終了しない
await page.close();
await browser.close();
await page.goto
とせずにpage.close
を呼んでしまうとエラーになる。
Error: Navigating frame was detached
page.goto(url, { waitUntil: "domcontentloaded" })
知らなかったが、goto
メソッドの戻り値は HTTPResponse
オブジェクト。
シンプルに GET の結果が欲しい場合は DCL まで待って戻り値を使う。
401
だけ page.goto
が例外を投げる
httpbin で 400
から 503
まで調べたが、ステータスコードが 401
のときだけ page.goto
メソッドが例外を投げる。401
だと Chrome は basic 認証のフォームを出すので、そのへんで特別扱いになっているのかも。
Error: net::ERR_INVALID_AUTH_CREDENTIALS
なので 401
が返されない保証がないときは try-catch をかかないといけない。