<commit_before>---
rid: 1516155
bid: chrome
rcd: cvim
srcpath: chrome/puppeteer.adoc
title: Puppeteerメモ
category: Chrome
date: 2018.10.25
layout: refill.jade
---

[.no-shadow]
image::https://s3-ap-northeast-1.amazonaws.com/syon.github.io/refills/chronicle/201810/puppeteer.png[Puppeteer,150]

link:https://github.com/GoogleChrome/puppeteer[GoogleChrome/puppeteer]::
Headless Chrome Node API


== documents

- link:https://pptr.dev/[]
- link:https://developers.google.com/web/tools/puppeteer/[Puppeteer  |  Tools for Web Developers  |  Google Developers]
- link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md[puppeteer/api.md at master · GoogleChrome/puppeteer]


== based on Headless Chrome

link:https://github.com/Kikobeats/browserless[Kikobeats/browserless]::
High level automation API for working with Headless Chrome

link:https://github.com/yujiosaka/headless-chrome-crawler[Headless Chrome Crawler]::
モダンフロントエンドに対応したヘッドレスChromeによる分散クローラー


== Puppeteerの使いどころ

- Chromeで動けば良い
- E2Eテスト用途ではない
- 自動操作が目的である（スクレイピング、キャプチャ）

コード内では *ブラウザのユーザー操作* を Puppeteer API に実行させ、
DOM の読み取りや加工は `page.evaluate()` に流す。


== tips

=== まとめ

- link:https://qiita.com/rh_taro/items/32bb6851303cbc613124[puppeteerでよく使うであろう処理の書き方 - Qiita]


=== launch options

- link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions[puppeteer/api.md at master · GoogleChrome/puppeteer]
- link:https://peter.sh/experiments/chromium-command-line-switches/[List of Chromium Command Line Switches « Peter Beverloo]

=== ERR_CERT_COMMON_NAME_INVALID

> UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: SSL Certificate error: ERR_CERT_COMMON_NAME_INVALID

```js
const browser = await puppeteer.launch({
  headless: false,
  ignoreHTTPSErrors: true
});
```

=== use proxy server with authentication

- link:https://blog.apify.com/how-to-make-headless-chrome-and-puppeteer-use-a-proxy-server-with-authentication-249a21a79212[Make headless Chrome and Puppeteer use proxy server with authentication]

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

