最近とあるサイトのユーザーアクションを定期的に自動化したい事項がありました。
まずGolangでクローリングするに当たってagoutiというライブラリをインポートします。
以下のコードはGolangの実行環境が整っている、またChromeDriverのローカル実行に関して、Homebrewなどでインストール済みであることを前提としています。
ローカルでの場合
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つめが怪しいのですが、対応バージョンのものを何通りか試してみたのですが、それでも解決に至りませんでした。
ちなみにこのサイトの事象とほぼ同様です。
page.urlやpage.Screenshotでも同様にstatus 6の事象が発生します。
上記の事象が解決に至らず、止むを得ずPythonでseleniumを使用する方法に切り替えました。
- 作者: 松木雅幸,mattn,藤原俊一郎,中島大一,上田拓也,牧大輔,鈴木健太
- 出版社/メーカー: 技術評論社
- 発売日: 2019/08/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る impress top gearシリーズ
- 作者: Sau Sheong Chang,武舎広幸,阿部和也,上西昌弘
- 出版社/メーカー: インプレス
- 発売日: 2017/03/17
- メディア: Kindle版
- この商品を含むブログ (1件) を見る