Google Apps Scriptの関数をPythonから起動する

#Google Apps ScriptをPythonから起動してスプレッドシートのデータを、Gmailで送信したい

状況
・WindowsのPythonでスクレイピングをしている
・スプレイピング結果をGmailでお知らせする
・送信先や店舗名などマスタ情報はスプレッドシート上にある

環境
・python3.9 Windows10
・GoogleWorkspace Business Starter

まず考えたこと
①GmailAPIでメールを送信して、GoogleSheetsAPIでマスタ情報を取得しようか
→GmailAPIを使って送信者名を設定したり、htmlメールを送信する方法がわからない
→[GmailAPI Python Quickstart](https://developers.google.com/gmail/api/quickstart/python)
<b>↑GmailAPIで送信者名を設定したり、htmlメールを送信する方法がわからなかった。どなたか教えてください。</b>

②pythonのsmtplibを使ってGmailを送信しようか
→スプレッドシートのデータはGoogle Sheets APIをつかわないといけない。結局グーグルのAPIが必要だなー。

#決めたこと
#AppsScriptAPIを使ってPythonからGoogleAppsScriptを起動。スプレッドシートから必要なデータを取得して、Gmailを送信しよう

[AppsScriptAPI公式ページ](https://developers.google.com/apps-script/api)

#グーグル検索に使ったキーワード
・GoogleCloudPlatform プロジェクト作成
・GoogleCloudPlatform apiを有効にする
・GoogleCloudPlatform 認証方法
・ExecutionAPI　使い方
・AppsScriptAPI　使い方
・GoogleAppScript　実行可能API




|番号|見出し|
|:----:|:-----------------|
|1|[GoogleAppsScriptの記述](#GoogleAppsScript)|
|2|[Pythonの記述](#pythonの記述)|
|3|[GoogleAppsScriptの実行可能APIを呼び出すプログラムのポイント](#googleappsscript%E3%81%AE%E5%AE%9F%E8%A1%8C%E5%8F%AF%E8%83%BDapi%E3%82%92%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%99%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88)|
|設定手順|
|1|[GoogleAppsScriptの作成(GAS)](#%E3%82%B9%E3%83%97%E3%83%AC%E3%83%83%E3%83%89%E3%82%B7%E3%83%BC%E3%83%88%E3%81%AE%E5%86%85%E5%AE%B9%E3%82%92%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%93%E3%81%A7gmail%E3%82%92%E9%80%81%E4%BF%A1%E3%81%99%E3%82%8B%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8Bgoogleappsscript)|
|2|[プロジェクトの作成(GoogleCloudPlatform)](#googlecloudplatform%E3%81%A7%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)|
|3|[APIを有効にする(GoogleCloudPlatform)](#api%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8Bgooglecloudplatform)|
|4|[Auth同意画面を作成する(GoogleCloudPlatform)](#auth%E5%90%8C%E6%84%8F%E7%94%BB%E9%9D%A2%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8Bgooglecloudplatform)|
|5|[認証情報を作成する(GoogleCloudPlatform)](#googlecloudplatform%E3%81%A7%E8%AA%8D%E8%A8%BC%E6%83%85%E5%A0%B1%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)|
|6|[プロジェクトを関連づける(GoogleAppsScript)](#googlecloudplatform%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E3%82%B9%E3%83%97%E3%83%AC%E3%83%83%E3%83%89%E3%82%B7%E3%83%BC%E3%83%88%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AB%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91%E3%82%8Bgoogleappsscript)|
|7|[実行可能APIとしてデプロイ(GoogleAppsScript)](#%E5%AE%9F%E8%A1%8C%E5%8F%AF%E8%83%BDapi%E3%81%A8%E3%81%97%E3%81%A6%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B)|


まずはソースから
簡単な記述だけど、ネットの情報が古かったり、微妙に自分の求めているものとちがったり。
環境構築しないとコピペしても動かないので要注意。
環境構築内容はソースの後にメモしました。


<font color="red">※はまりポイントGoogleAppsScriptは単体で動作することを必ず確認すること</font>

#GoogleAppsScript

```:GoogleAppsScript

function testAPI(params){

　//下の内容は適時変更する
  const FROM_FORM="1〇〇〇f8sQLIYGQyeKeoqNnOOBP-IzpWWYDaabcdefg";  //スプレッドシートのID
  const FROM_SHEET="シート1";                                      //シート名
  const MAILADD="test〇〇〇〇@gmail.com";                           //メールの送信先
  const MAILTITLE="メールのタイトルです";                            //メールのタイトル
  const FROMNAME="山本やま子";                                      //メールの差出人
    
  //関数の引数がどのようになっているのか出力してみる
  Logger.log("型="+typeof(params));  
  Logger.log( params.test1 );
  Logger.log(params.test2)
 
  

  //スプレッドシートからA1の値を取得する
  let from_sheet = SpreadsheetApp.openById(FROM_FORM).getSheetByName(FROM_SHEET)
  let A1=from_sheet.getRange("A1").getValue()

  //スプレッドシートA１の内容をHTMLメールで送信してみる

  let html_body ="<html><body>リンク<br><a href='https://yahoo.co.jp'>A1の内容" + A1 + "</a></body></html>"
    
//MailApp.sendEmailの参考URL
//https://developers.google.com/apps-script/reference/mail/mail-app#sendEmail(String,String,String,Object)

  //メールを送信する
  MailApp.sendEmail(MAILADD, MAILTITLE, html_body, {name:'岩崎絵李子',htmlBody:html_body});
  
　//文字列をリターンしてみる
  return "aaaaaaaaaa";

}

```

#pythonの記述

AppsScriptAPIを使ってGoogleAppsScriptの関数を呼び出す
Windows python3.9

```:python
from __future__ import print_function
import os.path
from googleapiclient import errors
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials

def main():
    """Runs the sample.
    """
    # GoogleAppsScriptのデプロイIDのこと
    SCRIPT_ID = 'aaaaaAXc1Ebxd0-XKhjVVV〇〇〇4l0-7yMIf0ZdqzAMSiQlLQDbbbbbbb'

    #④GoogleCloudPlatformからダウンロードしたJSONファイル    
    #任意の場所で名前を変えてもOK。
    #謎の不具合解消のため、最初はフルパスで記入することをおすすめ。例はwindowsだよ。
    JSONFILE=r"C:\Users\test\Desktop\GmailApi\××××××××××××××.json"

    # ④GoogleCloudPlatformでAuth同意画面を作成する で選択したスコープのこと
    SCOPES = [
        'https://www.googleapis.com/auth/script.scriptapp',
        'https://www.googleapis.com/auth/script.send_mail',
        'https://www.googleapis.com/auth/spreadsheets'
    ]

    creds = None
    
　　#　プログラムのある場所にtoken.jsonが自動で作成されます。
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)

    # token.jsonがない場合ブラウザ上で認証画面が表示されます。
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                JSONFILE, SCOPES)
            creds = flow.run_local_server(port=0)
        
        with open('token.json', 'w') as token:
            token.write(creds.to_json())
    service = build('script', 'v1', credentials=creds)

    # GoogleAppsScriptで呼び出す関数名の設定・パラメータの設定
　　# 下の例だと 関数名=testAPI　キー=test1値=test1の文字
    request = {"function": "testAPI",
        "parameters":{'test1':'test1の文字','test2':'test2の文字'}
    }

    try:
        
        response = service.scripts().run(body=request,
                scriptId=SCRIPT_ID).execute()

        if 'error' in response:
            
            error = response['error']['details'][0]
            print("Script error message: {0}".format(error['errorMessage']))

            if 'scriptStackTraceElements' in error:
               
                print("Script error stacktrace:")
                for trace in error['scriptStackTraceElements']:
                    print("\t{0}: {1}".format(trace['function'],
                        trace['lineNumber']))
        else:
           
            #GoogleAppsScriptでreturanされた値　
            folderSet = response['response'].get('result', {})
            print(folderSet)

    except errors.HttpError as e:        
        print(e.content)


if __name__ == '__main__':
    main()

```


#GoogleAppsScriptの実行可能APIを呼び出すプログラムのポイント

参考にしたのは以下の二つ
[1 AppsScriptAPIサンプル](https://developers.google.com/apps-script/api/how-tos/execute#python)
[2 認証部分のサンプル](https://developers.google.com/apps-script/api/quickstart/python)

[1 AppsScriptAPIサンプル](https://developers.google.com/apps-script/api/how-tos/execute#python)は認証部分が非推奨で古い、しかも、グーグルドライブを操作しています。
なので認証部分は[2 認証部分のサンプル](https://developers.google.com/apps-script/api/quickstart/python)を参考にし、グーグルドライブの部分はスプレッドシートとGmailに置き換える必要があります。

####①使うためにはライブラリのインストールが必要です。自分はWindowsなので先頭にpy -m を追加しています。

```:python
  py -m pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

```

<font color="red">※はまりポイント
pipしたのに、from googleapiclient import errors　が有効にならない。
→PCの再起動で解決
</font>




####②■デプロイIDが必要
[⑤実行可能APIのデプロイIDをコピーする](#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4id%E3%82%92%E3%82%B3%E3%83%94%E3%83%BC%E3%81%99%E3%82%8B)
<font color="red">※はまりポイント
GoogleAppsScriptを変更するためにデプロイが必要。
更新後デプロイするときに「新しいデプロイ」を選ぶとデプロイIDが変わってしまうので、「デプロイを管理」を選んで「編集」→「デプロイ」をすればデプロイIDが変わらないので便利
</font>

###③OAuthクライアント(Jsonファイル)が必要
[Jsonファイルをダウンロードする](#oauth%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F%E7%94%BB%E9%9D%A2%E3%81%8B%E3%82%89json%E3%82%92%E3%83%80%E3%82%A6%E3%83%B3%E3%83%88%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B)
ファイル名は修正OK
<font color="red">※はまりポイント
exeファイルにした場合エラーがでるのでファイルの場所をフルパスで指定。</font>

####④　token.json には要注意
token.json、存在しない場合自動で作られます。
他のソースで上書きされないように。
謎の不具合が発生した場合、token.jsonを消すとうまくいくことも・・・

<font color="red">※はまりポイント
Auth同意画面を変更したり修正した場合、token.jsonも新たに必要です。
そのままだと更新されないのでいったん削除が必要
</font>







#環境構築手順
##①スプレッドシートの内容を読み込んでGmailを送信するスクリプトを作成する(GoogleAppsScript)
当然だけどここから。
初めてスクリプトを実行ときは認証画面が表示されるので認証を済ませる。
<font color="red">※はまりポイント
後ででてくるAuth同意画面との区別がつかなくなるので、GoogleAppsScriptの認証画面は前もって済ませておく</font>
<b>関数名をメモしておく。pythonプログラムで使います。</b>

##②GoogleCloudPlatformでプロジェクトを作成する
プロジェクトの作成方法は検索すればたくさんでてきます。
[プロジェクトの作成方法](https://cloud.google.com/resource-manager/docs/creating-managing-projects)
<b>プロジェクトを作成したらプロジェクト番号を控える。（GoogleAppsScriptと関連付けるため)
プロジェクト番号はGoogleCloudPlatformのダッシュボードで確認できます。</b>
[ダッシュボード](https://console.cloud.google.com/home/dashboard)
<font color="red">※はまりポイント
プロジェクト名とログインIDを確認すること</font>
特にログイン名は勝手に別のものに切り替わっています。
プロジェクト名も気が付くと別のプロジェクトに切り替わってました。

プロジェクト番号確認方法↓
![プロジェクト番号を確認する](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/cbb8b96f-83d7-2e9f-0fb9-56b94eaecb0e.png "プロジェクト番号を確認する")


##③APIを有効にする(GoogleCloudPlatform)
今回は以下の３つを有効にしました
[Google cloud platform apiを有効にする](https://cloud.google.com/endpoints/docs/openapi/enable-api?hl=ja)
・Apps Script API
・Gmail API
・Google Sheets API
<font color="red">※はまりポイント
APIはプロジェクトごとに有効にする。</font>
以前有効にしたよと思ってもだめです。
プロジェクトごとに有効にしてください。

##④Auth同意画面を作成する(GoogleCloudPlatform)
・内部でしか使わないので「内部」を選択し「作成」を押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/3391324b-dec4-d10d-2c2d-c1de26636c52.png)
・次にでてくる「アプリ登録の編集」画面は認証画面デザインのこと。今回は社内の人しか見ないので適当でOK
![アプリ登録の編集](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/b1a16fed-585e-af0b-7063-7c0372196504.png)

・スコープの選択
GoogleAppScriptでスプレッドシートを読み込んでGmailを送信したいので以下の３つを選択します。
.../auth/script.scriptapp
.../auth/script.send_mail
.../auth/spreadsheets
Pythonのプログラムで↑のアドレスを使うのでこれとても重要です。



![スコープの選択](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/74443e8f-cea8-5ac9-f7fb-5a493f1a3d67.png)
「スコープを追加または削除」ボタンを押すと以下のような画面が表示されるので、
.../auth/script.scriptapp
.../auth/script.send_mail
.../auth/spreadsheets
の３つを選択する
<font color="red">※はまりポイント</b>
APIが有効になっていないと↑のアドレスでてきません。
表示されない場合はAPIが有効になっているかどうか確認する</font>

![スコープの選択](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/8aa77366-c17d-21dd-ad7b-f2434742d6c7.png)

これでOath同意画面が作成できたました。

##⑤GoogleCloudPlatformで認証情報を作成する

####①[認証情報画面](https://console.cloud.google.com/apis/credentials)を開く
<font color="red">※はまりポイント
ログインIDとプロジェクト名を確認すること。毎回意図しないプロジェクト名が表示されます。</font>

####②「＋認証情報」を作成をクリックし、OAuthクライアントIDを選択する

![認証情報の作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/7d0e0e98-f3b4-a9ab-9545-9a3fee901f33.png)

####③アプリケーションの種類を選択する。
今回はWindows上のPythonなのででディスクトップアプリを選択しました。
例えばサーバー上のphpの場合ウェブアプリケーションを選択します。

![アプリケーションの種類を選択する](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/2885e350-7b78-9044-c99a-c1533d60b5df.png)

####④作成ボタンを押す

####⑤OAuthクライアントを作成しました画面からJsonをダウントードする。
※pythonのプログラムで使うのでなくさないように。
もちろんあとからもダウンロードできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/a11dd24d-8b63-1fff-bf98-ae60f328167f.png)





##⑥GoogleCloudPlatformのプロジェクトをスプレッドシートのプロジェクトに関連付ける(GoogleAppsScript)

・GoogleCloudPlatformのプロジェクト番号をGoogleAppsScriptに登録します。
画面左の「プロジェクトの設定」を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/4bb3485d-dfc1-f9bb-037b-c6114b9268af.png)


画面の下の方にある「プロジェクトを変更」ボタンを押すと下記載画面がでてきます。
GoogleCloudPlatformのプロジェクト番号を入力して「プロジェクトを設定」ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/c0fd04f6-6d33-78ff-8ba6-837f500853bc.png)

<b>※はまりポイント①</b>
GoogleCloudPlatformのダッシュボード。気が付くと別のプロジェクトに変わっていた。
そのせいで間違ったプロジェクト番号を登録してしまった。
どのプロジェクトのページを開いているか確認すること

##⑦実行可能APIとしてデプロイする
GoogleAppsScriptを実行可能APIとしてデプロイします。


####①画面右上のデプロイボタンを押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/3144eec2-ac26-f8db-1a37-3c13d11b415a.png)

####②新しいデプロイを選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/1a075c91-2af8-25df-6f34-6a1ad3e0c3a0.png)

####③種類の選択から実行可能APIを選択する
※はまりポイント　種類の選択の　設定マークをクリックするのにしばらくきがつかなかった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/cc4f777c-0690-9b43-4bf4-29cf331d9ab4.png)

####④アクセスできるユーザーを自分のみにする
今回はスクレイピングでメール送信するIDしか使わないので「自分のみ」を選択しました。
その後「デプロイ」ボタンを押す

※慣れるまでは自分のみにしたほうがよいです。仕組みが分かってから、公開範囲を広げたほうが安心です。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/c1b2330c-db03-b647-9157-6ba387d078e6.png)

###⑤デプロイIDをコピーする
Pythonのプログラムで使います。
「デプロイ」ボタンを押すと以下の画面が表示されるので、デプロイIDをコピーして控えておく
(もちろん後から確認することもできます。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369082/a82a64b2-ee8e-e767-fa9f-cc7fe7c70dd4.png)!



感想
スクレイピングの結果をスプレッドシート上で使うことが格段に増えてきているので
APIの出番はこれからも増えそうです。
次この案件が必要になったとき、キャプチャとかすべて変わっていそう。・・・・