質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

Google API

Googleは多種多様なAPIを提供していて、その多くはウェブ開発者向けのAPIです。それらのAPIは消費者に人気なGoogleのサービス(Google Maps, Google Earth, AdSense, Adwords, Google Apps,YouTube等)に基づいています。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

Heroku

HerokuはHeroku社が開発と運営を行っているPaaSの名称です。RubyやNode.js、Python、そしてJVMベース(Java、Scala、Clojureなど)の複数のプログラミング言語をサポートしている。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

0回答

742閲覧

Heroku上のflaskAPIサーバーへGASから出すリクエストが500エラーとなる

touch4645

総合スコア3

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

Google API

Googleは多種多様なAPIを提供していて、その多くはウェブ開発者向けのAPIです。それらのAPIは消費者に人気なGoogleのサービス(Google Maps, Google Earth, AdSense, Adwords, Google Apps,YouTube等)に基づいています。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

Heroku

HerokuはHeroku社が開発と運営を行っているPaaSの名称です。RubyやNode.js、Python、そしてJVMベース(Java、Scala、Clojureなど)の複数のプログラミング言語をサポートしている。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/10/23 05:51

Google Apps Script(GAS)上でGoogle Ads APIを利用することを目標にしています。

Google Ads APIは特定の言語(Python等)に対してのライブラリしか用意が無いため、
基本的にはGAS上では利用が困難です。
そのため、PythonのGoogle Adsライブラリとflaskを用いてGoogle Ads APIへリクエストし、
レスポンスをリターンするAPIサーバーをHeroku上に作成しました。

app.py

Python

1from flask import Flask 2from flask import request 3from flask import jsonify 4 5from src.modules.Google_Ads import get_keywords_data 6 7import logging 8import sys 9 10 11app = Flask(__name__) 12app.config['JSON_AS_ASCII'] = False 13 14 15# ログを標準出力に出力する 16app.logger.addHandler(logging.StreamHandler(sys.stdout)) 17# (レベル設定は適宜行ってください) 18app.logger.setLevel(logging.ERROR) 19 20 21@app.route('/') 22def index(): 23 return 'Hello World!' 24 25 26@app.route('/volume', methods=['POST']) 27def get_volume(): 28 if request.method == 'POST': 29 request_json = request.json 30 required = ( 31 'keys', 'page_url') 32 if not any(k in request_json for k in required): 33 app.logger.info(jsonify({'message': 'missing values'})) 34 return jsonify({'message': 'missing values'}), 400 35 36 customer_id = DEFAULT_CUSTOMER_ID 37 language_id = DEFAULT_LANGUAGE_ID 38 location_id = DEFAULT_LOCATION_ID 39 40 keyword = [] 41 if 'keys' in request_json: 42 keyword.append(request_json['keys']) 43 44 if 'page_url' in request_json: 45 page_url = request_json['page_url'] 46 else: 47 page_url = '' 48 49 is_volumed = get_keywords_data( 50 customer_id, location_id, language_id, keyword, page_url 51 ) 52 53 if not is_volumed: 54 app.logger.info(jsonify({'message': 'fail'})) 55 return jsonify({'message': 'fail'}), 400 56 app.logger.info(jsonify({'message': 'success', 'result': is_volumed})) 57 return jsonify({'message': 'success', 'result': is_volumed}), 201 58 59 60if __name__ == '__main__': 61 app.run(host='0.0.0.0', port=8080, debug=True)

ここまではおそらく問題がなく、curlでPOSTをしても問題なく値を取得できます。

curl

1curl -X POST -H "Content-Type: application/json" -d '{"keys": "東京都 レストラン"}' https://my-app-name.herokuapp.com/volume

ですが、Google Apps ScriptからUrlfetchにてリクエストを出した場合、稀にリクエストが通る時がある程度の成功率となってしまいます。(必ずエラーになるわけではなく、数十回に1回はなぜかリクエストが正常に通り、レスポンスが帰ってきます)

GoogleAppsScript

1function mainVolume() { 2 const base_url = 'https://my-app-name.herokuapp.com/'; 3 4 const keys = "東京都 レストラン"; 5 6 const volumes = getKeywordVolumes(base_url, address, keys); 7 return volumes; 8} 9 10 11function getKeywordVolumes(base_url, keys=false, page_url=false) { 12 const url = base_url + 'volume'; 13 14 const data = {}; 15 16 if(page_url) { 17 data["page_url"] = page_url; 18 } 19 if(keys) { 20 data["keys"] = keys; 21 } 22 23 const param = { 24 'method' : 'POST', 25 'headers' : { 26 'Content-Type' : 'application/json' 27 }, 28 'payload': JSON.stringify(data), 29 'muteHttpExceptions' : true 30 }; 31 console.log(url, param); 32 33 const json = UrlFetchApp.fetch(url, param).getContentText(); 34 console.log(json); 35 return JSON.parse(json); 36}

エラーも様々に出ていて、Herokuサーバー自体が原因としても考えられるのですが、その場合はcurlが問題なく通る理由がわからなくなります。

確認したエラー

HerokuLog

