Blow Up by Black Swan

Google calendar APIをPythonで使ってみる(クイックスタート編)

今回はPythonからGoogle Calendar APIを利用する方法についての記事です。基本的には以下のサイトがよくまとまっており、そのサイトを見るだけで十分進めることはできるのですが、公式のサンプルコードや画面設定などは少し変わったので、今回はこの参考サイトを補足するような意味合いで記事を書きました。

なお、今回の記事は基本的に上記参考サイトと公式のクイックスタートに沿いつつ、私がつまづいたところなどを補足する内容となっています(見出しに記載している”step”の番号はクイックスタートと同じ番号です)。公式ページは英語版しか存在しないので、英文サイトや公式リファレンスに不慣れな方などにこの記事が参考になればと思っています。Google Calendar APIの公式ページとPythonからGoogle APIを利用するためのクライアントライブラリーは下記サイトになります。記事内で説明していますが、PythonからAPIを利用する方法は少しクセがあるのでご注意下さい。なお、私はずっと誤解していたのですが、「calender」ではなく「calendar」が正しい綴りでした(^^;

1. APIの利用の全体像と利用環境について

今回の実行フローは次のようになります。

(2-1)GoogleカレンダーのAPIを登録
 -> (2-2)認証情報の取得とJSONデータのダウンロード
 -> (3)Google client libraryのインストール
 -> (4, 5)サンプルコードの実行

APIを登録するにはプロジェクトを作る必要がありますが、このプロジェクトとGoogle CalendarなどのAPI、アプリケーションとの関係は次のようになります。

pythonとGoogleAPIの利用イメージ図

図を見るとわかりますが、Projectの中で利用するAPIを設定し、PythonプログラムをOAuthで認証することによって、Pythonプログラムからプロジェクト内のAPIを利用できるようになります。このプロジェクトはGoogle Cloud Platform(GCP)のプロジェクトと同様のものですが、今回はGCPを利用したことがない、プロジェクトを作ったことがないという方が登録する場合を前提としています。

今回の利用環境は次になります。

[利用環境]
OS: MAC OSX
Google Acount: 取得済み&ログイン済み
Python: 3.7.1

[前提条件]
・Google Cloud Platformへの登録無し
  (登録していると少しだけ画面の表示などが変わります)

2. (step1)Google Clender APIの有効化

まずはGoogle Calendar APIの有効化とOAuth認証の準備です。

2-1. APIの有効化

プロジェクトを持たない場合、APIを有効化する時にプロジェクトも作成されます。登録ページはこちらになります。

上記のリンク先に飛ぶと、次のような画面が出ます。グーグルアカウントにログインしていない場合はログイン作業が求められます。利用規約への同意をした上で「同意して続行」をクリックします。居住国やキャンペーンメールの受信は任意の項目です。またここで自動的に「My Project」というプロジェクトが作成される旨が記載されています。

APIコンソールの設定画面1

これでGoogle CalendarのAPIが有効になりました。別のプロジェクトを作成したことがある場合は、プロジェクト画面からもっと簡単にAPIを有効化することができます。次はOAuth認証の準備へと進んでいきます。

Google APIコンソールの設定画面2

2-2. 認証情報の取得

認証は他のアプリケーションからこのプロジェクトで指定したAPIを使えるようにするためのものです。認証する方法は複数ありますが、今回はOAuthという認証方法を利用します。OAuth認証については次のサイトが非常にわかりやすかったので、参考にして頂ければと思います。

前項の画面で「認証情報に進む」をクリックすると次のような画面に遷移します。このページで認証情報を取得するように思えますが、よく読むと「1 必要な認証情報の種類を調べる」と書いてあり、必要な認証情報について教えてくれるページだとわかります。今回は、OAuth認証を使うことが決まっているので、このページは特に活用しません。「ダッシュボード」メニューから「認証情報」メニューへと飛び、「OAuth同意画面」タブをクリックします。

Google APIの設定画面3

「OAuth同意画面」ページは以下のようになっています。ここでは認証の時などに表示される同意画面についての設定をします。他のアプリでGoogleを利用するときなどに同意画面が出ることがあると思いますが、その同意画面の表記などについて設定するページです。

Google APIの設定画面4

ここで「アプリケーション名」を決め、一番下の「保存」ボタンを押します。アプリケーション名とは、今回は外部からこのAPIを活用するPythonプログラムのことで、なんでも良いです。どのアプリケーションがプロジェクトに認証を求めているかを知るために必要となります。また、SCOPEとはこのプロジェクトで対応できる範囲です。今回は初期値のままにしています。

Google APIの設定画面5
Google APIの設定画面6

次に認証情報を作成します。「認証情報」タブをクリックし、そのページに表示された「認証情報を作成」ー「OAuthクライアントID」を選択します。

Google APIの設定画面7

そして、「アプリケーションの種類」とクライアントの「名前」を決め、作成ボタンをクリックすると認証情報が作成されます。

Google APIの設定画面8
Google APIの設定画面9

これで認証情報の作成ができました。このクライアントIDとクラアントシークレットをアプリケーションで利用することで、このプロジェクトのAPIを自由に使えるようになります。ただ、このやり方だと毎回この重要なクライアントIDを入力する必要があり、漏洩の恐れなどリスクもあるので、元記事と同様にJsonファイルを利用して、アプリケーションからより簡単に安全に利用できるようにします。「認証情報」タブの「名前」をクリックし、認証情報の詳細が書かれたページに飛びます。

Google APIの設定画面10

ここで、上部にある「JSONをダウンロード」をクリックします。

Google APIの設定画面11

ダウンロード画面で「credentials.json」と名前を変えて保存します。以上で認証情報の取得できました。この認証情報を活用し、PythonからAPIを利用できるようにします。

3. (step2)Google client libraryのインストール

次にPythonスクリプトからGoogleアプリのAPIを利用するときに必要となるGoogle client libraryをインストールします。pipで次のようにインストールします。後ろ2つは認証用のライブラリーで、最初のgoogle-api-python-clientは、Google Calendarを含めたGoogleアプリのAPIを操作するためのライブラリーです。こちらは個人的に若干癖があったので、最後に使い方をまとめています。

pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

これでGoogle Calendar APIを使うためのライブラリーがインストールできました。

4. (step3)サンプルコードのセットアップ

ここからはサンプルコードを使って実際にAPIを使います。サンプルコードは冒頭に紹介したクイックスタートに載っているものになります。わかりにくい認証トークンの取得フローについて説明した後、サンプルコードを載せています。

プログラム内容: ユーザーカレンダーの直近10個のイベントを表示
実行フロー:
   1. 認証
     a. token.pickleの適用範囲のSCOPEを指定(token.pickleが後続フローで既に存在する場合、そのtoken.pickleとSCOPEの範囲が一致している必要がある)
       * token.pickleはユーザーアクセスと更新のためのトークンを保管するファイルで、OAuth認証が完了した時に自動で作られる
     b. token.pickleファイルの存在を確認しあれば認証トークンを取得。有効であればそのまま認証トークンとして利用
     c. token.pickleが存在しない、または認証トークンが有効でない場合
       c-1. token.pickleが存在するが、利用期間が切れており、更新用トークンが存在する場合、トークンを更新し、token.pickleに書き込み
       c-2. c-1以外の場合、credentials.jsonを使ってOAuth認証を開始。ブラウザを立ち上げ認証画面を表示。認証が完了したらトークンをtoken.pickleに書き込み
   2. Goolge calendar APIを操作するためのインスタンス作成
   3. インスタンスでAPIに接続し、情報を取得
   4. プログラムをターミナルに表示 

サンプルコードの認証フローで使われているオブジェクトやメソッドの公式リファレンスは下記になります。

サンプルコードは下記になります。

from __future__ import print_function
import datetime
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# (a)token.pickleのSCOPEと一致している必要があり、不一致の場合、適合するtoken.pickleを再度認証作業を行い、作り直す必要がある。
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']

def main():
    # 1. 認証
    creds = None

    # (b)token.pickleファイルが存在するか確認
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # (c)有効なトークンを持たない場合、ユーザにログインと認証を促す
    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(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # 次回実行用にtoken.pickleにトークンを書き込み
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)

    # 2. Google Calnder APIを操作するためのインスタンス作成
    service = build('calendar', 'v3', credentials=creds)

    # 3. Google Calendar APIの呼び出し
    now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
    print('Getting the upcoming 10 events')
    events_result = service.events().list(calendarId='primary', timeMin=now,
                                        maxResults=10, singleEvents=True,
                                        orderBy='startTime').execute()
    events = events_result.get('items', [])

    # 4. ターミナルに取得情報を表示
    if not events:
        print('No upcoming events found.')
    for event in events:
        start = event['start'].get('dateTime', event['start'].get('date'))
        print(start, event['summary'])

if __name__ == '__main__':
    main()

以上がサンプルコードの構造です。ポイントはtoken.pickleファイルの作成の部分で、OAuthの認証フローと認証後に有効な状態を維持する方法が書かれています。

5. (step4)サンプルコードの実行

これまでのアプリケーションの認証とダウンロードしたJsonファイルを利用し、サンプルコードを実行してみます。サンプルコードの実行では、ファイル構造は下記のように、サンプルコードを保存したquickstart.pyファイルと認証情報作成時にダウンロードしたcredentials.jsonを同じフォルダに格納しています。

.
├── quickstart.py
└── credentials.json

このフォルダ直下でターミナルで下記のようにサンプルコードを実行すると、ブラウザが立ち上がり認証を求める内容が表示されます。これは実行フローのc-2が実行されていることを意味し、SCOPEが変更されたり、トークンの有効期限が切れない限り、一番最初の実行時にだけ行われます。この認証要求を許可すると、Googleカレンダーに登録されている直近10個のイベントがターミナルに表示されると思います。表示内容は省略していますので、ご自身の環境で実行し、確認してみてください。

python quickstart.py

なお、実行するとこのディレクトリにtoken.pickleというファイルが新たにできていることが確認できます。これが存在しかつ有効である限り、2回目以降ではブラウザが立ち上がって認証の許可を求められるフローは実行されません。また、定期的にこのtoken.pickleファイルも書き直され、持続的にアプリケーションを利用できるようになります。

6. PythonでのGoogle Client Libraryの使い方

Google Calendar APIをPythonスクリプトで実行する場合、Google Client Libraryを利用します。当初、このGoogle Client Libraryの使い方がよくわからず、かなり戸惑ったので、ここでその使い方を簡単にまとめてみようと思います。公式リファレンスはこちらになります。

Google Client Libraryは、Pythonスクリプトから様々んGoogle APIを呼び出すために必要とされるライブラリーです。Googleカレンダーだけでなく、GoogleタスクやYoutubeなども対象としており、守備範囲が非常に広いライブラリーです。対応しているAPIは、公式リファレンスのAPIsというタブで確認することができます。このライブラリーを使ってAPIを操作する方法は、構造がわかれば非常に簡単でした。

・クライアントライブラリーからAPIを操作するフロー
1. 対応するAPI用のインスタンスを作成
  ex) service = build('calendar', 'v3', credentials=creds)
