unoh.github.com

Google Spreadsheet APIを使ってみた

Sun Jun 06 18:08:40 -0700 2010

おはようございます、chihiroです。

弊社で開発している某ソーシャルゲームで、YAMLで書かれたゲームのマスターデータ(fixture)をExcelに出力するというタスクが生じました。

Excelにするならば、YAMLからCSVに変換して取り込むとか、あるいはExcelファイルを直接扱えるライブラリを使うといった方法が考えられます。しかし、今後プログラマではない他の社員にデータを編集してもらい、かつ編集したデータとfixtureの同期をとれるようにしておきたいところです。

そこで、以前どこかの勉強会で「管理ツールなんて自作する必要ない。Google Spreadsheet上で編集させて、必要に応じてAPIでアクセスすればいい」という話を聞いたことがあるのを思い出し、Google Spreadsheet APIを使ってGoogle Spreadsheetにデータを書き出すという方法を試してみました。Google Spreadsheetに書き出しておけばExcel形式でダウンロードできますし、ドキュメントの共有や権限管理も楽ですし、いいことずくめのように思えます。もちろん、会社の規約等でGoogleのサービスを使えないというケースはあるでしょうが・・・

そんなわけで、とりあえず書いてみたスクリプトが以下のものです。

# -*- coding: utf-8 -*-
import sys
import getpass
import yaml

# ドキュメントにはいろいろimportしろと書いてありますが、
# 今回のスクリプトのように単純なものならば
# gdata.spreadsheet.service.SpreadsheetsService をimportすれば事足ります
from gdata.spreadsheet.service import SpreadsheetsService

def main(email, password, document_name, sheet_name, data):
    # 使い捨てのスクリプトなので ClientLogin を使う
    gd_client = SpreadsheetsService()
    gd_client.email = email
    gd_client.password = password
    gd_client.ProgrammaticLogin()

    # 書き込み先のドキュメントを探す
    # 見つからない場合は例外を排出して処理を終了
    feed = gd_client.GetSpreadsheetsFeed()
    for entry in feed.entry:
        if entry.title.text == document_name:
            # SpreadsheetのIDは、
            # http://spreadsheets.google.com/feeds/spreadsheets/private/full/abcdefgh-ijklmnopqrstuv
            # のようなURLになります。API経由でアクセスするにはこのうちの最後の
            # "abcdefgh-ijklmnopqrstuv"をキーとして使います
            current_key = entry.id.text.split('/')[-1]
            break
    else:
        raise ValueError('document not found')

    # 書き込み先のシートを探す
    # 見つからない場合は例外を排出して処理を終了
    feed = gd_client.GetWorksheetsFeed(current_key)
    for entry in feed.entry:
        if entry.title.text == sheet_name:
            # sheetのIDは、
            # http://spreadsheets.google.com/feeds/worksheets/abcdefgh-ijklmnopqrstuv/private/full/od6
            # のようなURLになります。API経由でアクセスするには上のキーと共に
            # このワークシートIDを保持しておく必要があります
            current_worksheet_id = entry.id.text.split('/')[-1]
            break
    else:
        raise ValueError('sheet not found')

    # 1行目にカラム名をセットする
    # あらかじめドキュメントの1行目を変更しておけばこの処理は
    # 必要がありませんが、CellベースのAPIのサンプルとして
    for idx, value in enumerate(('id', 'name', 'description')):
        # Cellのrowとcolは(1, 1)からはじまります
        gd_client.UpdateCell(row=1,
                             col=(idx + 1),
                             inputValue=value,
                             key=current_key,
                             wksht_id=current_worksheet_id)

    # 列を追加
    for params in data:
        # 辞書のキーにしているid, name, descriptionは
        # 上で1行目のCellにセットした列名です
        # 指定した列名がないとListベースのAPIの呼び出しは失敗します
        entry = gd_client.InsertRow({ 'id'         : unicode(params['id']),
                                      'name'       : unicode(params['name']),
                                      'description': unicode(params['description']),
                                      },
                                    current_key,
                                    current_worksheet_id)


if __name__ == '__main__':
    if len(sys.argv) != 5:
        print 'usage %s email document_name sheet_name filename' % sys.argv[0]
        sys.exit(1)

    email, document_name, sheet_name, filename = sys.argv[1:]
    password = getpass.getpass('Password for %s: ' % email)

    data = yaml.load(open(filename))
    main(email, password, document_name, sheet_name, data)

以前のエントリで紹介したようにpipをインストールした上で、

$ pip install gdata PyYAML

のようにgdataとPyYAMLをインストールし、

$ python gdata-insert.py user@example.com document-name sheet-name fixture.yml

のようにして使います。fixture.ymlは、

- id: 1
  name: アナキン
  description: "アナキン・スカイウォーカー。後のダース・ベイダー。"
- id: 2
  name: ルーク
  description: "オビ=ワン・ケノービの弟子。"
- id: 3
  name: オビ=ワン・ケノービ
  description: "ルークの師匠。"

のようなYAMLになっています。

エラー処理はまったくなしですが、使い捨てのスクリプトなので勘弁していただき、Google Spreadsheet APIを利用するサンプル程度にお考えください。Google Spreadsheet APIに関しては、以下のURLに公式のドキュメントがあります。