Google Cloud上で定期的にスクレイピングを実施、結果をGoogleDirveに保存する方法をお伝えする講座の4回目です。
今回の講座では、CloudFunctionからGoogleドライブにファイルを保存する方法を説明します。講座のポイントとしては、Googleドライブへアクセスするための認証をどうやって取得するかがポイントになります。
過去の講座はこちらより参照してください。
第2回「cloud schedulerによるジョブの定期実行方法」
第3回「Google Cloud Functionでスクレイピングを実行する方法(Selenium+Python)」
Googleドライブへのアクセス方法
Googleドライブへのアクセスできるようにするには、いくつかの方法がありますが、今回はGoogleCloud上でサービスアカウントを作成し、そのサービスアカウントに対してGoogleドライブの共有フォルダの書き込み権限を付与することで、Googleドライブにファイルを書き込む方法を実現します。
Googleドライブの認証には、GoogleCloud上で作成した秘密鍵を利用します。この方法はとても簡単ですが、秘密鍵が漏れてしまうと誰もがGoogleドライブにアクセスできてしまうので、秘密鍵のファイル管理には注意してください。
実行手順
手順1 GoogleCloud上でサービスアカウントと秘密鍵の作成
まず、サービスアカウントを作成します。
左のメニューより「APIとサービス」→「認証情報」をクリックします。
「+認証情報の作成」→「サービスアカウント」をクリックします。
「サービスアカウント名」を設定します。任意の文字列でOKですが、ここでは「test」と入力し、「完了」をクリックします。
また「サービスアカウント名」を入力すると自動で「サービスアカウントID」が設定されます。
戻った画面より、サービスアカウントが追加されていることが確認できます。サービスアカウントの作成はこれで完了です。
次に秘密鍵を作成します。まず、さきほど作成したサービスアカウントをクリックします。
サービスアカウントの詳細画面が表示されます。この画面の「メール」に記載されているアドレスは、後の手順の中で利用するので、メモをとっておいてください。また、このアドレスのことを「サービスアカウントのメールアドレス」と呼ぶことにします。
メニューから「キー」→「鍵を追加」→「新しい鍵を作成」をクリックします。
秘密鍵の作成画面が開くので、「キーのタイプ」に「JSON」を選択して「作成」をクリックします。
「秘密キーがパソコンに保存されました」と表示されるので「閉じる」をクリックします。
秘密キーはダウンロードされ、下側の赤枠より取得できます。
秘密鍵ファイルの作成が完了しました。
手順2 Googleドライブでフォルダの共有設定
Googleドライブで共有フォルダを作成、手順1で作成した「サービスアカウントのメールアドレス」に対して、アクセス権限を付与します。
まずはGoogleドライブにアクセスした上で、フォルダ「test」を作成します。
フォルダ「test」を右クリックするとメニューが開くのでその中から「共有」を選択します。
共有画面では、「ユーザーやグループを追加」と表示されている部分に「サービスアカウントのメールアドレス」を設定、「リンクをコピー」をクリック後、「送信」をクリックします。
共有が設定されると左側のフォルダ「test」のアイコンマークに人型のマークが設定されます。
次に共有フォルダのIDを取得します。ノートパッドを立ち上げて、さきほど「リンクをコピー」で取得した文字列を張り付けて内容を確認します。
緑色で塗りつぶした部分に「共有フォルダID」が記載されています。「共有フォルダID」は後で利用しますのでメモしておきましょう。
手順3 CloudFunctionのPythonコード作成
Pythonのプログラム「main.py」は、第3回の講座で使ったコードに、Googleドライブの共有フォルダ「test」に「test.csv」をアップロードする部分を追加しています。
なお79行目の「【共有フォルダIDで置き換え】」の箇所は、手順2で取得したGoogleドライブの「共有フォルダID」に書き換えてください。
from selenium import webdriver
import os
# GoogleDirveにファイル格納のための設定追加
from io import StringIO
import csv
from pydrive2.auth import GoogleAuth
from pydrive2.drive import GoogleDrive
from oauth2client.service_account import ServiceAccountCredentials
def hello_pubsub(event, context):
# ブラウザーを起動
global driver
options = webdriver.ChromeOptions()
# 以下は各種オプションを設定 なくても動作するものがほとんどです
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--disable-extensions')
options.add_argument('--single-process')
options.add_argument('--proxy-server="direct://"')
options.add_argument('--proxy-bypass-list=*')
options.add_argument('--blink-settings=imagesEnabled=false')
options.add_argument('--lang=ja')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument("--log-level=3")
options.add_argument("--disable-logging")
options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36')
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
options.page_load_strategy = 'eager'
# WEBブラウザとWEBドライバの場所を指定(GoogleCloudFunction用)
options.binary_location=os.getcwd()+"/headless-chromium"
driver = webdriver.Chrome(os.getcwd()+"/chromedriver",options=options)
# ↑2行をコメントアウトして以下1行に置き換えるとWindowsPC等他の環境でも動きます。
#driver = webdriver.Chrome(options=options)
# WEBページを取得
driver.get("https://hobby.somnia.jp")
driver.implicitly_wait(5) # 結果取得に5秒まで待つ
page_source = driver.page_source
# WEBページのタイトルを取得、7文字分表示
len=page_source.find("<title>")
print(page_source[len+7:len+14])
# ドライバのClose
driver.close()
driver.quit()
# ↓今回新たに追加した部分
#credentials.jsonを利用してGoogleDriveの認証情報の取得
gauth = GoogleAuth()
scope = ["https://www.googleapis.com/auth/drive"]
gauth.credentials = ServiceAccountCredentials.from_json_keyfile_name('credentials.json', scope)
# 認証情報を利用してGoogleドライブにアクセス
global g_drive # GoogleDrive
g_drive = GoogleDrive(gauth)
# CSVファイルの中身(今回は固定値)
out_all = [["aaa","bbbbb","12345"],["11","12","13"]]
# メモリ上にCSVファイルを作成
si = StringIO()
cw = csv.writer(si, quoting=csv.QUOTE_ALL)# csvの値にダブルクオートを付与。
cw.writerows(out_all)
# Googleドライブの操作
# ファイル名の指定
file1 = g_drive.CreateFile({'title': 'test.csv'})
# 内容の設定
file1.SetContentString(si.getvalue())
# 保存するフォルダの指定
file1['parents'] = [{'id': '【共有フォルダIDで置き換え】'}]
# Googleドライブへアップロード
file1.Upload()
if __name__ == "__main__":
hello_pubsub(None,None)
また、手順1で取得した公開鍵のファイルは「credentials.json」という名前で保存するのと、新しく「requirements.txt」というファイルを作成して以下を記載してください。
結果、以下の3ファイルの準備ができればOKです。
手順4 ファイルのアップロード
手順3で準備した3ファイルをGoogleCloudの「prj」フォルダにアップロードします。GoogleCloudのアップロード機能は、アップロード先に同じ名前のファイルがあるとうまく動作しないので、同じ名前のファイルがある場合「rm」コマンドで削除しておきます。
#prjフォルダに同じファイル名がある場合は以下のコマンドで削除しましょう
rm main.py
rm credentials.json
rm requirements.txt
テスト実行&動作確認
後はCloudFunctionでデプロイして、テストを実行しましょう。デプロイやテストの方法については第3回の講座を参照ください。
プログラムが正常に動作した場合、GoogleDrive上に「test.csv」が作成されます。
作業上のポイント・注意点
GoogleドライブへアクセスにはPyDrive2を使うこと
「PyDirve2」はGoogleドライブへのアクセス方法を容易にしてもらえるPythonライブラリです。これまでは「PyDrive」を使うことが多かったようですが、「PyDrive」は2016年以降更新されていないこともあり、「PyDirve2」を使っています。
CloudFunction上でのCSVファイルの作成はメモリ上で行うこと
Seleniumによるスクレイピングの結果をGoogleドライブに格納する方法を考えた場合、一度ディスク上にCSVファイルに保存し、そのファイルをGoogleドライブにアップロードするのが一般的かと思います。
ただし、CloudFunction上のディスクにCSVファイルを作成することは、権限等の課題があり簡単にはできません。
そこで、この講座では、StirngIOを利用して、プログラムのメモリ上にCSVファイルを作成し、そのファイルをGoogleドライブにアップロードしています。
※ 大きいファイルを作成する場合はメモリの容量も必要となるので注意してください。
コメント