怠慢プログラマーの備忘録

怠慢でナマケモノなプログラマーの備忘録です。

【Golang】AWSLambda上でchromedriver+headless-chromeでクローリングできない[未解決]

最近とあるサイトのユーザーアクションを定期的に自動化したい事項がありました。

まずGolangでクローリングするに当たってagoutiというライブラリをインポートします。

以下のコードはGolangの実行環境が整っている、またChromeDriverのローカル実行に関して、Homebrewなどでインストール済みであることを前提としています。

github.com

godoc.org

ローカルでの場合

     opts := []agouti.Option{
        agouti.Browser("chrome"),
        agouti.ChromeOptions(
            "args", []string{
                //"--headless",
                //"--no-sandbox",
                //"--disable-gpu",
                //"--single-process",
            },
        ),
    }

    driver := agouti.ChromeDriver(opts...)

    if err := driver.Start(); err != nil {
        log.Fatalf("Failed to start driver:%v", err)
    }

これでchromedriverが起動準備が完了します。

次にNewPage()で実際にchromedriverを起動します。 headlessにしていない場合はローカルの場合実際にChromeが立ち上がります。

 page, err := driver.NewPage()
    if err != nil {
        log.Fatalf("Failed to open page:%v", err)
    }

そしてこれから操作したいページのUrlをNavigate()に渡すとchrome上に表示が確認できます。

 err := page.Navigate(loginUrl)
    if err != nil {
        log.Fatalf("Failed to navigate:%v", err)
    }

通常ローカル実行の場合上記のようなプログラムで問題がないかと思います。

AWSLambda上での場合

AWSLambda上で事項する際には以下のことがローカルの場合と異なってきます。

  • PCにインストールしたchromedriver($ brew install chromedriver)は使用できないので、chromedriver自体をAWSLambdaのデプロイパッケージに含める必要がある
  • 同時にデプロイパッケージ内のchromedriverを使用するようにコードの修正が必要
  • ローカルではPCにインストールされたchromeブラウザが立ち上がったが、AWSLambda上には当然ないので、それに相当するserverless-chromiumのheadless-chromeをデプロイパッケージに含める必要がある。
  • 同時にデプロイパッケージ内のheadless-chromeを使用するようにコードの修正が必要
  • chromedriverとheadless-chromeのバージョンを対応させる必要がある(大切)

chromedriver

以下のように取得します。 バージョンは少々古いですが、serverless-chromium自体のバージョンとの試行錯誤していた結果、参考していたサイトの中で1番近いケースのものに一旦しています。

$ curl -SL https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip > chromedriver.zip
$ unzip chromedriver.zip
$ rm chromedriver.zip

serverless-chromium

$ curl -SL https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-37/stable-headless-chromium-amazonlinux-2017-03.zip > headless-chromium.zip
$ unzip headless-chromium.zip 
$ rm headless-chromium.zip

対象コードの変更

変更箇所は以下の2点です。

※今回はデプロイパッケージ内に含めて最終的にS3からLambdaを動かしていますが、headless-chromeのサイズを考慮するとS3に置いておくかLambda Layerを使うのが正しいかと思います。

chromeoptionsのbinaryにheadless-chrome追加

  opts := []agouti.Option{
        agouti.Browser("chrome"),
        agouti.ChromeOptions(
            "args", []string{
                //"--headless",
                //"--no-sandbox",
                //"--disable-gpu",
                //"--single-process",
            },
        ),
        agouti.Debug,
        agouti.ChromeOptions(
            "binary", "./headless-chromium",
        ),
    }

driverをパッケージ内のchromedriverを使用するように

driver := agouti.NewWebDriver("http://{{.Address}}", []string{"./chromedriver", "--port={{.Port}}"}, opts...)

問題発生箇所

いざ実行してみると、NewPage()の箇所でタイムアウトになるもしくは、DOMを操作しようとした際にstatus 6のinvalid sessionが発生します。 試したこととしては、

  • chromedriver及びserverless-chromium(headless-chrome)のバージョンを変更したものをバイナリに含める
  • Driverの起動前にネットワーク上の遅延が発生していることを考慮してtime.sleepを入れてみる -> driver.urlでは{{.Address}}の結果が取れるので問題はなさそう
  • disable-renderer-backgroundingあたりのchromeoptionsを追加してみた
  • windowsizeをchromeoptionsに追加し画面サイズを縮小してみた(原因不明だがそれでなおったとの記事を見つけたので)

上記の4つ程のうちやはり1つめが怪しいのですが、対応バージョンのものを何通りか試してみたのですが、それでも解決に至りませんでした。

ちなみにこのサイトの事象とほぼ同様です。

stackoverflow.com

page.urlやpage.Screenshotでも同様にstatus 6の事象が発生します。

上記の事象が解決に至らず、止むを得ずPythonseleniumを使用する方法に切り替えました。

yutaabe200.hatenablog.com

改訂2版 みんなのGo言語

改訂2版 みんなのGo言語