風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

【覚書】Google Fusion Tables に Python でアクセスする手順

Google Fusion Tables の更新は、実際には、自前のLinuxサーバー等から自動的に行うことを考えているため、まずは Linux 端末上から Fusion Tables にアクセスできる方法を調べる必要がある。
この場合、Linux 端末側は、OAuth 2.0 クライアントとして Fusion Tables API を使用することになるらしい。developers.google.com
で公開されている各プログラミング言語用のライブラリを用いることで、アクセスできるのだと思われる。
とりあえず、ある程度使い慣れている Python 2.7 によるアクセスを行うことを試みた。
いろいろと調べつつ、手探りで試してとりあえずアクセスは出来たが、これが正しい方法なのかどうかは自信がない。もっとスマートなやり方がありそうな気がするが……。

OAuth 2.0 クライアントID&シークレットの取得

Google Developers Consoleを開き、Fusion Tables へのアクセス用に、新しいプロジェクトを作成する。
f:id:furyu-tei:20150819212036p:plain
f:id:furyu-tei:20150819212043p:plain
f:id:furyu-tei:20150819212048p:plain


作成したプロジェクトを開き、
f:id:furyu-tei:20150819212051p:plain
左のメニューの
APIと認証 → API
から、Fusion Table API を選んで
f:id:furyu-tei:20150819212054p:plain
[APIを有効にする]を押す。
f:id:furyu-tei:20150819212100p:plain


次に、同じく
APIと認証 → 認証情報
から、
[認証情報を追加] → OAuth 2.0 クライアント ID
を選択し、
f:id:furyu-tei:20150819212111p:plain
[同意画面を設定]ボタンを押して
f:id:furyu-tei:20150819212117p:plain
サービス名を適当につけて入力し、[保存]を押す。
f:id:furyu-tei:20150819212123p:plain


「アプリケーションの種類」は「○ その他」を選択し、[作成]ボタンを押すと、
f:id:furyu-tei:20150819212128p:plain
OAuth 2.0 のクライアントID及びクライアントシークレットが発行されるので、[OK]を押す。
これは後からでも確認できるため、特にコピーしておく必要などはない。
f:id:furyu-tei:20150819212142p:plain

OAuth 2.0 クライアントID情報は、右側にあるダウンロードボタンにて、JSON形式でダウンロードできる。
f:id:furyu-tei:20150819212153p:plain


Linux 端末(OAuth 2.0 クライアント)側の準備

Installation  |  API Client Library for Python  |  Google Developers
に従い、

sudo pip install --upgrade google-api-python-client

のようにして、google-api-python-client をインストールしておく。


Python で Fusion Tables にアクセスするサンプルコード(sample_fusiontables.py)

上記でダウンロードしたJSON(OAuth 2.0 クライアントID情報)ファイルを、
client_secrets.json
という名前にして同じディレクトリに配置した状態で実行すると、アクセス可能な Fusion Tables の最初のテーブルIDを取得し、当該テーブルの行列を表示するサンプル。

# -*- coding: utf-8 -*-

# OAuth 2.0 for Devices (sample code)
# https://github.com/google/oauth2client/blob/master/samples/oauth2_for_devices.py

# Using OAuth 2.0 for Devices
# https://developers.google.com/accounts/docs/OAuth2ForDevices

# Fusion Tables REST API
# https://developers.google.com/fusiontables/docs/v2/reference/

from __future__ import print_function

import sys
import json
import time
import datetime
import httplib2
import traceback
from pprint import pprint
from oauth2client import client
from googleapiclient.discovery import build

