つくるもの
Pythonで取得したデータを順次GASに返却し、GAS側で受け取ったデータをスプレッドシートに書き込む。
GASには時間制限があるため、開始位置と終了位置を指定し、細かく区切って返却するようにする。
- Flask側アプリ
- Flaskで作成
- Herokuで公開
- 処理内容:開始位置と終了位置を受け取りデータ(今回は仮データ)を返す
- GAS側
- 実行時、開始位置と終了位置をFlaskアプリに投げ、受け取ったJSONデータをGoogleスプレッドシートに書き込む
- 一回の実行で10ずつデータを受け取り、50受け取るまで繰り返し実行する
環境
- MAC
- Python 3.11.3
- Flask 3.0.2
Flask側アプリの作成
flaskのinstall
pip install Flask
開発フォルダの作成
mkdir my-test-api
cd my-test-api
APIの作成
pythonファイルのファイル名は任意
main.py
from flask import Flask, request, jsonify
import os
app = Flask(__name__)
@app.route('/process', methods=['POST'])
def process_data():
# POSTリクエストからstart_numberとend_numberの値を取得
start_number = int(request.form['start_number'])
end_number = int(request.form['end_number'])
results = []
# start_numberとend_numberの範囲で処理を繰り返す
for i in range(start_number, end_number + 1):
# ここでstart_numberとend_numberを使って何らかの処理を行う
result = f"実行結果{i}"
# 結果をリストに追加
results.append([i, result])
# 処理結果をJSON形式で返す
return jsonify(results)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
ローカル環境でのテスト
先ほど作成したFlaskアプリケーションを実行する
Python main.py
テスト用アプリの作成
先ほど作ったFlaskアプリケーションをテストするためのFlaskアプリケーションを作成する。
test.py
import requests,json
url = 'http://127.0.0.1:5000/process'
data = {'start_number': 10, 'end_number': 20}
response = requests.post(url, data=data)
# エスケープされたJSON文字列をデコードしてリストに変換
decoded_list = json.loads(response.text)
print(decoded_list)
テスト用アプリケーションの実行
先ほどのFlaskアプリケーションを起動したまま、別ターミナルで実行する
Python test.py
実行結果
$ python test.py
[[10, '実行結果10'], [11, '実行結果11'], [12, '実行結果12'], [13, '実行結果13'], [14, '実行結果14'], [15, '実行結果15'], [16, '実行結果16'], [17, '実行結果17'], [18, '実行結果18'], [19, '実行結果19'], [20, '実行結果20']]
FlaskアプリケーションをHerokuにデプロイする
runtime.text
herokuでサポートしているPythonのバージョンを確認する。
echo "python-3.11.3" > runtime.txt
Procfile
install gunicorn
echo web: gunicorn main:app --log-file=- > Procfile
requirements.txt
pip freeze > requirements.txt
gunicornの行が出力されていることを確認。
ファイルの作成完了
my_test_app/
│
├── main.py
├── Procfile
├── requirements.txt
└── runtime.txt
heroku CLI インストール
brew tap heroku/brew && brew install heroku
heroku ログイン
heroku login
アプリ作成
アプリ名は他と被らないような任意のサイト名を設定する。
heroku create アプリ名
(例)これだと被るので作成できない
heroku create my-test-api
リモートブランチ作成
my-test-apiの部分は作成したアプリ名に置き換え
heroku git:remote -a my-test-api
Herokuにmainブランチをpushする
git push heroku main
デプロイ後の動作確認
先ほど作成したtest.py
を流用する
test.py
import requests,json
url = '[★この部分をデプロイ後のURLに差し替え]/process'
data = {'start_number': 10, 'end_number': 20}
response = requests.post(url, data=data)
# エスケープされたJSON文字列をデコードしてリストに変換
decoded_list = json.loads(response.text)
print(decoded_list)
実行
Python test.py
実行結果
$ python test.py
[[10, '実行結果10'], [11, '実行結果11'], [12, '実行結果12'], [13, '実行結果13'], [14, '実行結果14'], [15, '実行結果15'], [16, '実行結果16'], [17, '実行結果17'], [18, '実行結果18'], [19, '実行結果19'], [20, '実行結果20']]
GAS側アプリの作成
前準備:スプレッドシートの作成
作成したGoogleスプレッドシートのURLからIDを取得する
https://docs.google.com/spreadsheets/d/[この部分がID]/edit#gid=0
シート名を結果出力
に変更
GASアプリケーションの作成
GAS から「新しいプロジェクト」を作成する。
var url = "[デプロイしたURL]/process";
function myFunction() {
// スプレッドシートのシート名を指定します
var sheetName = "結果出力";
// スプレッドシートを取得します
var ss = SpreadsheetApp.openById("[スプレッドシートのID]");
var sheet = ss.getSheetByName(sheetName);
// スクリプトプロパティから最後に処理した値を取得
var startNumberStr =
PropertiesService.getScriptProperties().getProperty("start_number");
var endNumberStr =
PropertiesService.getScriptProperties().getProperty("end_number");
// 値がない場合は初期値を設定
if (!startNumberStr || !endNumberStr) {
// スプレッドシートのデータをすべてクリアします
sheet.clear();
startNumber = 1;
endNumber = 10;
} else {
startNumber = parseInt(startNumberStr);
endNumber = parseInt(endNumberStr);
}
var payload = {
start_number: startNumber.toString(),
end_number: endNumber.toString(),
};
var options = {
method: "post",
payload: payload,
};
var response = UrlFetchApp.fetch(url, options);
// 取得したデータを処理する(ここではログに出力する)
Logger.log(response); // 取得したデータをログに出力
// データをシートに書き込む
let json = JSON.parse(response)
var row = startNumber;
json.forEach(function(rowData) {
rowData.forEach(function(cellData, col) {
sheet.getRange(row, col + 1).setValue(cellData);
});
row++;
});
// 次の範囲をセットアップ
startNumber += 10;
endNumber += 10;
// スクリプトプロパティに次回処理する値を保存
PropertiesService.getScriptProperties().setProperty(
"start_number",
startNumber
);
PropertiesService.getScriptProperties().setProperty("end_number", endNumber);
// 100まで取得が終わったらトリガーを削除して処理を終了する
if (endNumber > 50) {
ScriptApp.getProjectTriggers().forEach(function (trigger) {
ScriptApp.deleteTrigger(trigger);
});
// ScriptProperties を削除します
PropertiesService.getScriptProperties().deleteAllProperties();
} else {
// 5秒後に自身を再実行するトリガーを設定
ScriptApp.newTrigger("myFunction").timeBased().after(5000).create();
}
}
GASアプリケーションの実行
トリガーとスクリプトプロパティがセットされていることを確認
トリガー
スクリプトプロパティ
最終的な実行結果
待っていれば、順次実行されるはず。
5回すべて実行完了するまでに7分程度かかっているようです。
終わりに
Flaskアプリケーション側の処理を変えれば、さまざまな応用ができると思います。