Python
Google Sheets
Tutorials
結合 Google Sheets 本身能直接分享、檢視、編輯的優點,拿來當作簡易的資料庫是不錯的選擇,或者是用來將持續變動的/重複輸出的資料直接呈現給客戶,他們不用總是要下載後開啟才能讀寫資料。
在開始使用前要先取得讀寫 Google Sheets 的權限,進入下面連結輸入一些資訊後即可取得憑證 json 檔。
https://console.developers.google.com/flows/enableapi?apiid=sheets.googleapis.com
值得注意的是,同一個憑證可以開啟多種權限(如同時取得 Firebase 的權限)。
取得憑證 json 檔後你擁有的是「讀取」你的 Google Drive 中 Google Sheets 的權限,如果想要「編輯」 Google Sheets,甚至是讀寫其他人共用給你的 Google Sheets,最快的方式就是:
- 打開憑證的 json 檔,複製 client_email 中的那串 email。
- 將你要讀寫的 Google Sheets 共用給上面那串 email,權限選擇「可以編輯」,就完成啦!
Google 有自己的 API 來讀寫 Google Sheets,但看起來有點麻煩,非原生的有
gspread
或pygsheets
套件,我剛開始是用gspread
,後來發現pygsheets
更簡單一點,主要原因是它可以直接輸入或輸出 dataframe,方便很多。
取得憑證後只需要用 Google Sheets 的 ID 就可以連接,不過用 url 可以直接點過去看一下也是滿方便的。
import pygsheets
gc = pygsheets.authorize(service_account_file='path/to/key.json')
survey_url = 'https://docs.google.com/spreadsheets/d/1cNqBIzF_T8yZm5S1hj7E1MNVXQegVDVuJsUWVoDKIDw/'
sh = gc.open_by_url(survey_url)
可以跟下面的
gspread
套件比較,pygsheets
不需要額外安裝認證套件,也不需特別指定 scope。
ws = sh.worksheet_by_title('demo')
ws.update_value('A1', 'test')
df1 = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
ws.set_dataframe(df1, 'A2', copy_index=True, nan='')
val = ws.get_value('A1')
df2 = ws.get_as_df(start='A2', index_colum=1, empty_value='', include_tailing_empty=False) # index 從 1 開始算
要特別注意這個套件在
get_as_df
預設會自動轉換可能是數字的資料,所以即使資料內容是文字的 '0001' 也是會被轉成數字 1 ,只要改參數numerize=False
就能避免這個問題。
如果想避免用
get_as_df
設定上的麻煩,最快的方法就是先輸出 csv 再讀進來。
ws.export(filename='df')
df3 = pd.read_csv('df.csv')
寫成 functions
def ws_to_df(self, **kwargs):
worksheet_title = self.title
self.export(filename=worksheet_title + '_df')
df = pd.read_csv(worksheet_title + '_df.csv', **kwargs)
return df
pygsheets.worksheet.Worksheet.ws_to_df = ws_to_df
使用範例
df4 = ws.ws_to_df()
其他更進階的就去看文檔吧:
https://pygsheets.readthedocs.io/en/stable/index.html
其實直接用
pygsheets
就好了,不過既然之前都做了些研究,就順便寫一下。
import gspread
from oauth2client.service_account import ServiceAccountCredentials as SAC
# Google Sheets credentials
## 定義使用的範圍
scope = ['https://spreadsheets.google.com/feeds',
'https://www.googleapis.com/auth/drive']
credentials = SAC.from_json_keyfile_name('path/to/key.json', scope)
gc = gspread.authorize(credentials)
survey_url = 'https://docs.google.com/spreadsheets/d/1cNqBIzF_T8yZm5S1hj7E1MNVXQegVDVuJsUWVoDKIDw/'
sh = gc.open_by_url(survey_url)
這個套件同樣也會自動轉換可能是數字的資料,但沒辦法改參數解決,所以為了讓輸出的資料維持原樣,必須要小改套件中的幾個 functions,如下:
def numericise_m(value, empty2zero=False, default_blank="", allow_underscores_in_numeric_literals=False):
if value is not None:
if "_" in value and not allow_underscores_in_numeric_literals:
return value
if value == "":
if empty2zero:
value = 0
else:
value = default_blank
return value
def numericise_all_m(input, empty2zero=False, default_blank="", allow_underscores_in_numeric_literals=False):
return [numericise_m(s, empty2zero, default_blank, allow_underscores_in_numeric_literals) for s in input]
def get_all_records_m(
self,
empty2zero=False,
head=1,
default_blank=None,
allow_underscores_in_numeric_literals=False,
):
idx = head - 1
data = self.get_all_values()
keys = data[idx]
values = [
numericise_all_m(
row,
empty2zero,
default_blank,
allow_underscores_in_numeric_literals,
)
for row in data[idx + 1:]
]
return [dict(zip(keys, row)) for row in values]
或許有比較好的改法,但當時想說能用就好,所以就沒有仔細去研究了。
這個套件也沒有可以直接轉成 dataframe 或 dictionary 的 function,所以一樣要自己寫:
def worksheet_to_df(worksheet, index):
df = pd.DataFrame(get_all_records_m(worksheet))
df = df.set_index(index, drop=False)
# check if index duplicate
duplicate_index = df.groupby(level=0).filter(lambda x: len(x) > 1).__len__()
if duplicate_index > 0:
raise IndexError('duplicate index!!')
else:
return df
def worksheet_to_dict(worksheet, index):
df = pd.DataFrame(get_all_records_m(worksheet))
df = df.set_index(index, drop=False)
# check if index duplicate
duplicate_index = df.groupby(level=0).filter(lambda x: len(x) > 1).__len__()
if duplicate_index > 0:
raise IndexError('duplicate index!!')
else:
df_dict = df.to_dict(orient='index')
return df_dict
main_df = worksheet_to_df(sh.worksheet('main'), 'numbering')
main_dict = worksheet_to_dict(sh.worksheet('main'), 'numbering')
gspread
雖然老牌,但它沒有支援 dataframe 變得非常難用,還要自己寫加改 functions,幸好後來又找到pygsheets
,真的方便許多,而且看了一下它套件裡的 functions,感覺應該是從gspread
做改良。
過去使用
gspread
,每次憑證連接的時間有限,所以過一段時間就要重連,對於拿來當作長時間連接的資料庫會有一些影響,雖然可以用定期重連的方式解決,但也還是比較麻煩。pygsheets
可能也有這個問題。