def get_credentials(scopes, client_secret_file = None, credential_file_in = None, credential_file_out = None): #{
  def load_credential_file(credential_file_in):
    if not credential_file_in:
      return None
    try:
      fp = open(credential_file_in, 'rb')
      credentials = client.OAuth2Credentials.new_from_json(fp.read())
      fp.close()
      if credential_file_in != credential_file_out:
        save_credential_file(credential_file_out, credentials)
      return credentials
    except Exception, error:
      #print(traceback.format_exc())
      pass
    return None
    
  def save_credential_file(credential_file_out, credentials):
    if not credential_file_out:
      return
    fp = open(credential_file_out, 'wb')
    fp.write(credentials.to_json())
    fp.close()
  
  credentials = load_credential_file(credential_file_in)
  if credentials:
    return credentials
  
  client_secrets = json.load(open(client_secret_file, 'rb'))
  client_id = client_secrets['installed']['client_id']
  client_secret = client_secrets['installed']['client_secret']
  
  while not credentials:
    flow = client.OAuth2WebServerFlow(client_id, client_secret, ' '.join(scopes))
    
    # Step 1: get user code and verification URL
    # https://developers.google.com/accounts/docs/OAuth2ForDevices#obtainingacode
    flow_info = flow.step1_get_device_and_user_codes()
    
    print('Verification URL: {0}'.format(flow_info.verification_url));
    print('User code: {0}'.format(flow_info.user_code));
    
    user_code_expiry = flow_info.user_code_expiry
    interval = flow_info.interval
    
    while datetime.datetime.now() < user_code_expiry:
      print('\r{0:>4} seconds remaining'.format( (user_code_expiry - datetime.datetime.now()).seconds ), end = '')
      sys.stdout.flush()
      # Step 2: get credentials
      # https://developers.google.com/accounts/docs/OAuth2ForDevices#obtainingatoken
      try:
        credentials = flow.step2_exchange(device_flow_info = flow_info)
        if credentials:
          break
      except Exception, error:
        #print(traceback.format_exc())
        credentials = None
      time.sleep(interval)
    print('')
  
  save_credential_file(credential_file_out, credentials)
  
  return credentials
#} // end of get_credentials()


if __name__ == '__main__': #{
  # https://console.developers.google.com/project/<project name>/apiui/credential (Download JSON)
  CLIENT_SECRET_FILE = 'client_secrets.json'
  
  # Allowed scopes
  # https://developers.google.com/identity/protocols/OAuth2ForDevices#allowedscopes
  SCOPES = ('https://www.googleapis.com/auth/fusiontables',)
  
  CREDENTIAL_FILE = 'credentials.json'
  
  credentials = get_credentials(
    client_secret_file = CLIENT_SECRET_FILE,
    scopes = SCOPES,
    credential_file_in = CREDENTIAL_FILE,
    credential_file_out = CREDENTIAL_FILE,
  )
  
  print('Access token: {0}'.format(credentials.access_token))
  print('Refresh token: {0}'.format(credentials.refresh_token))
  
  # Get Fusion Tables service
  # https://developers.google.com/accounts/docs/OAuth2ForDevices#callinganapi
  fusiontables = build(
    serviceName = 'fusiontables',
    version = 'v2',
    http = credentials.authorize( httplib2.Http() ),
  )
  
  # Fusion Tables REST API
  # https://developers.google.com/fusiontables/docs/v2/reference/
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Table
  Table = fusiontables.table()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Column
  Column = fusiontables.column()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Template
  Template = fusiontables.template()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Style
  Style = fusiontables.style()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Query
  Query = fusiontables.query()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Task
  Task = fusiontables.task()
  
  # ===== Samples
  print('***** Tables')
  table_list = Table.list().execute()
  pprint(table_list)
  tableId = table_list['items'][0]['tableId']
  
  print('***** Columns & Rows')
  rows = Query.sql(
    sql = 'SELECT * FROM {0}'.format(tableId)
  ).execute()
  pprint(rows)

Shell 上から、

$ ls
client_secrets.json  sample_fusiontables.py
$ python ./sample_fusiontables.py

のように実行すると、初回には、標準出力上に

Verification URL: https://www.google.com/device
User Code: XXXX-XXXX
xxxx seconds remaining

のように表示されて待ち状態に入る。

手動で「Verification URL」に示された URL にブラウザでアクセスすると、(場合によってはアカウント選択・ログイン画面の後で)端末に表示されるコードの入力を促されるため、「User Code」で示されたコードを入力し、[続行]を押す。
f:id:furyu-tei:20150819212158p:plain
Fusion Tables の管理の許可を求められるため、[承認する]を押す。
f:id:furyu-tei:20150819212207p:plain


すると、しばらくしてサンプルプログラム側が承認を認識し(Access token・Refresh tokenを取得し)、結果が表示される。


なお、Access token・Refresh tokenを含む情報は、カレントディレクトリ上の
credentials.json
というファイルに書き込まれ、

$ ls
client_secrets.json  credentials.json  sample_fusiontables.py

次回実行時にはこれを読み込んで使用するため、基本的には、上記のブラウザによる手動の承認手順は初回のみでよい。
この動作の詳細は、上記ソースコード中の get_credentials() 関数の定義及び呼び出し箇所を参照のこと。