GoでGoogle Sheets APIを利用する【認証/セルの参照】

## 経緯

業務でGoogle Sheets APIを使用する機会があったので、Go言語での認証及び値の参照についてまとめてみました。
Go言語、Google Sheets APIは学習途中なので間違った記述、非推奨なコードの書き方があった場合、ご指摘いただければ幸いです。



## 事前準備

事前準備として下記の作業を実施します。
実行環境で下記のコマンドを実行し、パッケージをインストールします。

```shell
go get -u golang.org/x/net/context
go get -u golang.org/x/oauth2
go get -u google.golang.org/api/sheets/v4
```



以下の作業は詳細を割愛し、作業の流れのみ記載させていただきます。

1. GCPにログイン
2. 任意のプロジェクトを作成
3. 作成したプロジェクトでGoogle Sheets API を有効化
4. サービスアカウントを作成（ロールは選択しなくて大丈夫です）
5. JSON形式で秘密鍵をダウンロード
6. 使用するスプレッドシートをサービスアカウントと共有する



## サービスアカウントでの認証

### サンプルコード

下記のコードで認証を行います。 
サンプルコードでは、エラーハンドリングを省略しております。


```go
pakage main

import (
	"golang.org/x/oauth2"
    "golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/api/sheets/v4"
)

func main() {
	conf := &jwt.Config{
		// サービスアカウントのEメールアドレス
		Email:        "",
		// サービスアカウントのプライベート鍵
		PrivateKey:   []byte(""),
		TokenURL:     google.JWTTokenURL,
		Scopes: []string{
			"https://www.googleapis.com/auth/spreadsheets.readonly",
		},
	}
    
	client := conf.Client(oauth2.NoContext)
	sheetsSrv, _ := sheets.New(client)
}
```



### jtw.Config

上記コードを実行する際は、事前準備でダウンロードしたJSONファイルに記載されている`client_email`、`private_key`を`jwt.Config`の値を`Email`、`PrivateKey`に追記します。
`jwt.Config`の`Scopes`ではスプレッドシートに対する権限を指定しています。

| URL                                                          | 権限                                |
| :----------------------------------------------------------- | :---------------------------------- |
| https&#58;//www&#046;googleapis.com/auth/spreadsheets.readonly | 読み取り専用でのアクセスを許可      |
| https&#58;//www&#046;googleapis.com/auth/spreadsheets        | 読み取り/書き込みでのアクセスを許可 |



### 補足

動作確認ではなく実運用では、コードに秘密鍵やメールアドレスを埋め込むのではなく下記のようにダウンロードしたJSONファイルを同じパスに配置し読み込むなどで実装したほうが望ましいと思います。

```go
package main

import (
	"encoding/json"
	"io/ioutil"

	"golang.org/x/oauth2"
    "golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/api/sheets/v4"
)

type credJson struct {
	Email      string `json:"client_email"`
	PrivateKey string `json:"private_key"`
}

func main() {
    // ダウンロードしたJSONファイルの名前はcredentials.jsonとしています。
    // 必要に応じて任意のファイル名に変更してください。
	bytes, _ := ioutil.ReadFile("credentials.json")
	var cred credJson
	json.Unmarshal(bytes, &cred)

	conf := &jwt.Config{
		Email:      cred.Email,
		PrivateKey: []byte(cred.PrivateKey),
		TokenURL:   google.JWTTokenURL,
		Scopes: []string{
			"https://www.googleapis.com/auth/spreadsheets.readonly",
		},
	}

	client := conf.Client(oauth2.NoContext)
	sheetsSrv, _ := sheets.New(client)
}
```



## セルの値を参照

### サンプルコード

参照するスプレッドシートは下記のように書き込んでおります。

|      |   A   |     B     |
| :--: | :---: | :-------: |
|  1   | hoge1 | hogehoge1 |
|  2   | hoge2 | hogehoge2 |
|  3   | hoge3 | hogehoge3 |



参照する際は、`Spreadsheets.Values.Get()`を使用します。
実装する際は下記のコードのような形で実装します。
サンプルコードでは、エラーハンドリングを省略しております。

```go
package main

import (
	"fmt"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/api/sheets/v4"
)

func main() {
	conf := &jwt.Config{
		// サービスアカウントのEメールアドレス
		Email:        "",
		// サービスアカウントのプライベート鍵
		PrivateKey:   []byte(""),
		TokenURL:     google.JWTTokenURL,
		Scopes: []string{
			"https://www.googleapis.com/auth/spreadsheets.readonly",
		},
	}
    
	client := conf.Client(oauth2.NoContext)
	sheetsSrv, _ := sheets.New(client)
    
	// スプレッドシートの指定
	spreadsheetId := ""
    // 参照範囲を指定
	valueRange := "Sheet1!A1:B3"

	ctx := context.Background()

    // セルの値を参照
	resp, _ := sheetsSrv.Spreadsheets.Values.Get(spreadsheetId, valueRange).Context(ctx).Do()

	for _, row := range resp.Values {
		fmt.Printf("%s, %s\n", row[0], row[1])
	}
}
```



実行結果

```
hoge1, hogehoge1
hoge2, hogehoge2
hoge3, hogehoge3
```



### スプレッドシートID

