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?
- 書き込み・・・
読み込みで疲れたので、ここまで ^^;