(async() => {
  const proxyUrl = 'http://proxy.example.com:8000';
  const username = 'bob';
  const password = '<PASSWORD>';

  const browser = await puppeteer.launch({
    args: [`--proxy-server=${proxyUrl}`],
    headless: false,
  });

  const page = await browser.newPage();

  await page.authenticate({ username, password });
  await page.goto('https://www.example.com');

  await browser.close();
})();
```

=== wait / sleep

- link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitforselectororfunctionortimeout-options-args[puppeteer/api.md at master · GoogleChrome/puppeteer]

```js
// wait for selector
await page.waitFor('.foo');
// wait for 1 second
await page.waitFor(1000);
// wait for predicate
await page.waitFor(() => !!document.querySelector('.foo'));
```

=== ファイルをダウンロードして保存

- link:https://www.sambaiz.net/article/131/[Headless Chromeでファイルをダウンロードする - sambaiz-net]

ブラウザのダウンロード機能を経由して保存したファイルを得る方法。
`Page.setDownloadBehavior` がダウンロードバーに相当し、ダウンロードフォルダを指定することができる。
この際、指定するパスは `path.resolve()` を使って適切な文字列を downloadPath に割り当てるとよい。


=== POSTリクエストを送る

- link:https://github.com/GoogleChrome/puppeteer/issues/669[Send POST request to a page and take screenshot · Issue #669 · GoogleChrome/puppeteer] +
  POSTリクエストを送るには Request Interception を使う。 overrides.headers で Content-Type も指定しないと Express では受け付けてもらえないので注意。
```bash
$ curl http://www.google.com -d 'a=b&c=d'
```
```js
await page.setRequestInterceptionEnabled(true);
page.on('request', request => {
  const overrides = {};
  if (request.url === 'http://www.google.com') {
    overrides.method = 'POST';
    overrides.headers = {
      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    };
    overrides.postData = 'a=b&c=d';
  }
  request.continue(overrides);
});
await page.goto('http://www.google.com');
```


=== link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageexposefunctionname-puppeteerfunction[page.exposeFunction]

ブラウザの window オブジェクトに Node.js 関数を渡して実行させることができる。
サンプルでは crypto モジュールを require してブラウザから md5 ハッシュを生成している。
もう１つのサンプルでは fs モジュールを require して window.readfile 関数を実現している。


== Run on

- link:http://syonx.hatenablog.com/entry/2017/09/20/210004[PuppeteerをDockerやHerokuで動かすメモ - syonx]

=== Heroku

- link:https://github.com/jontewks/puppeteer-heroku-buildpack[jontewks/puppeteer-heroku-buildpack] +
  Installs dependencies needed in order to run puppeteer on heroku
- link:https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack[CoffeeAndCode/puppeteer-heroku-buildpack] +
  adds support for Chinese, Korean, and Japanese characters

=== link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-on-aws-lambda[AWS Lambda]

- link:https://github.com/Kikobeats/aws-lambda-chrome[Kikobeats/aws-lambda-chrome] +
  Chrome binary compatible with AWS Lambda.


== articles

- link:https://qiita.com/Quramy/items/26058e83e898ec2ec078[--headless時代の本命？ Chrome を Node.jsから操作するライブラリ puppeteer について - Qiita]
<commit_msg>:memo: Puppeteer<commit_after>---
rid: 1516155
bid: chrome
rcd: cvim
srcpath: chrome/puppeteer.adoc
title: Puppeteerメモ
category: Chrome
date: 2018.10.25
layout: refill.jade
---

[.no-shadow]
image::https://s3-ap-northeast-1.amazonaws.com/syon.github.io/refills/chronicle/201810/puppeteer.png[Puppeteer,150]

link:https://github.com/GoogleChrome/puppeteer[GoogleChrome/puppeteer]::
Headless Chrome Node API


== documents

- link:https://pptr.dev/[]
- link:https://developers.google.com/web/tools/puppeteer/[Puppeteer  |  Tools for Web Developers  |  Google Developers]
- link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md[puppeteer/api.md at master · GoogleChrome/puppeteer]


== based on Headless Chrome

link:https://github.com/Kikobeats/browserless[Kikobeats/browserless]::
High level automation API for working with Headless Chrome

link:https://github.com/yujiosaka/headless-chrome-crawler[Headless Chrome Crawler]::
モダンフロントエンドに対応したヘッドレスChromeによる分散クローラー


== Puppeteerの使いどころ

- Chromeで動けば良い
- E2Eテスト用途ではない
- 自動操作が目的である（スクレイピング、キャプチャ）

コード内では *ブラウザの UI 操作* を Puppeteer API に実行させ、
DOM の読み取りや加工は `page.evaluate()` に流す。
Puppeteer には link:https://github.com/cheeriojs/cheerio[cheerio] のような
API はないので、渡した先の Chromium 実行環境で


== tips

=== まとめ

- link:https://qiita.com/rh_taro/items/32bb6851303cbc613124[puppeteerでよく使うであろう処理の書き方 - Qiita]


=== launch options

- link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions[puppeteer/api.md at master · GoogleChrome/puppeteer]
- link:https://peter.sh/experiments/chromium-command-line-switches/[List of Chromium Command Line Switches « Peter Beverloo]

=== ERR_CERT_COMMON_NAME_INVALID

> UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: SSL Certificate error: ERR_CERT_COMMON_NAME_INVALID

```js
const browser = await puppeteer.launch({
  headless: false,
  ignoreHTTPSErrors: true
});
```

=== use proxy server with authentication

- link:https://blog.apify.com/how-to-make-headless-chrome-and-puppeteer-use-a-proxy-server-with-authentication-249a21a79212[Make headless Chrome and Puppeteer use proxy server with authentication]

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

(async() => {
  const proxyUrl = 'http://proxy.example.com:8000';
  const username = 'bob';
  const password = '<PASSWORD>';

  const browser = await puppeteer.launch({
    args: [`--proxy-server=${proxyUrl}`],
    headless: false,
  });

  const page = await browser.newPage();

  await page.authenticate({ username, password });
  await page.goto('https://www.example.com');

  await browser.close();
})();
```

