{%hackmd @rpyapp/theme %} # Python 讀寫 Google Sheets 教學 ###### tags: `Python` `Google Sheets` `Tutorials` [TOC] ## 前言 > 結合 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,最快的方式就是: > > 1. 打開憑證的 json 檔,複製 client_email 中的那串 email。 > 2. 將你要讀寫的 Google Sheets 共用給上面那串 email,權限選擇「可以編輯」,就完成啦! ## 套件選擇 > Google 有自己的 API 來讀寫 Google Sheets,但看起來有點麻煩,非原生的有`gspread`或`pygsheets`套件,我剛開始是用`gspread`,後來發現`pygsheets`更簡單一點,主要原因是它可以直接輸入或輸出 dataframe,方便很多。 ## pygsheets 套件 ### 使用憑證開啟 Google Sheets > 取得憑證後只需要用 Google Sheets 的 ID 就可以連接,不過用 url 可以直接點過去看一下也是滿方便的。 ```python= 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。 ### 寫入方式 ```python= 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='') ``` ### 讀取方式 ```python= 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 再讀進來。 ```python= ws.export(filename='df') df3 = pd.read_csv('df.csv') ``` > 寫成 functions ```python= 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 ``` > 使用範例 ```python= df4 = ws.ws_to_df() ``` ### 文檔 > 其他更進階的就去看文檔吧: > https://pygsheets.readthedocs.io/en/stable/index.html ## gspread 套件 > 其實直接用`pygsheets`就好了,不過既然之前都做了些研究,就順便寫一下。 ### 使用憑證開啟 Google Sheets ```python= 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,如下: ```python= 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,所以一樣要自己寫: ```python= 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 ``` ### 讀取方式 ```python= 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`可能也有這個問題。
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.