Cloud SchedulerとCloud Functionsを使って自動スクレイピングさせて、スプレッドシートに書き出してみた(Python)

ゴール(作成するプロダクト)

「データ領域の求人数がこれからどれくらい上がってくるのかみたい」

実際に可視化してみましょう。ダッシュボードはDataPortalを使いましたが、今回の話では割愛。

今回は月1で求人まとめサイトを見に行って、「データエンジニア」「データアナリスト」「データサイエンティスト」という職種の求人数をとってくるスクリプトを作成しました。

また、クラウド上でcron設定して完全自動化処理もしてみました。後述しますが、ローカルでのcron設定だとパソコンを常時起動しておかないと行けないので面倒ですし、挙動も不安定ですからね、、、。

構築にはGCPの「Cloud Scheduler」と「Cloud Functions」を使用しました!選択理由はまじで安いから。というか月1処理だったらお金かからないから(笑)

対象読者

この記事の対象読者は以下のような方です。

  • 初級~中級のエンジニアの方
  • GCPとはこういうものと理解している方
  • スクレイピングやクローリングについて理解している方

上記のことを知っている方を対象とします。「GCPとはなんぞや」「スクレイピングってなに」という説明は省きます。

また、私が独自で作成したスクレイピングのPythonコードは非公開とします。自分の作成したコードを反映して構築してみてください。

構築の料金について

先にこの記事で構築したGCPの料金について、お伝えしておきます。

料金は0円です。

今回の処理は月に1回対象のサイトを読みに行く処理なので、Cloud Functionは無料枠で処理できてしまいます。

※ただ、テストの際にたくさん流しまくったらそのときに数円ぐらい料金が掛かる可能性はあります。

事前説明編

今回使用するGCPツールについて、簡単に説明します。

Google Cloud Shellについて

Google Cloud ShellはGCP上のブラウザだけで開発を行える機能です。普段ローカルで開発環境を整えたりしていると思いますが、開発環境を整えるのって結構大変ですよね。

GCPであれば、Cloud Shellから開発することができます。

詳細:https://www.topgate.co.jp/gcp09-how-to-use-cloud-shell

Cloud Functionsについて

完全にサーバーなしで、アプリケーションを実行できる超絶便利なサービスです。データ処理でもたまに使ったりします。

詳細:https://cloud.google.com/functions/docs/concepts/overview?hl=ja

Cloud Scheduler

GCPでCRONの設定ができるサービスです。このサービスのおかげで定期実行ができるようになります。

詳細:https://cloud.google.com/scheduler/docs/?hl=JA

ローカル環境とクラウド環境でのスクリプト自動実行設定の違いについて

よく「こういうのを自動で処理できるようにしたいな」というのをよく依頼されてスクリプト化していたのですが、今までスクリプトを完全に自動処理させるには、「サーバー」上にそのスクリプトを置いて、cron設定をするというのが通常でした。サーバーは常時稼働しているので、スクリプトの自動実行ができるようになります。ただ、サーバーってデータ領域のエンジニアはあまりさわれない気がしていて、自動化したいからcron設定させてと開発側に依頼するのもちょっと気が引けます。私だけかもしれませんが、、。

サーバー設定の場合

ローカルでも実装はできるのですが、PCを常時起動させておく必要があります。スリープでも動かない時があります。私の経験上、挙動が不安定すぎて使えなかったです。なのであまりローカルで実装している人はいないような気がします。

ローカル設定の場合

GCP環境の場合

そこで、GCP環境でまるっと構築してしまおうということです。GCPには前述した「Google Cloud Shell」での開発環境が用意されており、エディア機能もついています。ブラウザでの処理ですべて完結できるので、非常に便利です!

構築の流れ

  1. Google Cloud Shellを開発環境にして、Pythonのスクリプトを作成
  2. Cloud FunctionsにCloud Shellからデプロイする
  3. Cloud Schedulerでcron設定をする

という流れで進めていきます!

本当に便利な時代になりましたね。。。

構築編

それでは構築していきましょう。

GCPの登録とプロジェクトの作成

GCPの登録とプロジェクトの作成はされているかと思いますが、もしされていない方は以下のサイトを参照してください。

https://cloud-ace.jp/column/detail148/

Cloud Shell

Cloud Shellで開発環境を整えていきます。

環境構築が難しそうという方は多いと思いますが、実はすでにgitにまとめてくれている方がいて、クローンするだけで済みます!安心して進みましょう。

1. 以下コードを実行

git clone https://github.com/ryfeus/gcf-packs.git
cd gcf-packs/selenium_chrome/source
unzip headless-chromium.zip

2. main.pyの中身を変更する