`Spreadsheets.Values.Get("スプレッドシートID", "範囲")`で指定するスプレッドシートIDは使用するスプレッドシートのURLから取得することができます。

https&#58;//docs.google.com/spreadsheets/d/**<u>hogehoge</u>**/edit#gid=0

上記の`hogehoge`の部分がスプレッドシートIDとなります。
なお、`#gid=`以降の部分は各シートのIDとなります。



### 参照する範囲

`Spreadsheets.Values.Get("スプレッドシートID", "範囲")`で参照する範囲は下記のような記述ができます。

* "シート名"：シート全体を範囲とします。
* "シート名!A1:B3"：シートのA1からB3までを参照する範囲とします。



### クエリパラメータ

`Spreadsheets.Values.Get()`のクエリパラメータは下記の３つあります。

* majorDimension
* valueRenderOption
* dateTimeRenderOption



#### majorDimension

このクエリパラメータでは、行で参照するか列で参照するかを指定できます。
デフォルトは`ROWS`です。

| 設定値  | 説明               |
| ------- | ------------------ |
| ROWS    | 行から参照します。 |
| COLUMNS | 列から参照します。 |



サンプルコードでmajorDimensionを使用する場合は下記のように修正します。

```go
// 35行目
resp, _ := sheetsSrv.Spreadsheets.Values.Get(spreadsheetId, valueRange).MajorDimension("COLUMNS").Context(ctx).Do()
```



実行結果

```
hoge1, hoge2
hogehoge1, hogehoge2
```



#### valueRenderOption

このクエリパラメータでは、数式が記述されているセルの値について指定することができます。
デフォルトは`FORMATTED_VALUE`です。

| 設定値            | 説明                                                         |
| ----------------- | ------------------------------------------------------------ |
| FORMATTED_VALUE   | セルの値は数式の計算結果になります。加えてセルの表示形式が反映されます。<br />取得した値はstring型となります。 |
| UNFORMATTED_VALUE | セルの値は数式の計算結果になります。セルの表示形式は反映されません。<br />取得した値は数値の場合はfloat64型、文字列はstring型となります。 |
| FORMULA           | セルの値は数式のままとなります。セルの表示形式は反映されません。<br />取得した値は数値の場合はfloat64型、数式と文字列はstring型となります。 |



サンプルコードでvalueRenderOptionを使用する場合は下記のように修正します。（ここでは設定値として`UNFORMATTED_VALUE`を指定します。）

```go
// 35行目
resp, _ := sheetsSrv.Spreadsheets.Values.Get(spreadsheetId, valueRange).ValueRenderOption("UNFORMATTED_VALUE").Context(ctx).Do()
// 38行目
fmt.Printf("%f, %f\n", row[0], row[1])
```



スプレッドシートを下記に変更します。（A1の表示形式は通貨に設定します。）

|      |  A   |  B   |
| :--: | :--: | :--: |
|  1   | 12.3 | =A1  |



実行結果

```
12.300000, 12.300000
```



#### dateTimeRenderOption

このクエリパラメータでは、表示形式が日付や時刻になっているセルの値について指定できます。
注意事項としてvalueRenderOptionが`FORMATTED_VALUE`の場合はこのクエリパラメータは無視されます。
デフォルトは`SERIAL_NUMBER`です。

| 設定値           | 説明                                                         |
| ---------------- | ------------------------------------------------------------ |
| SERIAL_NUMBER    | 日付や時刻の値をシリアル番号で取得します。シリアル番号とは1899年12月30日から日時をカウントした値です。整数は日数を表し、小数点は余りの時間を表しています。<br />この設定値を指定して取得した日付や時刻の値はfloat64型となります。 |
| FORMATTED_STRING | 日付や時刻の値を表示形式の値で取得します。<br />この設定値を指定して取得した日付や時刻の値はstring型となります。 |



サンプルコードでdateTimeRenderOptionを使用する場合は下記のように修正します。（ここでは設定値として`SERIAL_NUMBER`を指定します。）

```go
// 35行目
resp, _ := sheetsSrv.Spreadsheets.Values.Get(spreadsheetId, valueRange).ValueRenderOption("UNFORMATTED_VALUE").DateTimeRenderOption("SERIAL_NUMBER").Context(ctx).Do()
// 38行目（SERIAL_NUMBERのみ）
fmt.Printf("%f, %f\n", row[0], row[1])
```



スプレッドシートを下記に変更します。（表示形式はA1が日付、B1を時間に設定します。）

|      |     A      |    B     |
| :--: | :--------: | :------: |
|  1   | 1899/12/31 | 14:59:32 |



実行結果

```
1.000000, 0.624676
```



## 参考資料

サービスアカウントでの認証

[package google](https://godoc.org/golang.org/x/oauth2/google)

セルの値を参照

[Go Quickstart](https://developers.google.com/sheets/api/quickstart/go)

[Method: spreadsheets.values.get](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get)

[Dimension](https://developers.google.com/sheets/api/reference/rest/v4/Dimension)

[ValueRenderOption](https://developers.google.com/sheets/api/reference/rest/v4/ValueRenderOption)

[DateTimeRenderOption](https://developers.google.com/sheets/api/reference/rest/v4/DateTimeRenderOption)