使用Go语言获取Google表格信息
背景 -> 背景资料
你好,我是@harhogefoo。
我认为您可能有使用Google Apps Script获取电子表格信息并执行某些操作的需求。然而,GAS无法进行并行处理。因此,如果要引用多个电子表格进行操作,执行时间可能会很长。
所以,这次我们使用Golang的groutine来整理同时获取电子表格信息的方法。
通过使用服务账号,可以访问到电子表格的信息。
建議事先閱讀官方文件的“快速入門”章節,以取得必要的背景知識。
https://developers.google.com/sheets/api/quickstart/go
該連結中所提及的內容可以總結如下:
-
- Google APIsのSpreadsheet APIを有効化し、OAuthクライアントIDを発行
-
- このIDを利用してプログラムからスプレッドシートにアクセス
- プログラム実行時にブラウザが立ち上がりGoogleアカウントログインを求められ、ログインをすると認証情報がファイルに保存される。これ以降は認証が不要
不过,这次我希望跳过认证流程。
我们采用了使用服务帐号来获取Google表格信息的方法。这个方法已经总结在下面的链接中,您可以参考这个链接来快速实现从创建服务帐号到访问表格的过程。
https://qiita.com/bati11/items/a4cd922149dac07981bc
为了参考,我会提供获取访问Google表格的客户端的代码。
import "golang.org/x/oauth2/google"
func getHTTPClient(credentialsJSONData []byte) (*http.Client, error) {
conf, err := google.JWTConfigFromJSON(credentialsJSONData, "https://www.googleapis.com/auth/spreadsheets")
if err != nil {
return nil, errors.WithStack(err)
}
return conf.Client(oauth2.NoContext), nil
}
以下是OAuth2 / Google文档的链接↓
https://godoc.org/golang.org/x/oauth2/google
使用 Spreadsheet API 从电子表格中获取单元格信息。
利用 Spreadsheet API 进行访问电子表格信息有三种方法。虽然这些方法已在以下总结,但为了写明选择的原因,在本文中也会整理。
使用spreadsheets.get方法,并添加includeGridData选项来获取数据。
获取包括所有单元格信息的电子表格数据。如果不添加includeGridData选项,将返回没有单元格信息的数据。将其转化为具体的Golang代码。
import "google.golang.org/api/sheets/v4"
func main() {
client, _ := getHTTPClient() // ここは先述のサービスアカウント情報を使ってクライアントを取得する
service, _ := sheets.New(client)
resp, _ := service.Spreadsheets.Get(spreadsheetID).IncludeGridData(true).Do()
// ごにょごにょ
}
获取SpreadsheetsService.Get的文档如下↓
https://godoc.org/google.golang.org/api/sheets/v4#SpreadsheetsService.Get
请使用“spreadsheets.values.get“从中获取。
通过指定电子表格中的工作表和范围来获取数据。
import "google.golang.org/api/sheets/v4"
func main() {
client, _ := getHTTPClient(credentialsJSONData)
service, _ := sheets.New(client)
rangeStr := "シート名" // シート名!A1:A99 のような記述をすれば範囲の指定も可能
resp, _ := service.Spreadsheets.Values.Get(spreadsheetID, rangeStr).IncludeGridData(true).Do()
// ごにょごにょ
}
下面是SpreadsheetValuesService.Get的文档链接:https://godoc.org/google.golang.org/api/sheets/v4#SpreadsheetsValuesService.Get。
以 `spreadsheets.values.batchGet` 获取
使用gRPC语法可以从电子表格中获取多个工作表的信息。
我尚未进行尝试,因此没有样本代码。
本次采用的单元信息获取方式
我们选择了使用 spreadsheets.get 加上 includeGridData 选项进行获取。
不採用2的原因
-
- シートごとにリクエストするため、大量に実行するとすぐにAPI制限に引っかかってしまう
スプレッドシートの読み取りは100秒あたり500リクエストまで
### ③を採用しなかった理由
スプレッドシート内のセル情報全てを取得したかった
①の方が簡潔にかけそう
## 取得したセル情報をパースする
①spreadsheets.get + includeGridDataオプションを付与して取得する 方式で情報を取得すると以下の形式で返ってきます。
https://godoc.org/google.golang.org/api/sheets/v4#Spreadsheet
APIドキュメントをたどるとセル情報は、SpreadsheetのSheetsのData配列のRowData配列のValues配列 にそれぞれ格納されており、Valuesは CellData型で定義されている(深い)。
CellDataのドキュメント↓
https://godoc.org/google.golang.org/api/sheets/v4#CellData
CellDataは、FormattedValue、EffectiveValue、UserEnteredValueを持っている。
それぞれについて整理しておくと、
FormattedValue: 整形された値(スプレッドシートに表示されている値)。戻り値は、string。
EffectiveValue: 評価された値。戻り値は、ExtendedValue。
UserEnteredValue: 入力された値。戻り値は、ExtendedValue。
以下是ExtendedValue的文档链接:https://godoc.org/google.golang.org/api/sheets/v4#ExtendedValue
ExtendedValue根据不同的类型具有相应的信息。
文字列ならExtendedValue.StringValueに
数値ならExtendedValue.NumberValueに (ただし戻り値はfloat)
真偽値なら ExtendedValue.BoolValueに格納されています。
获取和显示单元格信息的示例代码。
import "google.golang.org/api/sheets/v4"
func main() {
client, _ := getHTTPClient() // ここは先述のサービスアカウント情報を使ってクライアントを取得する
service, _ := sheets.New(client)
resp, _ := service.Spreadsheets.Get(spreadsheetID).IncludeGridData(true).Do()
// セル情報の取得
for _, s := range resp.Sheets {
for _, row := range s.Data[0].RowData {
for _, value := range row.Values {
fmt.Println(value.FormattedValue)
fmt.Println(value.EffectiveValue.StringValue)
fmt.Println(value.UserEnteredValue.StringValue)
}
}
}
}
选择哪个值?
这是我在实际实施中获得的经验,日期和时间数据在ExtendedValue中包含奇怪的值(如果有任何经验,请告诉我)。因此,我们需要将其作为FormattedValue(字符串)进行处理,并根据需要进行格式化和读取。对于其他数值和字符串数据,我们将从EffectedValue中读取。我们没有使用UserEnteredValue。关于EffectiveValue,我编写了一段很好的代码来获取值。
func getValueFromEffectiveValue(ev *sheets.ExtendedValue) interface{} {
if ev == nil {
return ""
}
if ev.StringValue != "" {
return ev.StringValue
}
if isInteger(ev.NumberValue) {
return math.Floor(ev.NumberValue)
}
return ev.NumberValue
}
同时获取电子表格信息
嗯,到目前为止,我们已经能够获取到电子表格的单元格信息了。但是,如果对多个电子表格进行串行访问,那可能会花费很长时间,所以我们将并行访问电子表格并获取信息。在Golang中,有groutine、WaitGroup和errorgroup等并行访问方法。因为我希望进行错误处理,所以选择使用errorgroup。我写了下面这样的代码。
import (
"sync"
"golang.org/x/sync/errgroup"
)
func getSpreadsheetDataByID(
mutex *sync.Mutex,
spreadsheetID string,
output *[]string,
) func() error {
return func() error {
// スプレッドシートからセル情報を取得する
// ごにょごにょ
// 並行に同一のリソースにアクセスする場合はmutexでLockをかけておくと安全
mutex.Lock()
// ここで取得したセル情報をoutputに格納する
mutex.Unlock()
}
}
func main() {
output := make([]string, 0)
mutex := &sync.Mutex{}
var eg errgroup.Group
spreadSheetIDs := []string{"xxx", "yyy", "zzz"}
for _, spreadSheetID := range spreadSheetIDs {
// NOTE: https://golang.org/doc/faq#closures_and_goroutines
spreadSheetID := spreadSheetID
eg.Go(getSpreadsheetDataByID(mutex, spreadSheet, &output))
}
if err := eg.Wait(); err != nil {
return nil, err
}
// outputで何かする
}
错误团队的小陷阱
在前一章中,我们使用了errorgroup。// 注意 在下面的注释行中可以看到,
spreadsheetID := spreadsheetID 进行了重新赋值。
如果不这样做,将始终引用列表的最后一个值,并且并发处理将无法获取期望的值。
有关详细原因,请参考这个链接。
https://qiita.com/harhogefoo/items/7ccb4e353a4a01cfa773
結束之時
你觉得怎么样?
如果能對某人有所幫助,我就很開心!
快乐编码!