gitからクローンして、対象のディレクトリに移動すると「main.py」というpythonファイルがあります。

ありがたいことに、すでにスクレイピングのコード例を記載してくれていて、wikipediaのタイトルをランダムでとってくるスクリプトになってます。

まずはこれをデプロイしてみたいのですが、そのまえに少しだけ修正箇所があります。おそらく仕様がかわってこのままではエラーがでてしまうようです。

標準のコードが以下。

import os
from selenium import webdriver
from fake_useragent import UserAgent

def handler(request):
    chrome_options = webdriver.ChromeOptions()

    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')
    chrome_options.add_argument('user-agent='+UserAgent().random)

    chrome_options.binary_location = os.getcwd() + "/headless-chromium"    
    driver = webdriver.Chrome(os.getcwd() + "/chromedriver",chrome_options=chrome_options)
    driver.get('https://en.wikipedia.org/wiki/Special:Random')
    line = driver.find_element_by_class_name('firstHeading').text
    print(line)
    driver.quit()
    return line

変更後

import os
from selenium import webdriver

def handler(request):
    chrome_options = webdriver.ChromeOptions()

    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')

    chrome_options.binary_location = os.getcwd() + "/headless-chromium"    
    driver = webdriver.Chrome(os.getcwd() + "/chromedriver",chrome_options=chrome_options)
    driver.get('https://en.wikipedia.org/wiki/Special:Random')
    line = driver.find_element_by_class_name('firstHeading').text
    print(line)
    driver.quit()
    return line

変更箇所は3行目と18行目のuseragent関連を削除しています。

これをGCPのエディタ機能から変更してみましょう。

3. 試しにデプロイしてみる

スクリプトの修正ができたら、以下のコードでデプロイしてみましょう。

gcloud functions deploy handler --runtime python37 --trigger-http --region asia-northeast1 --memory 512MB

ちょっとだけ説明すると、「gcloud functions deploy」まではcloud functionsにデプロイしてくださいという意味。「handler」はmain.pyで作成した関数の名前です。あとは設定として、python3.7系でトリガーはhttp、リージョンを東京リージョン、メモリは512MBで設定するという意味になります。

また、実行中に以下のようなコードが現れたら、「y」を選択してください。

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

これはデプロイのURLにだれでも入れるようにしますか「Yes」「No」の選択肢です。Yesにしても問題ないのでyで。

Cloud Functions

4. デプロイ完了後、Cloud Functionsの画面に以下のように表示されます。

5. Cloud Shellの画面に戻り、https:asia-northeast1-~~というURLをコピーして、アクセスしてみる

curl https://asia-northeast1-************.cloudfunctions.net/handler

6. wikipediaのどっかのページのタイトルが返ってきます。

7. ログを確認してみる

8. main.pyのファイルの中身を自分のコードに修正する

9. 書き換えたら再び下記コードを実行する

gcloud functions deploy ****** --runtime python37 --trigger-http --region asia-northeast1 --memory 512MB

*****は書き換えた関数の名前を指定します。

10. 問題なく動けば完成です!!

私の方では設定した関数を実行すると、以下のようにスプレッドに吐き出される仕組みをつくることができました!毎月の分が積み重なるようにスクリプトを書きました。あとは毎月1日に自動で実行されるようにCloud Schedulerを設定して完了です!

Cloud Scheduler

11. Cloud Schedulerでジョブを作成

名前、説明、頻度、タイムゾーンを入力します。

頻度については、cronを設定する時と同じように設定します。画像のものは、日本時間で毎月1日の12:00に実行される設定です。

cronについてわからない方は以下の記事を参照してください。

http://miya0.dyndns.org/pc/settei/crontab.html

実行するものは、「HTTP」「Pub/Sub」「App Engine HTTP」の3種類から選択できます。今回はCloud FunctionsでHTTPを設定しているので、HTTPを選択します。

選択するとURLを入力する項目がでてくるので、Cloud Functionsで実行したURLを入力します。Cloud Functionsのトリガータブからも確認できます。

あとは、デフォルトのままで作成をクリックします。

問題なく作成できたら、完成です!

もしきちんとうごくか確認したい場合は、いますぐ実行をクリックすると実行できます!

まとめ

今までローカルやサーバー上で自動処理させていたのをCloud SchedulerとCloud Functionsを使って、GCP環境で実行させることができました!

今回はスクリプトの説明を省きましたが、spreadsheetとの連携やスクレイピングの書き方も知りたい方いらっしゃいましたら、twitterにてDMください!

次はGCPサービスの「Dataflow」と「Cloud Composer」について触れたいと思います。

お楽しみに!