1 app[web.1]: Traceback (most recent call last): 2 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/grpc_helpers.py", line 57, in error_remapped_callable 3 app[web.1]: return callable_(*args, **kwargs) 4 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 216, in __call__ 5 app[web.1]: response, ignored_call = self._with_call(request, 6 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 257, in _with_call 7 app[web.1]: return call.result(), call 8 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_channel.py", line 333, in result 9 app[web.1]: raise self 10 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 241, in continuation 11 app[web.1]: response, call = self._thunk(new_method).with_call( 12 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 266, in with_call 13 app[web.1]: return self._with_call(request, 14 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 257, in _with_call 15 app[web.1]: return call.result(), call 16 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_channel.py", line 333, in result 17 app[web.1]: raise self 18 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 241, in continuation 19 app[web.1]: response, call = self._thunk(new_method).with_call( 20 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 266, in with_call 21 app[web.1]: return self._with_call(request, 22 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 254, in _with_call 23 app[web.1]: call = self._interceptor.intercept_unary_unary(continuation, 24 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/ads/google_ads/interceptors/exception_interceptor.py", line 169, in intercept_unary_unary 25 app[web.1]: self._handle_grpc_failure(response) 26 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/ads/google_ads/interceptors/exception_interceptor.py", line 141, in _handle_grpc_failure 27 app[web.1]: raise self._get_error_from_response(response) 28 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_interceptor.py", line 241, in continuation 29 app[web.1]: response, call = self._thunk(new_method).with_call( 30 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_channel.py", line 837, in with_call 31 app[web.1]: return _end_unary_response_blocking(state, call, True, None) 32 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/grpc/_channel.py", line 729, in _end_unary_response_blocking 33 app[web.1]: raise _InactiveRpcError(state) 34 app[web.1]: grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with: 35 app[web.1]: 36 app[web.1]: 37 app[web.1]: 38 app[web.1]: > 39 app[web.1]: 40 app[web.1]: The above exception was the direct cause of the following exception: 41 app[web.1]: 42 app[web.1]: Traceback (most recent call last): 43 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app 44 app[web.1]: response = self.full_dispatch_request() 45 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request 46 app[web.1]: rv = self.handle_user_exception(e) 47 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception 48 app[web.1]: reraise(exc_type, exc_value, tb) 49 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise 50 app[web.1]: raise value 51 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request 52 app[web.1]: rv = self.dispatch_request() 53 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request 54 app[web.1]: return self.view_functions[rule.endpoint](**req.view_args) 55 app[web.1]: File "/app/app.py", line 78, in get_volume 56 app[web.1]: is_volumed = get_keywords_data( 57 app[web.1]: File "/app/src/modules/Google_Ads.py", line 198, in get_keywords_data 58 app[web.1]: return main(google_ads_client, customer_id, location_ids, language_id, keyword_texts, page_url) 59 app[web.1]: File "/app/src/modules/Google_Ads.py", line 103, in main 60 app[web.1]: for idea in keyword_ideas: 61 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/page_iterator.py", line 212, in _items_iter 62 app[web.1]: for page in self._page_iter(increment=False): 63 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/page_iterator.py", line 249, in _page_iter 64 app[web.1]: page = self._next_page() 65 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/page_iterator.py", line 534, in _next_page 66 app[web.1]: response = self._method(self._request) 67 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/gapic_v1/method.py", line 145, in __call__ 68 app[web.1]: return wrapped_func(*args, **kwargs) 69 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/retry.py", line 281, in retry_wrapped_func 70 app[web.1]: return retry_target( 71 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/retry.py", line 184, in retry_target 72 app[web.1]: return target() 73 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/timeout.py", line 214, in func_with_timeout 74 app[web.1]: return func(*args, **kwargs) 75 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/google/api_core/grpc_helpers.py", line 59, in error_remapped_callable 76 app[web.1]: six.raise_from(exceptions.from_grpc_error(exc), exc) 77 app[web.1]: File "<string>", line 3, in raise_from 78 app[web.1]: google.api_core.exceptions.ResourceExhausted: 429 Resource has been exhausted (e.g. check quota). 79 app[web.1]: 10.69.31.173 - - [23/Oct/2020:04:51:27 +0000] "POST /volume HTTP/1.1" 500 290 "-" "Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; +https://script.google.com; id: UAEmdDd_PyV8UkOp5JGIO-aAM4YeiKkU-Ug)"

429エラーが確認されていますが、リソースがどの部分を指しているのかがわかりません。
Google Apps ScriptやGoogle Adsは一人で使用し、リクエスト回数を数十回程度です。
複雑なことになっていますが、どなたかお力添えいただけると助かります。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

macaron_xxx

2020/10/25 23:20

関数の呼び出し元の引数の順番が一致していない?
FiroProchainezo

2020/10/26 02:50

429というエラーコードから、リソース枯渇なのではないかと推測しました。 Google Apps ScriptとGoogle Ads APIのどちらで枯渇しているのかわかりませんが、それぞれの利用制限を超えていないか確認してみてはいかがでしょうか? 例えば、(その制限が存在するかわかりませんが、クラウドサービスにありがちな制限として)1日のリクエスト制限や1時間のリクエスト制限、1リクエストの処理時間制限、1リクエストのサイズ制限(インプット/アウトプット)などです。 1回しかリクエストしていないつもりでも、管理コンソールのようなところで見たら尋常じゃない数のリクエストが来てるとか、意図しないサイズを送っているとかがあるかもしれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問