2. インスタンスを使ってAPIから情報を取得
  ex) events_result = service.events().list(calendarId='primary', timeMin=now, singleEvents=True, orderBy='startTime').execute()

2のAPIから情報を取得するためのメソッドがどのような構造か当初よくわからなかったのですが、実はかなり単純な仕組みとなっています。

まず、serviceはAPIと接続するためのインスタンスです。そして、そのあとのevents、listはGoogle Calendar APIがもつメソッドで、この2つは大セグメント、小セグメントと形容できるような構成になっています。これは公式リファレンスの記載の仕方を見るとわかります。

公式リファレンスを見るとAclから始まってSettingまで記載されていますが、これがいわば大セグメントです。この中のEventsセグメントを見るとさらに細分化されて複数の項目が記載されていますが、これがいわば小セグメントになります。events().list()という記載は、大セグメント、小セグメントと記載したこの構造に一致させたものです。各大セグメントの概要欄でそのセグメントの機能と各小セグメントの機能を確認することができます。

小セグメントに相当するメソッドでは、基本的にカレンダーIDなどのパラメータの指定が必要になります。メソッドの必須パラメータやオプションパラメータは、各小セグメントのページで見ることができます。基本的には各パラメータで各値を指定する方法をとられていますが、freebusyのqueryメソッドのようにbodyという引数に辞書型のデータを渡すものもあります(参考ページ)。下記でfree_busyを用いたコード例を記載していますので公式リファレンスとともに確認してみてください。また、公式リファレンス上でもパラメータの指定方法などを試してみることができます。

