ローカルでのスクレイピング作業をGCPに持っていく作戦(その１)

#やりたいこと
現状ローカルPCにて
①PythonにてSeleniumを使用してWebサイトにスクレイピング
②スクレイピング結果をログファイルに蓄積
③CRONで定時実行
している

![クリックツールGCP移行.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485045/7012a758-2c63-8e3c-fa46-c8bd5dcb0106.png)
これをクラウドであるGCPの環境に構築したい
![クリックツールGCP移行 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485045/68d483bc-1eb7-5aea-b3b7-aac4f1bda041.png)
おそらく
Pythonの実行環境は**Google Cloud Functions(GCF)**
ログファイルの蓄積には**Google Cloud Storage**もしくは**Google Drive**
定時実行は**Google Cloud scheduler**
これらに置き換えられるはず！
#背景
3つ。
①**GCPを使ってみたかった。**AWSでも良かったけど、なんとなく。
②留学に行くつもりなので、**PCをつけっぱにしたくなかった。**僕が住む街は夏は~~クソほど~~暑いので、そんな中PCを稼働させたら負担が凄そうだと考えた。雷もメチャ多いので停電も心配。あとはクラウド移行しちゃえばどこにいても管理やメンテがしやすそうだしお寿司。
③**電気代が気になった。**僕が使う[iMacの電気代は1日で60円ほど](https://web.waytoearnmoney.org/2015/03/03/imac%E3%82%B9%E3%83%AA%E3%83%BC%E3%83%97%E6%99%82%E3%81%AE%E5%BE%85%E6%A9%9F%E9%9B%BB%E5%8A%9B%E3%81%A8%E9%9B%BB%E6%B0%97%E4%BB%A3%E3%81%AF%E3%81%A9%E3%82%8C%E3%81%8F%E3%82%89%E3%81%84%EF%BC%9F/)(らしい)。1ヶ月で1800円だ。対して[GCFは無料枠がある。](https://cloud.google.com/functions/pricing?hl=ja)スクレイピングは5分に１回。おそらく余裕で収まる。つまりタダだ。

#献立
必要な作業は主に以下の3つ
①プログラムのGCF移行
②定時実行の設定
③ログの格納
で、今回は「プログラムのGCF移行」まで

####GCFについて
数あるGoogleCloudのサービスにおいて、今回Cloud Functions以外には
・Compute Engineでクラウド環境にPCを立ち上げる
・Cloud Runでコンテナに全部ぶち込んで実行する
を検討した
なぜCloud Functionsを採用したかというと、
Compute Engineは5分に１回実行させるためにずっとインスタンス立ち上げでお金がかかるし、
Cloud Runはコンテナ作るほどか？と思ったのと、Cloud Runの用途がそもそももっと完成されたアプリとかを一時的に動かすときに使うという理解なので、ちと違うかな、と
誤解してたら教えてエロい人

#プログラムのGCF移行
##GCPの登録、GCFの初期設定
腐る程記事があるのでググって！
##seleniumなどの準備からデプロイ、テストまでの流れ
[こちらのサイトがめたんこ参考になった。](https://blowup-bbs.com/gcp-cloud-functions-python3/)**多謝**
まずは画面右上にあるボタン群の一番左のボタンからCloud Shellを起動。
<img width="226" alt="スクリーンショット 2020-05-16 0.34.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485045/f99b1d12-5b94-79a8-c19b-89e8a0ab9cbd.png">
起動できたら以下のコマンドを実行。

```bash
#webdriverなど便利ツールをまとめてくれてる神Gitからクローン
git clone https://github.com/ryfeus/gcf-packs.git
#移動
cd gcf-packs/selenium_chrome/source
#解凍
unzip headless-chromium.zip
#とりあえずデプロイ(Wikiにランダムにアクセスし、ページタイトルを取ってくるプログラム)
gcloud functions deploy handler --runtime python37 --trigger-http --region asia-northeast1 --memory 512MB
```

[デプロイオプションはこちらから](https://cloud.google.com/sdk/gcloud/reference/functions/deploy?hl=ja)
途中

```
Allow unauthenticated invocations of new function [handler]? (y/N)?
```

と表示されたら「y」を入力。
トリガーをhttp、つまりデプロイ後に発行されるURLがわかれば知らん人も実行できてしまう、てことだけど、他人が実行したところでその人に利益は(僕のプログラムの場合は)全くないので、特に問題ないはず(その人が悪意を持って1億回とか実行したら利用料がすごいことになって僕が死ねるけど)。

そうすると画面上はこのようにhandlerという名前で表示される。
<img width="1171" alt="スクリーンショット 2020-05-16 0.46.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485045/fb26406a-a99d-6ffc-21d9-12ae09dca307.png">
Cloud Shellに戻って

```
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
entryPoint: handler
httpsTrigger:
  url: https://asia-northeast1-************.cloudfunctions.net/handler
ingressSettings: ALLOW_ALL
labels:
```
のhttps〜部分をコピーして

```
curl https://asia-northeast1-************.cloudfunctions.net/handler
```
を実行するとWIkiのどっかのページのタイトルが帰ってくる。
また、コンソール以外だと画面の「handler」をクリックして遷移先の「テスト」をクリックして「関数をテストする」でも同じことができる。
元になったコードは同じディレクトリの「main.py」となっている。
また、chromedriverやheadless-chromium以外のツールを使っている場合は自分で持ってくる必要がある(pythonでimportでどうにかなるやつは大丈夫、、、なはず)。

あとは「main.py」の中身を自分がローカルで使っていたコードに書き換えるだけ。
コード書く際はCloud Shellを立ち上げた画面にある「エディタを開く」を使うと便利。
<img width="162" alt="スクリーンショット 2020-05-16 1.12.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485045/ba2371f8-6f12-48c6-5c02-c12da3a69749.png">
##コードを書き換えたら再び

```
gcloud functions deploy *** --runtime python37 --trigger-http --region asia-northeast1 --memory 512MB
```
すること。deployの後の***はmain.pyの中の関数名と一致していないとエラーになるので注意。
テストしてみて問題なければ完成！！お疲れ様でした。

#注意点

##メモリサイズについて
chromeは案外メモリを食う。
デプロイした関数名をクリックすると詳細画面に遷移する。<img width="427" alt="スクリーンショット 2020-05-18 16.38.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485045/4cb31816-1025-1176-88a0-d30048a48df6.png">
「全般」のプルダウンからメモリ使用量が確認できる。挙動がおかしい際はメモリサイズを変更する。


##書き換える際に注意したいのが

```
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--window-size=1280x1696')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--hide-scrollbars')
    chrome_options.add_argument('--enable-logging')
    chrome_options.add_argument('--log-level=0')
    chrome_options.add_argument('--v=99')
    chrome_options.add_argument('--single-process')
    chrome_options.add_argument('--ignore-certificate-errors')
```
これらは消さないこと。動かなくなる。
ただ、webdriverの公式とか見てもどの引数がどんな意味を持ってて、というのが載ってなかったので適切なページなどご存知の方いたらご教示いただけますと幸いです。




#あとがき
テスト中に画像のようなエラーが出るけどログはきちんと最後まで実行されて出力されているのが謎。うーーん。。。
<img width="625" alt="スクリーンショット 2020-05-16 1.23.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485045/f85f1c43-5ef1-34c5-5458-6a1ecb1001a2.png">

#追記
上記エラーの理由がわかりました！！
スクレイピング時にリンクを開くのに新しくタブを開くように

```
key_down(Keys.CONTROL).click().key_up(Keys.CONTROL)
```
[とすべきところ](https://www.seleniumqref.com/api/python/element_set/Python_special_send_keys.html)をローカル環境がMacだったのでKeys.**COMMAND**としていました。
[GCFはPythonの実行環境はUbuntu](https://cloud.google.com/functions/docs/concepts/exec?hl=ja)だそうです。

#次回
次回、~~城之内死す、デュエルスタンバイ！~~
定時実行の設定です、お楽しみに！