Python

Pythonを使ってGoogleカレンダーの予定を取得する方法

こんにちは!Pythonに関する情報を日々発信しているWolf-Code(@wolf-code-p)です。

私は、株式シミュレーションの結果をTwiiterに自動投稿をするなどしています。(@stock-simulate)

本記事では、Googleカレンダーの予定をPythonで取得する方法について紹介したいと思います。

こちらを参考にして本記事は作成しています。

前提条件

以下の「quickstart.py」を実行するには以下の条件が必要となるので注意してください。

  1. Python 2.6以上
  2. pip パッケージ管理ツール
  3. Googleカレンダーを有効にしたGoogleアカウント

GoogleカレンダーAPIをオンにする

こちらのページの「Enable the Google Calendar API(訳:Google Calendar APIを有効にする)」ボタンをクリックしてCloud Platformプロジェクトを新規作成し、Google Calendar APIを自動的に有効化します。

基本的に全てデフォルトの選択で構いません。

ダイアログが表示されたら、「DOWNLOAD CLIENT CONFIGURATION(訳:クライアント設定のダウンロード)」をクリックして、「Credentials.json」というファイルを作業ディレクトリに保存します。

Googleクライアントライブラリのインストール

以下のコマンドを実行して、pipを使ってライブラリをインストールします。

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

サンプルの作成

作業ディレクトリに quickstart.py というファイルを作成し、以下のコードをコピーします。

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

# これらのスコープを変更する場合は、token.pickleファイルを削除してください.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']


def main():
    # Google Calendar API の基本的な使い方を表示します。
    # ユーザーのカレンダーの、次の10件のイベントの開始日と名前を表示します。
    creds = None
    # token.pickleファイルは、ユーザーのアクセストークンとリフレッシュトークンを格納しており、
    # 初めて認証フローが完了したときに自動的に作成されます。

    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # 利用可能な(有効な)資格情報がない場合, ユーザをログインさせます.
    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)
        # 次回の実行のために資格情報を保存します
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)

    service = build('calendar', 'v3', credentials=creds)

    # カレンダーAPIを呼び出す
    # 'Z' は UTC 時間を示します。
    now = datetime.datetime.utcnow().isoformat() + 'Z'
    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', [])

    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()

サンプルの実行

以下のコマンドでサンプルを実行します。

python quickstart.py

サンプルはデフォルトのブラウザで新しいウィンドウまたはタブを開こうとします。失敗した場合は、コンソールからURLをコピーし、ブラウザで手動で開きます。

Google アカウントにログインしていない場合は、ログインするように促されます。複数の Google アカウントにログインしている場合は、認証に使用するアカウントを 1 つ選択するよう求められます。

承諾ボタンをクリックします。
サンプルが自動的に進みますので、ウィンドウ/タブを閉じても構いません。

応用編

おそらく、今回のプログラムを実行するとプライマリカレンダー(現在ログインしているユーザ)のメインカレンダーのこれからの予定が表示されます。

これを

  • 指定したカレンダーで
  • 期間を指定して

予定を抜き出してみましょう。

指定したカレンダーで予定を取得する

今取得した予定の具体的な内容はは41行目に書かれています。抜き出すと

calendarId='primary', 
timeMin=now,
maxResults=10, 
singleEvents=True,
orderBy='startTime'

となっています。それぞれ説明は

calendarId現在ログインしているユーザのプライマリカレンダーにアクセスしたい場合は、”primary” キーワードを使用します。
timeMinフィルタリングするイベントの終了時刻の下限
maxResults1 つの結果ページに返されるイベントの最大数。
singleEvents定期的に発生するイベントをインスタンスに展開し、単一の単発イベントと定期的に発生するイベントのインスタンスのみを返すが、基礎となる定期的なイベント自体は返さないかどうか。
orderBy結果で返されるイベントの順序。

となっています。(こちら参照)

なので、プライマリカレンダー以外にアクセスする場合はカレンダーIDを指定する必要があります。

カレンダーIDを確認するには

  1. Googleカレンダーにアクセス
  2. 設定に移動
  3. 取得したいカレンダーを選択
  4. 設定の下のほうにあるカレンダー ID(○○@group.calendar.google.com)を確認

で可能です。
これを先ほどの

calendarId='primary'

のprimaryの部分に置き換えればOKです。

もし、取得したいカレンダーが多いのならば以下のメソッドで一覧を取得することが可能です。

page_token = None
while True:
  calendar_list = service.calendarList().list(pageToken=page_token).execute()
  for calendar_list_entry in calendar_list['items']:
    print calendar_list_entry['summary']
    print calendar_list_entry['id']
  page_token = calendar_list.get('nextPageToken')
  if not page_token:
    break

こちらを参考)

こちらでIDを取得し、For文などで回して予定を取得することも可能です。

指定した期間で予定を取得する

指定した期間で予定を取得するには、さきほどのtimeMinとtimeMaxを指定すればOKです。ただし、datetimeで指定はできないので以下のようにisoformatで記述します。

例えば、1週間先の予定まで表示したいのであれば以下のようになります。

start = datetime.datetime.utcnow().isoformat() + 'Z'
end = (datetime.datetime.today() + datetime.timedelta(days=7)).isoformat() + 'Z'

これを先ほどのtimeMinとtimeMaxに代入すればOKです。
最後に、予定は時刻が入っている場合isoformatで返されます。
それを、Datetime形式で取得するには

start = event['start'].get('dateTime', event['start'].get('date'))
end = event['end'].get('dateTime', event['end'].get('date'))
try:
 # 終日の予定はこっち
 start = datetime.date.fromisoformat(start)
except ValueError:
 # 終日ではない予定はこっち
 # 日本の時間に合わせるために9時間足している
 start = datetime.datetime.fromisoformat(start.replace('Z','')) + datetime.timedelta(hours=9)
 end = datetime.datetime.fromisoformat(end.replace('Z','')) + datetime.timedelta(hours=9)

となっています。