このようにメソッドを実行することでAPIから情報を取得できますが、この状態だと情報を格納したオブジェクトを取得しただけになります。Pythonのデータ型に合わせてデータを取得するためには、さらにexecuteメソッドを実行する必要があります。executeメソッドを実行することで、辞書型データとして各小セグメントメソッドのページの”Response”部分に記載されたプロパティネームでアクセスすることで、各値を取得できるようになります。下記にコード例を書いていますので参考にして頂ければと思います。

service = build('calendar', 'v3', credentials=creds)
body = {
  "timeMin": "2019-03-28T09:00:00+09:00",
  "timeMax": "2019-03-28T09:00:00+09:00",
  "timeZone": "Asia/Tokyo",
  "items": [
    {
      "id": "aaaa@gmail.com"
    },
    {
      "id": "bbbb@gmail.com"
    }
  ]
}
free_busy = service.freebusy().query(body=body).execute()
# free_busyのデータ内容
{
#   "kind": "calendar#freeBusy",
#   "timeMin": datetime,
#   "timeMax": datetime,
#   "groups": {
#     (key): {
#       "errors": [
#         {
#           "domain": string,
#           "reason": string
#         }
#       ],
#       "calendars": [
#         string
#       ]
#     }
#   },
#   "calendars": {
#     (key): {
#       "errors": [
#         {
#           "domain": string,
#           "reason": string
#         }
#       ],
#       "busy": [
#         {
#           "start": datetime,
#           "end": datetime
#         }
#       ]
#     }
#   }
# }


7. まとめ

Google Calendar APIをPythonで利用する方法は以上になります。当初、かなり苦戦しましたが、構造がわかってしまえば、充実した公式リファレンスを参照しながら、簡単に使えるようになりました。Googleは様々なアプリを提供しているので、APIを利用することでアプリ間を連携して使い勝手の良いサービスを作ったり、業務を効率化することもできると思います。そういった時にこの記事の内容が参考になればと思います。この記事が役に立ちましたら幸いです。読んで頂き、ありがとうございました。