Google Spreadsheet の中身をrubyで読み出してみた。

始めに、感想を。めんどくさい!誰か、よいライブラリを知っている方、教えてください!
メモ書き程度のドキュメントです。生のXMLをパースして読み出してます。
参考にしたのは、Goole Code FAQ - Using Ruby with the Google Data APIsです。

  • 心構え

Googleドキュメントの情報は、ドキュメント(Spreadsheet)一覧 - ドキュメント情報 - シート一覧 - シート情報 - セル一覧 - セル情報 という階層構造になっています。(Excelを扱うPOIに比べると、ドキュメント一覧の情報が増えている)このため、深い階層のXMLを扱う操作が必要です。

  • 準備

Mac OS X 10.5+MacPorts環境で試しています。
問い合わせの結果帰ってくるXMLを、扱いやすくするためにXmlSimpleを入れておきます。他のライブラリでも良いです。

% sudo gem install xml-simple

また、以下のサンプルコードでは、以下のコードをヘッダとしてくっつけてください。your_gmail_addressとyour_passwordは、gmailのアドレスとパスワードを入れてください。

#!/usr/bin/env ruby

require 'net/https'
require 'rubygems'
require 'xmlsimple'
require 'pp'

# constants
@email = "your_gmail_address"
@passwd = "your_password"
  • テーブルの作成

Google Spreadsheet に次の様なテーブルがあると仮定してます。

    • テーブル名:Address
    • シート名:PrimaryTable
ID Name Address
1 山田 太郎 東京都皇居1-1-1
2 川田 花子 東京都国会2-2-2

http://spreadsheets.google.com/pub?key=pu1uHhwsDA_jeOFtAF8LIIA

  • 認証をする

Google様に認証をします。変数headersに接続情報が入ります。

def authentication()
  # preparations
  http = Net::HTTP.new('www.google.com', 443)
  http.use_ssl = true
  path = '/accounts/ClientLogin'
  ## authentication data
  data = "accountType=HOSTED_OR_GOOGLE&Email=#{@email}&Passwd=#{@passwd}&service=wise"
  headers = { 'Content-Type' => 'application/x-www-form-urlencoded'}

  # Post the request to server
  resp, data = http.post(path, data, headers)
  
  # extract token
  cl_string = data[/Auth=(.*)/, 1]
  
  # extract headers
  headers["Authorization"] = "GoogleLogin auth=#{cl_string}"
  return headers
end

headers = authentication()
  • 全てのドキュメント(Sheet)の名前を取り出す。

@emailさんの保持しているSpreadsheetドキュメント(ブック or アイテム)一覧表を取り出します。取り出した結果の変数docには、取り出されるシートの順番が捕縄されていないので、取り出すたびに異なる順序で結果が帰ることがあります(もともとXMLは順番を保証していませんが)。注意。下に、全てのドキュメントのidを取り出して、タイトル名を表示する例を出します。

    • doc["totalResults"] に保持しているドキュメント数
    • doc["entry"]が、ドキュメント情報の配列
    • doc["entry"][要素番号]["title"][0]["content"] に「要素番号」のドキュメントのタイトルが入る。上の例のテーブルではAddress。下の例では、putsで全タイトルを表示。
    • doc["entry"][要素番号]["id"][0][/full\/(.*)/,1] で「要素番号」のドキュメントのid番号が取り出す。下の例では、全ドキュメントのidを取り出して、配列idに入れる。
def get_feed(url, headers=nil)
  uri = URI.parse(url)
  Net::HTTP.start(uri.host, uri.port) do |http|
    return http.get(uri.path, headers)
  end
end

def get_documentlist(headers)
  spreadsheets_uri = 'http://spreadsheets.google.com/feeds/spreadsheets/private/full'
  my_spreadsheets = get_feed(spreadsheets_uri, headers)

  doc = XmlSimple.xml_in(my_spreadsheets.body, 'KeyAttr'=>'name')
  return doc
end

doc = get_documentlist(headers)
ids = Array.new
doc["entry"].each do |entry|
  ids << entry["id"][0][/full\/(.*)/,1]
  puts entry["title"][0]["content"]
end
  • 指定したidのドキュメントに含まれるシートを得る

ドキュメント内のシートの情報を得るには、そのドキュメントのidが必要です。上のコードではidsという配列に全idが含まれます。ここでは、ids[0]にあるid番号のドキュメントの中を見ていきます。(名前で直接指定したりできるわけではないので、毎回違うドキュメントが入る可能性があり、実運用では注意が必要です)

下のコードでは、worksheet_dataには全シートのドキュメントの情報が入り、全シートのシート名を表示します。

    • worksheet_data["title"][0]["content"] :ドキュメント名。doc["entry"][要素番号]["title"][0]["content"]と同一。(上の例のテーブルではAddress)
    • worksheet_data["entry"][シート番号]["title"][0]["content"]:シート番号のシート名(上の例では、PrimaryTable)
    • worksheet_data["entry"][シート番号]["link"][0]["href"]:シート番号のデータのURI
def get_worksheet(id, headers)
  worksheet_feed_uri = "http://spreadsheets.google.com/feeds/worksheets/#{id}/private/full"
  worksheet_response = get_feed(worksheet_feed_uri, headers)
  worksheet_data = XmlSimple.xml_in(worksheet_response.body, 'KeyAttr'=>'name')

  return worksheet_data
end

worksheet_data = get_worksheet(ids[0], headers)
worksheet_data["entry"].each do |w|
  puts w["title"][0]["content"]
end
  • 指定したシート内のセルの情報を得る

シート(テーブル)内の値を指定する方法として、listで得る方法とcellで得る方法があるらしいが、listの方がよく分からなかったので、cellのみ。cellには、そのcellの位置の座標と値、更新日時が入っています。

    • cellfeed_doc["totalResults"][0]: 内容の含まれているセルの数
    • cellfeed_doc["colCount"][0]: シートの列数
    • cellfeed_doc["rowCount"][0]: シートの行数
    • cellfeed_doc["entry"][cell_number]["cell"]: cell_numberのセルの情報
      • cellfeed_doc["entry"][cell_number]["cell"]["col"]: cell_numberの列位置(川田 花子と書かれたcellは2)
      • cellfeed_doc["entry"][cell_number]["cell"]["row"]: cell_numberのセルの行位置(川田 花子と書かれたcellは3)
      • cellfeed_doc["entry"][cell_number]["cell"]["content"]: cell_numberのセルの値
def get_cells(worksheet_data, sheetno, headers)
  cellfeed_uri = worksheet_data["entry"][sheetno]["link"][1]["href"]
  response = get_feed(cellfeed_uri, headers)
  cellfeed_doc = XmlSimple.xml_in(response.body, 'KeyAttr'=>'name')

  return cellfeed_doc
end

cellfeed_doc = get_cells(worksheet_data, 0, headers)

puts cellfeed_doc["colCount"][0] unless cellfeed_doc["colCount"].nil?
puts cellfeed_doc["rowCount"][0] unless cellfeed_doc["rowCount"].nil?
  • 書き込み・・・

読み込みで疲れたので、ここまで ^^;