Hurry Puppeteer とのろいの子

このタイトル思いついて書いてみたのであとは蛇足みたいなものですが。

Headless Chromeを操作できるNode.jsのライブラリのPuppeteerで重いサイトを快速に使える方法がないか調べてみたがうまいこといかなかった話です。

## waitUntilの待つタイミングを調整する

```js
await page.goto(TARGET_URL, {waitUntil: 'domcontentloaded'});
```

取得完了とみなすタイミングを調整する。これが一番効果があったように思えます。
`load`だと画像を含めてページの読込みが全て完了した時点を完了としますが、`domcontentloaded`だとHTMLの解析が完了した時点で完了となる。

ただし、画像まで必要な場合や、外部リソースを取り込んで遅延で描画が行われるなど、`domcontentloaded`の時点だとダメな場合もあるので、必要に応じて設定が必要になる。

| 値 | 内容 | 
|:-----------|:------------|
| load       | ローディングの完了時点        |
| domcontentloaded     | HTMLの読み込みと解析が完了時点      |
| networkidle0       | 500ms以上ネットワーク接続が無い        |
| networkidle2       | 500ms以上ネットワーク接続が２件以下        |


* [puppeteerのoptionsのメモ、とくにwaitUntilの値をよく忘れる](https://blog.local-c.com/archives/3275)
* [Puppeteerで次ページへの遷移を待つ](https://qiita.com/hnw/items/a07e6b88d95d1656e02f)
* [PuppeteerのwaitForNavigationで正しくページ遷移を待つ](https://www.meganii.com/blog/2020/01/30/puppeteer-wait-for-navigation/)
* [Puppeteer備忘録（Nuxt.js, ElementUI対応？）](https://zenn.dev/yassh_i/articles/970760ac701986)

## Chromeの起動パラメーターを調整する。

[Run Puppeteer with Minimal Settings
](https://www.bannerbear.com/blog/ways-to-speed-up-puppeteer-screenshots/#run-puppeteer-with-minimal-settings)にあるように、Chromeの起動パラメーターを最小限の設定にすることが出来る。
`--no-sandbox`が効果がありそうだが、セキュリティ上のリスクもあるので、サイトによって使い分けが必要。　
いろいろ試してみたが実感するほど差はなかったので、やみくも付けるのではなく、都度試しながらした方が良さそう。



```js
const puppeteer = require('puppeteer');

const options = [
  '--autoplay-policy=user-gesture-required',
  '--disable-background-networking',
  '--disable-background-timer-throttling',
  '--disable-backgrounding-occluded-windows',
  '--disable-breakpad',
  '--disable-client-side-phishing-detection',
  '--disable-component-update',
  '--disable-default-apps',
  '--disable-dev-shm-usage',
  '--disable-domain-reliability',
  '--disable-extensions',
  '--disable-features=AudioServiceOutOfProcess',
  '--disable-hang-monitor',
  '--disable-ipc-flooding-protection',
  '--disable-notifications',
  '--disable-offer-store-unmasked-wallet-cards',
  '--disable-popup-blocking',
  '--disable-print-preview',
  '--disable-prompt-on-repost',
  '--disable-renderer-backgrounding',
  '--disable-setuid-sandbox',
  '--disable-speech-api',
  '--disable-sync',
  '--hide-scrollbars',
  '--ignore-gpu-blacklist',
  '--metrics-recording-only',
  '--mute-audio',
  '--no-default-browser-check',
  '--no-first-run',
  '--no-pings',
  '--no-sandbox',
  '--no-zygote',
  '--password-store=basic',
  '--use-gl=swiftshader',
  '--use-mock-keychain',
];

  
(async () => {
const startTime = performance.now();
const browser = await puppeteer.launch({
    headless: true,
    args: options
  })
  
const page = await browser.newPage();
await page.goto('https://ja.wikipedia.org/wiki/');
await browser.close();

const endTime = performance.now();
console.log(endTime - startTime); 
})();
```


* [8 Tips for Faster Puppeteer Screenshots](https://www.bannerbear.com/blog/ways-to-speed-up-puppeteer-screenshots/)
* [puppeteerで--no-sandboxを付ける理由は何なのか](https://scrapbox.io/yuta0801/puppeteer%E3%81%A7--no-sandbox%E3%82%92%E4%BB%98%E3%81%91%E3%82%8B%E7%90%86%E7%94%B1%E3%81%AF%E4%BD%95%E3%81%AA%E3%81%AE%E3%81%8B)
* [Puppeteer・起動オプション](http://www.ajisaba.net/javascript/puppeteer/launch_opts.html)
* [ ‎Google Chrome／Chromium‎ > ‎起動オプション](https://sites.google.com/site/thesubstituteformemo/Home/google-chrome-chromium/kidou-opushon)
* [Puppeteerが遅いなと感じたときの高速化Tips](https://qiita.com/markey/items/ebf2b48626b6ac61ee89)

## ユーザーデータの保存先を指定する

[Cache Resources with userDataDir](https://www.bannerbear.com/blog/ways-to-speed-up-puppeteer-screenshots/#cache-resources-with-userdatadir)
によると、userDataDirパスを指定しないと、CSSなどのキャッシュが効かないので指定するようにした方が良いとのこと。

```js
const browser = await puppeteer.launch({
  userDataDir: './my/path'
}
```

### 画像の読み込みをブロックする

画像を読み込む必要がなければ、setRequestInterceptionを使って
不要なリクエストをabortすることが出来る。
画像(image)、CSS(stylesheet)、フォント(font), スクリプト(script)などの指定や
特定のドメインの除外なども出来る。
もしくは（document）だけで絞ることも可能。
画像が多いサイトであれば数秒の短縮はできるが、画像がないと思った通りの操作が出来ないケースもあったので、条件によって使い分けが必要か。


```js

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.setRequestInterception(true)
  page.on('request', async (request) => {
    if (request.resourceType() == 'image') {
      await request.abort()
    } else {
      await request.continue()
    }
  })
  await page.setViewport({ width: 1280, height: 800 })
  await page.goto('https://www.nytimes.com/')
  await page.screenshot({ path: 'nytimes.png', fullPage: true })
  await browser.close()
})()

```

* [checkly/puppeteer-examples](https://github.com/checkly/puppeteer-examples#request-interception)
* [puppeteerでjsやcss、画像の読み込みをさせず高速化する方法
](https://qiita.com/Ancient_Scapes/items/27b21a9e23a5b215b7d4)
* [How to disable images and CSS in Puppeteer to speed up web scraping
](https://www.scrapehero.com/how-to-increase-web-scraping-speed-using-puppeteer/)
* [コピペで簡単！puppeteerのこんなとき実例８件
](https://blog.capilano-fw.com/?p=5824)

## プロセスのクローズし忘れない

エラーが発生した際に`browser.close()`をするのを忘れていたらプロセスがたまってしまうので、`browser.close()`を入れ忘れないようにする。


* [Dockerコンテナ内でpuppeteerを使うとChromeゾンビプロセスがたまる問題](https://moznion.hatenadiary.com/entry/2019/03/02/163042
)

## そもそもヘッドレスブラウザを使わない

HTMLを直接読み込んで解析した方が明らかに速いので、
場面によっては、ヘッドレスブラウザと使い分けて使用した方が良さそう。

```js
const axios = require('axios');
const { JSDOM } = require('jsdom');

(async () => {
    const res = await axios.get("https://ja.wikipedia.org/wiki/");
    const { data } = res;
    const dom = new JSDOM(data);
    const selector = '#welcome';
console.log(dom.window.document.querySelector(selector).textContent);
})();
```

### 結果

参考サイトだと効果があった書かれてあったが、自分が試したサイトだとそこまで効果を感じなかった。
一律でこれを設定すればOKみたいなものはなさそう。
該当のサイトで何の操作をしたいのか、サイトがどうやって描画されているかや（JavaScript等々）を調べてからサイト別にチューニングが必要になってきそうです。



### その他参考サイト

* [Puppeteerが遅いなと感じたときの高速化Tips
](https://qiita.com/markey/items/ebf2b48626b6ac61ee89)
* [puppeteerを使ったスクレイピング
](https://qiita.com/unhurried/items/56ea099c895fa437b56e)
* [8 Tips for Faster Puppeteer Screenshots
](https://www.bannerbear.com/blog/ways-to-speed-up-puppeteer-screenshots/#path-vs-buffer)
* [Ways to speed up recognition of the desired element
](https://stackoverflow.com/questions/62852481/how-to-speed-up-puppeteer)
* [Puppeteerの魅力は何か
](https://gihyo.jp/article/2022/09/rapid-learning-puppeteer-01)
* [Getting to Know Puppeteer Using Practical Examples
](https://nitayneeman.com/posts/getting-to-know-puppeteer-using-practical-examples/)
* [Puppeteer から Headless Chromium を動かしている時のログ
](https://mozy-ok.hatenablog.com/entry/2018/12/05/150000)
* [Puppeteer waitUntil Options](https://blog.cloudlayer.io/puppeteer-waituntil-options/)
* [puppeteerのoptionsのメモ、とくにwaitUntilの値をよく忘れる
](https://blog.local-c.com/archives/3275)
* [Promise.allによる並列処理
 PuppeteerによるヘッドレスChromeの使い方 2021年度版
](https://iwb.jp/puppeteer-headless-chrome-2021/#Promiseall)
* [Puppeteerでメモリリーク対策](https://qiita.com/nobodytolove123/items/358e8aa00278e4960768)
* [【nodejs】headless chromeで重いページを連続で開くとエラーになるときの対策](https://engineer-lady.com/program_info/nodejs-headless-chrome-err/)