Python用Google APIクライアントライブラリのImportエラーが発生してしまう問題をなんとかする

2020.03.26

データアナリティクス事業本部@札幌の佐藤です。

突然ですが皆さんは『アイカツオンパレード!』はご覧になっているでしょうか。
『アイカツオンパレード!』はアイカツ!シリーズの歴代メインキャラクターだけではなくゲストキャラまで大集合するお祭りアニメです。
今までのダンスCGもたくさん出てくるので、毎週土曜日10時30分からテンションが上がります。
最終回が3月28日なのでぜひご覧ください。

また突然ですが、公式YouTube「アイチューブ」にて『アイカツオンパレード!ドリームストーリー』が3月28日から放送されることはご存じでしょうか。
『アイカツオンパレード!』の特別編として配信されます。
舞台がドリームアカデミーになっており、主人公が音城ノエルということで往年のアイカツ!ファンの方には吉報だったと思います。

アニメがテレビからYoutubeに移り変わったということもあり、どれだけ視聴されたのかが数字として見えるようになりました。

再生回数がどのくらいになるか個人的に興味があるため、AWS Lambdaを使用してYoutubeの再生回数を自動で取得できるようにしたいなと思っています。

ということで今回はAWS LambdaからYoutubeの再生回数を取得する実装で詰まった、google-api-python-client のimportエラーについて書いていきたいと思います。

遭遇した事象

以下のようにbuild() 関数を実行した際に、ModuleNotFoundErrorImportError が出力されてしまう状況です。

from apiclient.discovery import build

def lambda_handler(event, context):
    YOUTUBE_API_KEY = 'Google Cloud PlatformサービスのAPIキー'

    youtube = build('youtube', 'v3', developerKey=YOUTUBE_API_KEY)

google-api-python-clientライブラリのアップロードまで

YoutubeにアクセスするためにPython用Google APIクライアントライブラリを使用します。
ただLambdaにはPython用Google APIクライアントライブラリはデフォルトで入っていないため、別途ライブラリを使えるようにする必要があります。

ローカルPCにディレクトリを作成して、以下コマンドで保存しておきます。

pip install --target ./python requests google-api-python-client google-auth requests-oauthlib

このフォルダをZIPファイル化します。
ただ、このZIPファイルは6MBくらいあるので、関数コードの「コードエントリタイプからアップロード」からアップロードすると

Lambda関数「XXXX」のデプロイパッケージが大きすぎて、インラインコード編集を有効にできません。ただし、関数を呼び出すことはできます。

というようなメッセージが出力されて、「コードをインラインで編集」が非表示になってしまいます。
ソース量もそんなに多くなくインライン編集したかったので、AWS LambdaのLayer機能からアップロードしました。

このような感じになっています。

実行時のエラー

動作チェックとして上記実装を行い、YouTube Data APIを使えるようにできるところまで実装しています。
この状況でボタン「テスト」を押下してエラーにならないかチェックしてみます。

正常終了しているようですが、ログ上エラーになっています。
出力されているログは以下になります。

Response:
null

Request ID:
"df60528a-90cf-4b50-a14c-XXXXXXXXX"

Function Logs:
START RequestId: df60528a-90cf-4b50-a14c-XXXXXXXXXVersion: $LATEST
[WARNING]   2020-03-26T06:07:01.330Z    df60528a-90cf-4b50-a14c-XXXXXXXXX   file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth
Traceback (most recent call last):
  File "/opt/python/googleapiclient/discovery_cache/__init__.py", line 36, in autodetect
    from google.appengine.api import memcache
ModuleNotFoundError: No module named 'google.appengine'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/python/googleapiclient/discovery_cache/file_cache.py", line 33, in <module>
    from oauth2client.contrib.locked_file import LockedFile
ModuleNotFoundError: No module named 'oauth2client'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/python/googleapiclient/discovery_cache/file_cache.py", line 37, in <module>
    from oauth2client.locked_file import LockedFile
ModuleNotFoundError: No module named 'oauth2client'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/python/googleapiclient/discovery_cache/__init__.py", line 42, in autodetect
    from . import file_cache
  File "/opt/python/googleapiclient/discovery_cache/file_cache.py", line 40, in <module>
    raise ImportError(
ImportError: file_cache is unavailable when using oauth2client >= 4.0.0 or google-authEND RequestId: df60528a-90cf-4b50-a14c-XXXXXXXXX

REPORT RequestId: df60528a-90cf-4b50-a14c-XXXXXXXXX Duration: 654.89 ms Billed Duration: 700 ms Memory Size: 128 MB Max Memory Used: 67 MB  Init Duration: 399.61 ms

ログを見る限りModuleNotFoundErrorImportError が発生しているようです。

対処方法

原因を探していると、google-api-python-clientのgithub上に同様の内容についてissueが起票されていました。

ModuleNotFoundError: No module named 'google.appengine'

タイトルの通りの現象が発生しているので、同様事象だと思います。
内容を見る限りbuild関数にcache_discovery=False をセットすることで回避できるようなので、試してみます。

from apiclient.discovery import build

def lambda_handler(event, context):
    YOUTUBE_API_KEY = 'Google Cloud PlatformサービスのAPIキー'

    youtube = build('youtube', 'v3', developerKey=YOUTUBE_API_KEY, cache_discovery=False)

結果も正常終了、ログも先ほどのようなメッセージが出力されなくなったのを確認できました。

最後に

google-api-python-clientのgithub上でissueとしてopenの状態のため、修正後はこのような対応は不要になると思います。

それにしても『アイカツオンパレード!ドリームストーリー』楽しみですね。