環境:MacOS
Python
Anaconda、JupiterLab(結果はJupyterLabでは実行できない)、Chrome
ターミナル(こっちでPython実行)
GoogleCloudPlatformの認証設定とJsonファイルのダウンロードが必要
目的:ローカルドライブのCSVをグーグルドライブにアップロードしたい
エラーになったスクリプト、動作しない
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#ターミナルで実行する。JypyterLabでは、エラーになるから #ターミナルで実行する #https://i-o-knowledge.blogspot.com/2018/04/pythonlocalgoogle-drive-csvspreadsheet.html #pythonでlocalからGoogle Driveへファイルアップロード #JupyterLabだとエラーになる 「ipykernel_launcher.py: error: unrecognized arguments:」 from __future__ import print_function from apiclient.discovery import build #インストールが必要、pip install google-api-python-client 2022/03/07 from apiclient.http import MediaFileUpload from httplib2 import Http from oauth2client import file, client, tools#インストールが必要、pip install oauth2client path='/Users/toshiromaseda/Documents/2022年/2022年株/yahoofinance_data/' myfilename="年初来高値更新20220307.csv" # Setup the Drive v3 API SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly' #https://www.googleapis.com/auth/drive #https://www.googleapis.com/auth/drive.file #https://www.googleapis.com/auth/drive.appdata #https://www.googleapis.com/auth/drive.apps.readonly store = file.Storage('credentials.json') creds = store.get() if not creds or creds.invalid: flow = client.flow_from_clientsecrets('/Users/toshiromaseda/Documents/2020年株関連/kabu_python/client_secret.json', SCOPES) creds = tools.run_flow(flow, store) drive_service = build('drive', 'v3', http=creds.authorize(Http())) file_metadata = { 'name': '年初来高値更新20220307.csv', 'mimeType': 'application/vnd.google-apps.spreadsheet' } media = MediaFileUpload('/Users/toshiromaseda/Documents/2022年/2022年株/yahoofinance_data/年初来高値更新20220307.csv', mimetype='text/csv', resumable=True) file = drive_service.files().create(body=file_metadata, media_body=media, fields='id').execute() #print 'File ID: %s' % file.get('id') |
(1)ライブラリ、モジュールインストール
pip install google-api-python-client
pip install oauth2client
したあとで
WEBで拾ったPythonスクリプトをjupyterで実行したら、
以下のエラー ’client_secret.json’がないということか?
「
/Users/toshiromaseda/opt/anaconda3/lib/python3.7/site-packages/oauth2client/_helpers.py:255: UserWarning: Cannot access credentials.json: No such file or directory
warnings.warn(_MISSING_FILE_MESSAGE.format(filename))
(略)
InvalidClientSecretsError: (‘Error opening file’, ‘client_secret.json’, ‘No such file or directory’, 2)
」
(2)間違い
サービスアカウントキー(JSONファイル)の発行が必要らしい
参考
https://algorithm.joho.info/programming/python/google-spread-sheet-py/
GoogleCloudPlatformでプロジェクトを作成して、「ウェブアプリケーション」として
作成した。
https://console.developers.google.com/cloud-resource-manager
>>>これは後ほど間違いで「デスクトップアプリ」で作成する
そのJsonファイルをダウンロードして、実行するPyファイルと同じ階層に
入れておいてリネームしておく
yearhigh2022-4fe5fcXXXXXX.json(ファイル名は仮名)
↓
client_secret.json
そして、スクリプト内のファイル名だけだと、No such fileになるので、フルパス+ファイル名にした。
(3)他のJsonファイルを作成
次のエラー
「
InvalidClientSecretsError: Invalid file format. See https://developers.google.com/api-client-library/python/guide/aaa_client_secrets Expected a JSON object with a single property for a “web” or “installed” application」
たぶん(2)で取得したJsonファイルではだめらしい。
他のJsonファイルを作成してみる。
スプレッドシートのキーではなくて、ドライブのキーを作成してみる。2022/03/08
https://dev.classmethod.jp/articles/google-spreadsheet-append-csv-from-command-line/
OAuth 2.0 クライアント IDのキーをダウンロードした。
画面の右側に操作という項目があるので、その下にダウロードアイコン(下矢印)をクリックすると
ダウンロードできた。
「client_secret_XXXX.apps.googleusercontent.com.json」というファイルがダウンロードできた。
これをリネームすればよさそう
(4)不要
(>>結果的に、アプリケーションの種類では「デスクトップアプリ」なので
このリダイレクトは不要になった)
次のエラー
Jsonファイルを作成したところまでできた。
「redirect_uris」についてなにか違うようだ。
「
InvalidClientSecretsError: Missing property “redirect_uris” in a client type of “web”.
」
GoogleCloudPlatform画面のどこかにURIを設定するようだ
>GoogleCloudPlatform画面の「認証情報」のOAuth 2.0 クライアント IDの欄で
作成したウェブクライアントの編集を行う。
「ウェブ アプリケーション のクライアント ID」画面で
「承認済みのリダイレクト URI」を設定する
http://localhost:8080
そして再度、またJsonファイルをダウンロードする。
リネームした。
jupyterで実行すると、また別のエラーになった
「credentials.json」がないそうだ。2022/03/09
「
/Users/toshiromaseda/opt/anaconda3/lib/python3.7/site-packages/oauth2client/_helpers.py:255: UserWarning: Cannot access credentials.json: No such file or directory
warnings.warn(_MISSING_FILE_MESSAGE.format(filename))
usage: ipykernel_launcher.py [–auth_host_name AUTH_HOST_NAME]
[–noauth_local_webserver]
[–auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT …]]]
[–logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
ipykernel_launcher.py: error: unrecognized arguments: -f /Users/toshiromaseda/Library/Jupyter/runtime/kernel-ba689470-fac8-4b05-9082-70df85f8e27f.json
An exception has occurred, use %tb to see the full traceback.
SystemExit: 2
」
空のファイルcredentials.jsonを作成する必要があるらしいので、
空のファイルcredentials.jsonをテキストエディタ(CotEditor)で作成した
(5)ターミナルコンソールでPython
jupyterで実行したらまたエラー
「
usage: ipykernel_launcher.py [–auth_host_name AUTH_HOST_NAME]
[–noauth_local_webserver]
[–auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT …]]]
[–logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
ipykernel_launcher.py: error: unrecognized arguments: -f /Users/toshiromaseda/Library/Jupyter/runtime/kernel-ba689470-fac8-4b05-9082-70df85f8e27f.json
An exception has occurred, use %tb to see the full traceback.
」
コマンドライン引数は jupyter では対応できないらしい
https://qiita.com/uenonuenon/items/09fa620426b4c5d4acf9
ということは、JupyterLabで実行しなければよいということか?
つまり、ターミナルコンソールでPythonで実行すればよいのかな?
(6)承認エラー
ターミナルで実行した。
するとChromeが開き、
「承認エラー
エラー 400: redirect_uri_mismatch
このアプリは Google の OAuth 2.0 ポリシーを遵守していないため、ログインできません。
」
となった。
参考
pydriveで「エラー 400: redirect_uri_mismatch」
https://awesome03.com/2021/01/10/pydrive%E3%81%A7%E3%80%8C%E3%82%A8%E3%83%A9%E3%83%BC-400-redirect_uri_mismatch%E3%80%8D/
GoogleCloudPlatform画面で
アプリケーションの種類では「デスクトップアプリ」を選択する必要があるらしい
GoogleCloudPlatform画面で「+認証情報を作成」をクリック
OAuth 2.0 クライアント IDでアプリケーションの種類では「デスクトップアプリ」を選択
して作成した。
そして、Jsonファイルをダウンロード
client_secret.jsonにリネームした
(7)承認エラー エラー 403: access_denied
実行したらChromeが開き、
アカウント(自分が持ってるアカウント)の選択で、アカウントを選択したらアクセス権がないらしい
承認エラー
エラー 403: access_denied
The developer hasn’t given you access to this app. It’s currently being tested and it hasn’t been verified by Google. If you think you should have access, contact the developer (maXXX@gmail.com).
アクセス権を与えてみる
https://console.developers.google.com/cloud-resource-manager
サービスアカウントの管理から
サービス アカウントでグーグルのアカウントを追加した。
いつも使っているグーグルメール mas***@gmail.com
画像
でもだめだったので
OAuth 同意画面
にテストユーザとして
グーグルアカウントのメールを追加した。
いくつかの画面が表示された
そしてChrome画面に
The authentication flow has completed.
もう一度ターミナルで実行すると
python upload_gdrive.py
Chromeには何も表示されず
ターミナル側にエラーになってた。
「
Traceback (most recent call last):
File “upload_gdrive.py”, line 32, in <module>
fields=’id’).execute()
File “/Users/toshiromaseda/opt/anaconda3/lib/python3.7/site-packages/googleapiclient/_helpers.py”, line 131, in positional_wrapper
return wrapped(*args, **kwargs)
File “/Users/toshiromaseda/opt/anaconda3/lib/python3.7/site-packages/googleapiclient/http.py”, line 901, in execute
_, body = self.next_chunk(http=http, num_retries=num_retries)
File “/Users/toshiromaseda/opt/anaconda3/lib/python3.7/site-packages/googleapiclient/_helpers.py”, line 131, in positional_wrapper
return wrapped(*args, **kwargs)
File “/Users/toshiromaseda/opt/anaconda3/lib/python3.7/site-packages/googleapiclient/http.py”, line 1021, in next_chunk
raise ResumableUploadError(resp, content)
googleapiclient.errors.ResumableUploadError: <HttpError 403 when requesting None returned “Insufficient Permission: Request had insufficient authentication scopes.”. Details: “[{‘domain’: ‘global’, ‘reason’: ‘insufficientPermissions’, ‘message’: ‘Insufficient Permission: Request had insufficient authentication scopes.’}]”>
」
このエラーを分析する
(8)エラー
2022/03/10
わかったスコープのURLがReadOnlyだ。ファイルをアップロードするからリードオンリーではだめなはず。
スクリプトに記載のURLがreadonly、読み込みのみ。アップロードするからReadではだめ。
SCOPES = ‘https://www.googleapis.com/auth/drive.metadata.readonly’
#https://www.googleapis.com/auth/drive
#https://www.googleapis.com/auth/drive.file
#https://www.googleapis.com/auth/drive.appdata
#https://www.googleapis.com/auth/drive.apps.readonly
参考8
[Python]GoogleDriveAPIの基本的な使い方
https://zenn.dev/wtkn25/articles/python-googledriveapi-operation
>>わかりやすい
最初にWebで拾ったスクリプトの内容が怪しい、自分の思うように動作しない
ように思えたので、「参考8」サイトを参考にして作り直す。
現在、2022/03/13作業中で、まだ実行すらしていない状態のスクリプト、参考8から抜粋した。
でも実行したらエラー。今ここ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
from googleapiclient.discovery import build from httplib2 import Http from oauth2client import file, client, tools from apiclient.http import MediaFileUpload #ローカルPCからグーグルドライブにファイルをアップロードする #https://zenn.dev/wtkn25/articles/python-googledriveapi-operation SCOPES = ['https://www.googleapis.com/auth/drive'] store = file.Storage('token.json') creds = store.get() if not creds or creds.invalid: flow = client.flow_from_clientsecrets('./client_secret.json', SCOPES) creds = tools.run_flow(flow, store) drive_service = build('drive', 'v3', http=creds.authorize(Http())) file_metadata = { 'name': 'フォルダ名', 'mimeType': 'application/vnd.google-apps.folder', 'parents': ['格納するフォルダのID'] } drive_service = drive_service.files().create(body=file_metadata, fields='id').execute() #fieldに指定したidをfileから取得できる print ('Folder ID: %s' % file.get('id')) folder_id = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' file_metadata = { 'name': 'sample.txt', 'parents': [folder_id] } media = MediaFileUpload( 'local_path/to/sample.txt', mimetype='text/plain', resumable=True ) file = drive_service.files().create( body=file_metadata, media_body=media, fields='id' ).execute() #fieldに指定したidをfileから取得できる print ('File ID: %s' % file.get('id')) |
エラー
「Traceback (most recent call last):
File “uploadgoogledrive2.py”, line 26, in <module>
fields=’id’).execute()
File “/Users/toshiromaseda/opt/anaconda3/lib/python3.7/site-packages/googleapiclient/_helpers.py”, line 131, in positional_wrapper
return wrapped(*args, **kwargs)
File “/Users/toshiromaseda/opt/anaconda3/lib/python3.7/site-packages/googleapiclient/http.py”, line 937, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/drive/v3/files?fields=id&alt=json returned “Insufficient Permission: Request had insufficient authentication scopes.”. Details: “[{‘domain’: ‘global’, ‘reason’: ‘insufficientPermissions’, ‘message’: ‘Insufficient Permission: Request had insufficient authentication scopes.’}]”>
」
>たぶんグーグルドライブのフォルダーのパーミッションの読み書きとかの設定が、
違っているのだろう。
またグーグル先生に頼る
(9)
Googleドライブの[設定]-> [アプリの管理]にリストされているアプリケーションを確認
認証しているスコープを確認
>>わかった。スコープの設定を飛ばしたから認証情報(/auth/drive 、auth/drive.file 等の設定)
がなかったのでエラーになってた。
もう一度認証情報を作成することにした。
作成のときにウィザードを選択
一度削除して、
設定して、再度Jsonファイルをダウンロードして、リネームした。client_secret.jsonにした
credentials.jsonも空のファイルにした。
2022/03/13、17:34
とりあえず、Chromeで認証まで進めた。
「$ python uploadgoogledrive2.py
Your browser has been opened to visit:
https://accounts.google.com/o/oauth2/auth?client_id=548555605276-j7kpa1otv5b387ekn3ndf6ktlp5aubs3.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&access_type=offline&response_type=code
If your browser is on a different machine then exit and re-run this
application with the command-line parameter
–noauth_local_webserver
Authentication successful.
Traceback (most recent call last):
File “uploadgoogledrive2.py”, line 29, in <module>
print (‘Folder ID: %s’ % file.get(‘id’))
AttributeError: module ‘oauth2client.file’ has no attribute ‘get’
」
printのところでエラーになってるからとりあえずコメントにしておく
再度実行する
>
(10)成功、アップロードできた
「Traceback (most recent call last):
File “uploadgoogledrive2.py”, line 42, in <module>
file = drive_service.files().create(
AttributeError: ‘dict’ object has no attribute ‘files’」
dictオブジェクトにfielsアトリビュートがないらしい。でもそんなdictなんてしらない。
25行目で自分に代入しているので別のオブジェクトにしておく
drive_service = drive_service.files()
↓
file = drive_service.files()
ターミナルから実行して、うまくいった。グーグルドライブの指定フォルダにCSVがアップロードできた
以下にうまく行ったコードを記入
Documents/2020年株関連/kabu_python/
uploadgoogledrive2.py
。不要なコードもある
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
from googleapiclient.discovery import build from httplib2 import Http from oauth2client import file, client, tools from apiclient.http import MediaFileUpload #ローカルPCからグーグルドライブにファイルをアップロードする #https://zenn.dev/wtkn25/articles/python-googledriveapi-operation SCOPES = ['https://www.googleapis.com/auth/drive'] store = file.Storage('credentials.json') creds = store.get() if not creds or creds.invalid: flow = client.flow_from_clientsecrets('./client_secret.json', SCOPES) creds = tools.run_flow(flow, store) drive_service = build('drive', 'v3', http=creds.authorize(Http())) #file_metadata = { # 'name': 'デイトレ画像', # 'mimeType': 'application/vnd.google-apps.folder', # 'parents': ['15aWXg--tQvOt4CluUtLUjmMdhYexcb7t'] # } #ディレクトリ作成 #file = drive_service.files().create(body=file_metadata, # fields='id').execute() #fieldに指定したidをfileから取得できる #print ('Folder ID: %s' % file.get('id')) folder_id = '15aWXg--tQvOt4CluUtLUjmMdhYexcb7t'#「デイトレ画像フォルダ」 file_metadata = { 'name': '年初来高値更新20220307sheet',#'年初来高値更新20220307.csv' 'mimeType': 'application/vnd.google-apps.spreadsheet',#グーグルスプレッドシートとしてアップ 'parents': [folder_id] } #mimetype='text/plain', #text media = MediaFileUpload( '/Users/toshiromaseda/Documents/2022年/2022年株/yahoofinance_data/年初来高値更新20220307.csv', mimetype='text/csv', resumable=True ) file = drive_service.files().create(body=file_metadata, media_body=media, fields='id').execute() #fieldに指定したidをfileから取得できる #print ('File ID: %s' % file.get('id')) |
最新版2022/03/22
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
from googleapiclient.discovery import build from httplib2 import Http from oauth2client import file, client, tools from apiclient.http import MediaFileUpload import os import time import re import datetime import sys import glob def checkFileExist(filename,path)->bool:#クラスメソッドの場合は、selfが必要 #他にもimport os os.listdir(path) というのもある if path[-1]=='/':#文字列の最後の1文字がスラッシュならなにもしない。 pass else: path+='/' files=glob.glob(path+'*.csv') count=0 for file in files: #print(file) if filename == os.path.split(file)[1]:#ファイル名と同じならTRUE print('ファイルは存在します') print(file) count+=1 return True#見つかれがすぐに抜ける #ループで探し終わってから判断もいれる if count>0: return True else: print('ファイルはありません') return False #end def #引数にCSVファイル名を指定、CSVはあってもなくてもよい。引数を指定しない場合、今日の日付 #でファイルの存在をチェックして見つかればアップロードする ##実行main args = sys.argv print(args[0]) #第一引数 実行ファイル名 # print(args[1]) #ローカルPCからグーグルドライブにファイルをアップロードする #https://zenn.dev/wtkn25/articles/python-googledriveapi-operation SCOPES = ['https://www.googleapis.com/auth/drive'] store = file.Storage('credentials.json') creds = store.get() if not creds or creds.invalid: flow = client.flow_from_clientsecrets('./client_secret.json', SCOPES) creds = tools.run_flow(flow, store) drive_service = build('drive', 'v3', http=creds.authorize(Http())) #file_metadata = { # 'name': 'デイトレ画像', # 'mimeType': 'application/vnd.google-apps.folder', # 'parents': ['15aWXg--tQvOt4CluUtLUjmMdhYexcb7t'] # } #ディレクトリ作成 #file = drive_service.files().create(body=file_metadata, # fields='id').execute() #fieldに指定したidをfileから取得できる #print ('Folder ID: %s' % file.get('id')) folder_id = '1PPAsjMW5tMWO7uiCqX0mzhBe83U5O6iL' #年初来高値 フォルダ #'15aWXg--tQvOt4CluUtLUjmMdhYexcb7t'#「デイトレ画像フォルダ」 if len(args)>=2:#args[1]!='': if args[1].find('.csv')>0:#CSVを除去して処理する filename=args[1].split('.')[0] else: filename=args[1]#2番めの要素が、実行1つ目の引数 #拡張子なし else: print('引数がなかったのでファイル名を自動生成') #引数にファイル名が存在しないときは、今日の日付をファイル名にする dt_now=datetime.datetime.now() StockDate=dt_now.strftime('%Y%m%d')# filename='年初来高値更新'+StockDate#拡張子なし #fileの存在チェックが必要 path='/Users/toshiromaseda/Documents/2022年/2022年株/yahoofinance_data/' if checkFileExist(filename+'.csv',path): print('処理中、、、') else: print('file not found, so exit. Please check file') exit #グーグルドライブにアップロードされたときに保存されるファイル名 file_metadata = { 'name': filename,#'年初来高値更新20220307sheet',#'年初来高値更新20220307.csv' 'mimeType': 'application/vnd.google-apps.spreadsheet',#グーグルスプレッドシートとしてアップ 'parents': [folder_id] } #mimetype='text/plain', #text #ローカル側のファイル名(パス) media = MediaFileUpload( path + filename + '.csv', mimetype='text/csv', resumable=True ) file = drive_service.files().create(body=file_metadata, media_body=media, fields='id').execute() #fieldに指定したidをfileから取得できる #print ('File ID: %s' % file.get('id')) print('end') |