Go で書いたCLIのプログラムから Google Sheets API v4 使ってスプレッドシートを操作したときのメモ。
使用した Go のバージョンは 1.6 です。
サービスアカウント
- Google Developer Console でプロジェクトを作成する
- API Manager の「ライブラリ」からGoogle Sheets API を有効にする
- API Manager の「認証情報」からサービスアカウントを作成する。作成すると秘密鍵が書かれたJSONファイルをダウンロードできるので大事に取っておく
スプレッドシート
- スプレッドシートを作成する。URLからスプレッドシートIDがあるので、メモしておく。
https://docs.google.com/spreadsheets/d/<スプレッドシートID>/
- スプレッドシートの共有設定から、サービスアカウントのメールアドレス指定して、サービスアカウントからのアクセスを許可する。
Go からスプレッドシートにアクセス
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/sheets/v4"
)
func httpClient(credentialFilePath string) (*http.Client, error) {
data, err := ioutil.ReadFile(credentialFilePath)
if err != nil {
return nil, err
}
conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/spreadsheets")
if err != nil {
return nil, err
}
return conf.Client(oauth2.NoContext), nil
}
func main() {
spreadsheetId := "スプレッドシートのURLから抽出したID"
credentialFilePath := "秘密鍵のJSONファイルのパス"
client, err := httpClient(credentialFilePath)
if err != nil {
log.Fatal(err)
}
sheetService, err := sheets.New(client)
if err != nil {
log.Fatalf("Unable to retrieve Sheets Client %v", err)
}
_, err = sheetService.Spreadsheets.Get(spreadsheetId).Do()
if err != nil {
log.Fatalf("Unable to get Spreadsheets. %v", err)
}
fmt.Printf("success!\n")
}
go run main.go
で実行。ここで403エラーが発生する場合は、スプレッドシートにサービスアカウントからのアクセスが許可されていない。
スプレッドシートを操作
Google Sheets API v4 のリファレンスはこちら。
以下の3つに分類されている。
- spreadsheets
- spreadsheets.sheets
- spreadsheets.values
Go では以下のように対応している
sheetService.Spreadsheets
sheetService.Spreadsheets.Sheets
sheetService.Spreadsheets.Values
Go のリファレンスはこちら。
GET /v4/spreadsheets/{spreadsheetId}
例えば、 spreadsheets の get (GET /v4/spreadsheets/{spreadsheetId}) を使いたい場合、リファレンス を見てみると、パスパラメータとして spreadsheetId
が必要なことが分かる。
Go で書くとこうなる。 sheetService.Spreadsheets.Get(spreadsheetId)
で SpreadsheetsGetCall
を取得できるので、それの Do()
メソッドを呼べば実際にAPIを呼ぶ。
sheetService.Spreadsheets.Get(spreadsheetId).Do()
PUT /v4/spreadsheets/{spreadsheetId}/values/{range}
spreadsheets.values の update ( PUT /v4/spreadsheets/{spreadsheetId}/values/{range} ) を使いたい場合、リファレンスを見ると、パスパラメータとして spreadsheetId
と range
が必要なことが分かる。クエリパラメータとして valueInputOption があって、リクエストボディとして ValueRange が必要なことも分かる。
Go で書くとこうなる。
sheetService.Spreadsheets.Values.Update(spreadsheetId, targetRange, valueRange).ValueInputOption("USER_ENTERED").Do()
引数の targetRange
はただの文字列で、 "シート名!A1:B1"
というような書式。 valueRange
は struct でドキュメントに詳しく書いてある -> https://godoc.org/google.golang.org/api/sheets/v4#ValueRange
何か操作してみる
行を挿入してから、以下のように書き込んでみる。
hoge | 1 |
---|---|
fuga | 2 |
- A2 = hoge
- B2 = 1
- A3 = fuga
- B3 = 2
行の挿入はAPIリファレンスを見ているとなさそうに思えるが、 POST /v4/spreadsheets/{spreadsheetId}:batchUpdate で色々できる。
Go のコードはこんな感じ。1行目に挿入することはできなかったので、2行目から2行分挿入して、データを書き込んでいる。
package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/sheets/v4"
)
func httpClient(credentialFilePath string) (*http.Client, error) {
data, err := ioutil.ReadFile(credentialFilePath)
if err != nil {
return nil, err
}
conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/spreadsheets")
if err != nil {
return nil, err
}
return conf.Client(oauth2.NoContext), nil
}
func sheetId(s *sheets.Spreadsheet, sheetName string) (int64, error) {
for _, sheet := range s.Sheets {
if sheet.Properties.Title == sheetName {
return sheet.Properties.SheetId, nil
}
}
return 0, errors.New(sheetName + " is not exists.")
}
func insertRow(sheetService *sheets.Service, spreadsheetId string, sheetId int64) (*sheets.BatchUpdateSpreadsheetResponse, error) {
req := sheets.Request{
InsertDimension: &sheets.InsertDimensionRequest{
InheritFromBefore: false,
Range: &sheets.DimensionRange{
Dimension: "ROWS",
StartIndex: 1,
EndIndex: 3,
SheetId: sheetId,
},
},
}
insertRowReq := sheets.BatchUpdateSpreadsheetRequest{
Requests: []*sheets.Request{&req},
}
return sheetService.Spreadsheets.BatchUpdate(spreadsheetId, &insertRowReq).Do()
}
func main() {
spreadsheetId := "スプレッドシートのURLから抽出したID"
credentialFilePath := "秘密鍵のJSONファイルのパス"
sheetName := "シート1"
client, err := httpClient(credentialFilePath)
if err != nil {
log.Fatal(err)
}
sheetService, err := sheets.New(client)
if err != nil {
log.Fatalf("Unable to retrieve Sheets Client %v", err)
}
spreadsheet, err := sheetService.Spreadsheets.Get(spreadsheetId).Do()
if err != nil {
log.Fatalf("Unable to get Spreadsheets. %v", err)
}
sheetId, err := sheetId(spreadsheet, sheetName)
if err != nil {
log.Fatal(err)
}
_, err = insertRow(sheetService, spreadsheetId, sheetId)
if err != nil {
log.Fatalf("Unable to insert row. %v", err)
}
valueRange := &sheets.ValueRange{
MajorDimension: "ROWS",
Values: [][]interface{}{
[]interface{}{"hoge", 1},
[]interface{}{"fuga", 2},
},
}
_, err = sheetService.Spreadsheets.Values.Update(spreadsheetId, sheetName+"!A2:B3", valueRange).ValueInputOption("USER_ENTERED").Do()
if err != nil {
log.Fatalf("Unable to write value. %v", err)
}
fmt.Printf("success!\n")
}