=== wait / sleep

- link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitforselectororfunctionortimeout-options-args[puppeteer/api.md at master · GoogleChrome/puppeteer]

```js
// wait for selector
await page.waitFor('.foo');
// wait for 1 second
await page.waitFor(1000);
// wait for predicate
await page.waitFor(() => !!document.querySelector('.foo'));
```

=== ファイルをダウンロードして保存

- link:https://www.sambaiz.net/article/131/[Headless Chromeでファイルをダウンロードする - sambaiz-net]

ブラウザのダウンロード機能を経由して保存したファイルを得る方法。
`Page.setDownloadBehavior` がダウンロードバーに相当し、ダウンロードフォルダを指定することができる。
この際、指定するパスは `path.resolve()` を使って適切な文字列を downloadPath に割り当てるとよい。

ブラウザのUI操作でダウンロードバーに落ちてくるケースであれば上記で対応できるが、
クリックしただけではページが開いてしまうようなケースでは他の様々なアプローチが存在する。

- link:https://github.com/GoogleChrome/puppeteer/issues/299[Question: How do I get puppeteer to download a file? · Issue #299 · GoogleChrome/puppeteer]
- link:https://github.com/GoogleChrome/puppeteer/issues/610#issuecomment-340160025[Response is Null when Navigating to a PDF · Issue #610 · GoogleChrome/puppeteer]
- 静的ファイルならば URL だけ割り出して Node.js に戻して curl のように処理するのも手
  * link:https://stackoverflow.com/questions/11944932/how-to-download-a-file-with-node-js-without-using-third-party-libraries[javascript - How to download a file with Node.js (without using third-party libraries)? - Stack Overflow]


=== POSTリクエストを送る

- link:https://github.com/GoogleChrome/puppeteer/issues/669[Send POST request to a page and take screenshot · Issue #669 · GoogleChrome/puppeteer] +
  POSTリクエストを送るには Request Interception を使う。 overrides.headers で Content-Type も指定しないと Express では受け付けてもらえないので注意。
```bash
$ curl http://www.google.com -d 'a=b&c=d'
```
```js
await page.setRequestInterceptionEnabled(true);
page.on('request', request => {
  const overrides = {};
  if (request.url === 'http://www.google.com') {
    overrides.method = 'POST';
    overrides.headers = {
      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    };
    overrides.postData = 'a=b&c=d';
  }
  request.continue(overrides);
});
await page.goto('http://www.google.com');
```


=== link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageexposefunctionname-puppeteerfunction[page.exposeFunction]

ブラウザの window オブジェクトに Node.js 関数を渡して実行させることができる。
サンプルでは crypto モジュールを require してブラウザから md5 ハッシュを生成している。
もう１つのサンプルでは fs モジュールを require して window.readfile 関数を実現している。


== Run on

- link:http://syonx.hatenablog.com/entry/2017/09/20/210004[PuppeteerをDockerやHerokuで動かすメモ - syonx]

=== Heroku

- link:https://github.com/jontewks/puppeteer-heroku-buildpack[jontewks/puppeteer-heroku-buildpack] +
  Installs dependencies needed in order to run puppeteer on heroku
- link:https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack[CoffeeAndCode/puppeteer-heroku-buildpack] +
  adds support for Chinese, Korean, and Japanese characters

=== link:https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-on-aws-lambda[AWS Lambda]

- link:https://github.com/Kikobeats/aws-lambda-chrome[Kikobeats/aws-lambda-chrome] +
  Chrome binary compatible with AWS Lambda.


== articles

- link:https://qiita.com/Quramy/items/26058e83e898ec2ec078[--headless時代の本命？ Chrome を Node.jsから操作するライブラリ puppeteer について - Qiita]

{'fraction_non_alphanumeric': 0.13603341808935707, 'fraction_numerical': 0.02115873592444606, 'mean_word_length': 5.190556492411467, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 46, 'lorem ipsum': 0, 'www.': 12, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4420178', 'n_tokens_mistral': 4792, 'n_tokens_neox': 4508, 'n_words': 684}