AWS Lambda上でpuppeteerを動かして、スクレイピングする

## はじめに
AWS Lambda上のヘッドレスブラウザで動かしたいものがあったので、それの備忘録も兼ねて書いておきます。ちなみに所属会社で似たようなことをしているのですが、PhatomJS（オワコンなのは知ってる）をAWS Lambda上で動かすために、わざわざカスタマイズしてからそれ専用にビルドするという変態技が使われている部分を書き換えたいという想いからの実験でもあります。

そこで本記事ではpuppeteer（パペティア？読み方あってる？）を使って、AWS Lambda上でヘッドレスブラウザを動かすということをしてみます。

サンプルとは少し異なりますが、実際にやってみたソースはこちらです。
https://github.com/chimame/search_ranking_checker_on_aws_lambda

## 前提条件

本記事は以下のライブラリ等を使用しております。
Nodejs: 8.10
puppeteer: 1.10.0

## AWS Lambda上でのヘッドレスブラウザ

みんな大好きサーバーレスのAWS Lambdaでヘッドレスブラウザを動かしてごにょごにょします。

### プログラム

とりあえず四の五の言わずプログラムを貼る

```javascript:index.js
const launchChrome = require('@serverless-chrome/lambda')
const CDP = require('chrome-remote-interface')
const puppeteer = require('puppeteer')

exports.handler = async (event, context, callback) => {
  try {
    // chromeを起動して、puppeteerから操作できるようにする
    const slsChrome = await launchChrome()
    const browser = await puppeteer.connect({ 
      browserWSEndpoint: (await CDP.Version()).webSocketDebuggerUrl 
    })
    const context = browser.defaultBrowserContext()
    const page = await context.newPage()

    // ブラウザ操作（Googleでの検索結果）
    await page.goto(`https://www.google.co.jp/search?q=${event.searchWord}`, { waitUntil: 'domcontentloaded' })

    const searchResults = await page.evaluate(() => {
      const ret = []
      const nodeList = document.querySelectorAll("div#search h3")

      nodeList.forEach(node => {
        ret.push(node.innerText)
      })

      return ret
    })

    return callback(null, JSON.stringify({ searchResults: searchResults }))
  } catch (err) {
    return callback(err)
  }
}
```

### 解説

特に難しいことはしてないが、大事なのは次の3つ。
この3つを守れば上記のプログラムでスクレイピングしているとこをお好きなように弄っても動くはずです。

#### 必要なのはパッケージは3つでpuppeteerのバージョンには注意
まずプログラムを見てわかるとおり、`@serverless-chrome/lambda`と`chrome-remote-interface`と`puppeteer`の3つを使用している。~~その中でもpuppeteerは現在`1.5.0`がリリースされているがそのバージョンでは動かないので、`1.4.0`で動作させる必要がある。
（逆に動いたよっていう方いらっしゃったら教えてください）~~
1.10.0で動くことを確認したので、修正しています。

#### puppeteerのchromiumは使用しない
普通なら使用してもいいのだが、今回はAWS Lambdaで動かすという条件がある。**AWS Lambdaで登録できるプログラムサイズは50Mバイト以内**という条件があるため、それをクリアするために`puppeteer`のchromiumを使用（ダウンロード）してはならない。
そこで`puppeteer`のchromiumを使用しないようにするために`.npmrc`を作成し、以下の記述をしておく。

```dot:.npmrc
puppeteer_skip_chromium_download=true
```

これで使用されなくのでOKです。（もちろん環境変数にセットするという手もあるが、誰でも同じ環境になるようにするにはファイルで書いておいた方が吉）

https://github.com/GoogleChrome/puppeteer/issues/2270

#### 日本語表示
今回はやってないが、参考記事通りにWebフォントいれたら表示されると思う。
この辺も含めて冒頭で触れたPhatomJSをカスタムビルドしている理由の1つ。

私自身で日本語フォントの適用をやってみたが、**UserAgentが設定されていないとフォントが反映されなかったので、そこは注意してほしい。**

```javascript
await page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36')

// If User Agent is not set, the font will not be reflected, so User Agent must be set
await page.evaluate(() => {
    var style = document.createElement('style')
    style.textContent = `
        @import url('//fonts.googleapis.com/css?family=Source+Code+Pro');
        @import url('//fonts.googleapis.com/earlyaccess/notosansjp.css');
        div, input, a{ font-family: 'Noto Sans JP', sans-serif !important; };`
    document.head.appendChild(style)
})
await page.waitFor(1000) // Wait until the font is reflected
```


## 参考記事
本記事はこちらを参考にさせて頂き書いております。

https://www.pressmantech.com/tech/serverless/4417