PythonでGoogle Drive を操作する方法(Pydrive2)
仕事でGoogle DriveのAPIを利用したいと思ったのですが、調べてもよくわからなかったので自分で試行錯誤してモジュールを作ってみました。
業務効率化のスクリプトを作るときなどに活用できるかなと思います。
ただ、Google Drive APIのAPIキーが必要になるので、そちらはネットの記事を参考に別途取得しておいてください。
事前準備
パッケージのインストール
まず、このスクリプトを実行するためには、以下のPythonパッケージが必要です。
-
pydrive2
: Google Drive APIとやり取りを行うためのライブラリ -
pandas
: データ分析ライブラリで、このスクリプトではファイルの情報をデータフレームとして扱うために使用しています。
これらのパッケージはpipを使用してインストールすることができます。
以下のコマンドを実行してインストールできます。
pip install pydrive2 pandas
このコマンドを実行すると、必要なパッケージがインストールされます。
セッティングファイルの作成
次にpydrive2
を使ってGoogle Drive APIを利用する場合、以下の3つのファイルが必要となります。
-
client_secrets.json
: Google Cloud Consoleからダウンロードすることができます。OAuth 2.0 クライアントIDの認証情報を含んでいます。 -
saved_credentials.json
: ユーザーが認証を行った後に生成されます。ユーザーのアクセストークンとリフレッシュトークンが含まれています。これによりユーザーは再認証することなくGoogle Drive APIを利用することができます。 -
settings.yaml
:pydrive2
の設定を含んでいます。認証に関する情報や、上記の2つのjsonファイルのパスなどが指定されます。
それぞれのファイルについて詳しく説明します。
client_secrets.json
1. このファイルはGoogle Cloud Consoleからダウンロードします。以下の手順で取得できます。(UIが変更になっている可能性があるのでその場合は同じような画面を探してみてください)
- Google Cloud Consoleにアクセスし、プロジェクトを選択または新規作成します。
- 左側のナビゲーションメニューから「APIとサービス」 > 「認証情報」を選択します。
- 「認証情報を作成」をクリックし、「OAuthクライアントID」を選択します。
- 「アプリケーションの種類」で「デスクトップアプリ」を選択し、「作成」をクリックします。
- 作成した認証情報の右側にあるダウンロードボタンをクリックし、jsonファイルをダウンロードします。
- ダウンロードしたjsonファイルを
client_secrets.json
として保存します。
saved_credentials.json
2. このファイルはユーザーが認証を行った後に自動的に生成されます。pydrive2
が初めて認証を行う際に、client_secrets.json
を参照してブラウザで認証画面が表示されます。ユーザーが認証を行うと、pydrive2
は得られたアクセストークンとリフレッシュトークンをsaved_credentials.json
に保存します。
settings.yaml
3. このファイルはpydrive2
の設定を指定します。以下のような内容を含めます。
client_config_backend: file
client_config_file: client_secrets.json
client_config:
client_id: your_client_id
client_secret: your_client_secret
save_credentials: True
save_credentials_backend: file
save_credentials_file: saved_credentials.json
get_refresh_token: True
oauth_scope:
- https://www.googleapis.com/auth/drive
- https://www.googleapis.com/auth/drive.install
client_config_file
にはclient_secrets.json
のパス、save_credentials_file
にはsaved_credentials.json
のパスを指定します。また、get_refresh_token
をTrue
にすることで、アクセストークンが失効した場合でも自動的に更新(リフレッシュ)されます。oauth_scope
はアプリケーションのアクセス許可を定義します。
これらのファイル (client_secrets.json
, saved_credentials.json
, settings.yaml
) は、スクリプトが実行されるときにPythonがアクセスできる場所に配置してください。一般的には、これらのファイルはスクリプトと同じディレクトリに配置します。
例えば、次のようなディレクトリ構造になるかもしれません:
.
├── main.py
├── gdrive_api.py
├── client_secrets.json
├── saved_credentials.json
└── settings.yaml
ここで、main.py
が実行されるスクリプト、gdrive_api.py
がGoogle Drive APIを操作するためのスクリプトで、これらは同じディレクトリにあることを前提としています。
ただし、これらの設定ファイルは機密情報を含むため、安全に管理することが重要です。共有されたり公開されたりしないように注意してください。
また、settings.yaml
において client_config_file
と save_credentials_file
の設定を変更すれば、これらのファイルが別のディレクトリに配置されていても問題ありません。例えば、次のように設定することが可能です:
client_config_file: /path/to/your/client_secrets.json
save_credentials_file: /path/to/your/saved_credentials.json
これらの設定を変更することで、client_secrets.json
と saved_credentials.json
の位置を自由に指定することができます。ただし、指定するパスはPythonからアクセス可能なものである必要があります。
以上が、pydrive2
を使う際に必要な設定ファイルの解説です。注意点としては、これらのファイルには機密情報が含まれますので、不適切に公開されないように注意してください。
コードの解説
こちらがコード全文です。
"""A Python library for interacting with Google Drive API"""
from pydrive2.drive import GoogleDrive
from pydrive2.auth import GoogleAuth
import os
import pandas as pd
class GoogleDriveAPI:
def __init__(self):
self.gauth = GoogleAuth() # Google認証のインスタンス化
self.drive = GoogleDrive(self.gauth) # GoogleDriveのインスタンス化
self.gauth.LocalWebserverAuth()# ローカルWebサーバーで認証する
def get_folder_id(self, folder_name):
"""フォルダ名からフォルダIDを取得"""
query = "trashed = false and mimeType='application/vnd.google-apps.folder' and title='{}'".format(folder_name)
folder_list = self.drive.ListFile({'q': query}).GetList()
if len(folder_list) > 0:
return folder_list[0]['id']
else:
return None
def get_file_id(self, folder_name=None, file_name=None, folder_id=None):
"""フォルダ内の指定された名前の最新ファイルのファイルIDを取得する"""
if not folder_id:
folder_id = self.get_folder_id(folder_name)
query = f"trashed = false and '{folder_id}' in parents and mimeType != 'application/vnd.google-apps.folder'"
if file_name:
query += f" and title = '{file_name}'"
file_list = self.drive.ListFile({'q': query}).GetList()
else:
file_list = self.drive.ListFile({'q': query}).GetList()
if len(file_list) > 0:
latest_file = max(file_list, key=lambda f: f['createdDate'])
return latest_file['id']
if file_list:
return file_list[0]['id']
else:
return None
def get_all_file_id(self, folder_name):
"""フォルダー名からフォルダを特定し、そのフォルダ内のファイルID一覧を取得する"""
folder_id = self.get_folder_id(folder_name)
file_list = self.drive.ListFile({'q': f"\'{folder_id}\' in parents"}).GetList()
file_id_list = []
for file in file_list:
file_id_list.append(file['id'])
return file_id_list
def get_file_df(self, folder_name):
"""フォルダ内のファイルIDと作成日時のデータフレームを取得する"""
folder_id = self.get_folder_id(folder_name)
query = f"trashed = false and '{folder_id}' in parents"
file_list = self.drive.ListFile({'q': query}).GetList()
file_id_list = []
for file in file_list:
file_id_list.append([file['title'] ,file['createdDate'] ,file['id']])
file_df = pd.DataFrame(file_id_list, columns=['title', 'createdDate', 'id'])
return file_df
def get_created_at(self, folder_name):
'''指定したフォルダ名のファイルIDと作成日時の辞書を取得する'''
folder_id = self.get_folder_id(folder_name)
query = f"trashed = false and '{folder_id}' in parents"
file_list = self.drive.ListFile({'q': query}).GetList()
file_dic = {}
for i in range(len(file_list)):
file_dic[file_list[i]['id']] = file_list[i]['createdDate']
return file_dic
def gdrive_upload(self, folder_id, content_file, file_name='no_name.csv'):
'''Upload file to Google Drive'''
metadata = {
'parents':[
{"id":folder_id}
],
'title': file_name,
'mimeType':'text/csv'
}
f = self.drive.CreateFile(metadata=metadata)
f.SetContentFile(content_file)
f.Upload()
def gdrive_download(self, folder_name=None, output_name=None, file_id=None, target_dir=""):
"""ファイルを現在のディレクトリまたは指定されたディレクトリにダウンロードする"""
if not file_id:
folder_id = self.get_folder_id(folder_name)
if not folder_id:
print("Folder not found")
return
file_id = self.get_file_id(folder_id=folder_id)
if not file_id:
print("File not found")
return
f = self.drive.CreateFile({'id': file_id})
f.FetchMetadata()
if not f.metadata.get('trashed', False):
f.GetContentFile(os.path.join(target_dir, output_name))
def download_from_file_name(self, folder_name, file_name, target_dir=""):
"""指定したフォルダ名とファイル名から特定のファイルをダウンロードする"""
folder_id = self.get_folder_id(folder_name)
if not folder_id:
print("Folder not found")
return
file_id = self.get_file_id(folder_name=folder_name, file_name=file_name)
if not file_id:
print("File not found")
return
f = self.drive.CreateFile({'id': file_id})
f.FetchMetadata()
if not f.metadata.get('trashed', False):
f.GetContentFile(os.path.join(target_dir, file_name))
ライブラリのインポート
from pydrive2.drive import GoogleDrive
from pydrive2.auth import GoogleAuth
import os
import pandas as pd
まず、必要なライブラリをインポートします。
pydrive2
はGoogle Drive APIを操作するためのライブラリで、GoogleDriveとGoogleAuthというクラスを使います。
os
はファイルパスの操作やディレクトリの作成など、オペレーティングシステムの機能を使うためのライブラリです。
pandas
はデータフレームを操作するためのライブラリです。
GoogleDriveAPIクラス
GoogleDriveAPIクラスは、Google Driveの操作を行うためのクラスです。
各メソッドはGoogle Drive APIを活用して、ファイルやフォルダの取得、ファイルのアップロード・ダウンロードなどを行います。
__init__
メソッド
def __init__(self):
self.gauth = GoogleAuth()
self.drive = GoogleDrive(self.gauth)
self.gauth.LocalWebserverAuth()
このメソッドは、GoogleDriveAPIクラスのインスタンスを初期化します。Google Driveとの接続と認証を行います。
get_folder_id
メソッド
def get_folder_id(self, folder_name):
"""フォルダ名からフォルダIDを取得"""
query = "trashed = false and mimeType='application/vnd.google-apps.folder' and title='{}'".format(folder_name)
folder_list = self.drive.ListFile({'q': query}).GetList()
if len(folder_list) > 0:
return folder_list[0]['id']
else:
return None
このメソッドは、指定されたフォルダ名に対応するフォルダIDを取得します。Google Driveでは、各フォルダやファイルは一意のIDを持っています。
get_file_id
メソッド
def get_file_id(self, folder_name=None, file_name=None, folder_id=None):
"""フォルダ内の指定された名前の最新ファイルのファイルIDを取得する"""
if not folder_id:
folder_id = self.get_folder_id(folder_name)
query = f"trashed = false and '{folder_id}' in parents and mimeType != 'application/vnd.google-apps.folder'"
if file_name:
query += f" and title = '{file_name}'"
file_list = self.drive.ListFile({'q': query}).GetList()
else:
file_list = self.drive.ListFile({'q': query}).GetList()
if len(file_list) > 0:
latest_file = max(file_list, key=lambda f: f['createdDate'])
return latest_file['id']
if file_list:
return file_list[0]['id']
else:
return None
このメソッドは、指定したフォルダ内の特定のファイル(または最新のファイル)のIDを取得します。
get_all_file_id
メソッド
def get_all_file_id(self, folder_name):
"""フォルダー名からフォルダを特定し、そのフォルダ内のファイルID一覧を取得する"""
folder_id = self.get_folder_id(folder_name)
file_list = self.drive.ListFile({'q': f"\'{folder_id}\' in parents"}).GetList()
file_id_list = []
for file in file_list:
file_id_list.append(file['id'])
return file_id_list
このメソッドは、指定したフォルダ内のすべてのファイルのIDを取得します。
get_file_df
メソッド
def get_file_df(self, folder_name):
"""フォルダ内のファイルIDと作成日時のデータフレームを取得する"""
folder_id = self.get_folder_id(folder_name)
query = f"trashed = false and '{folder_id}' in parents"
file_list = self.drive.ListFile({'q': query}).GetList()
file_id_list = []
for file in file_list:
file_id_list.append([file['title'] ,file['createdDate'] ,file['id']])
file_df = pd.DataFrame(file_id_list, columns=['title', 'createdDate', 'id'])
return file_df
このメソッドは、指定したフォルダ内のすべてのファイルの情報(ファイル名、作成日時、ID)をpandasのデータフレームとして取得します。
get_created_at
メソッド
def get_created_at(self, folder_name):
'''指定したフォルダ名のファイルIDと作成日時の辞書を取得する'''
folder_id = self.get_folder_id(folder_name)
query = f"trashed = false and '{folder_id}' in parents"
file_list = self.drive.ListFile({'q': query}).GetList()
file_dic = {}
for i in range(len(file_list)):
file_dic[file_list[i]['id']] = file_list[i]['createdDate']
return file_dic
このメソッドは、指定したフォルダ内のすべてのファイルの作成日時を取得します。戻り値は、ファイルIDをキーとし、作成日時を値とする辞書です。
gdrive_upload
メソッド
def gdrive_upload(self, folder_id, content_file, file_name='no_name.csv'):
'''Upload file to Google Drive'''
metadata = {
'parents':[
{"id":folder_id}
],
'title': file_name,
'mimeType':'text/csv'
}
f = self.drive.CreateFile(metadata=metadata)
f.SetContentFile(content_file)
f.Upload()
このメソッドは、指定したフォルダにファイルをアップロードします。ファイル名が指定されていない場合、デフォルトで'no_name.csv'という名前が付けられます。
gdrive_download
メソッド
def gdrive_download(self, folder_name=None, output_name=None, file_id=None, target_dir=""):
"""ファイルを現在のディレクトリまたは指定されたディレクトリにダウンロードする"""
if not file_id:
folder_id = self.get_folder_id(folder_name)
if not folder_id:
print("Folder not found")
return
file_id = self.get_file
_id(folder_id=folder_id)
if not file_id:
print("File not found")
return
f = self.drive.CreateFile({'id': file_id})
f.FetchMetadata()
if not f.metadata.get('trashed', False):
f.GetContentFile(os.path.join(target_dir, output_name))
このメソッドは、指定したフォルダからファイルをダウンロードします。ファイルのIDが指定されていない場合、そのフォルダ内の最新のファイルがダウンロードされます。
download_from_file_name
メソッド
def download_from_file_name(self, folder_name, file_name, target_dir=""):
"""指定したフォルダ名とファイル名から特定のファイルをダウンロードする"""
folder_id = self.get_folder_id(folder_name)
if not folder_id:
print("Folder not found")
return
file_id = self.get_file_id(folder_name=folder_name, file_name=file_name)
if not file_id:
print("File not found")
return
f = self.drive.CreateFile({'id': file_id})
f.FetchMetadata()
if not f.metadata.get('trashed', False):
f.GetContentFile(os.path.join(target_dir, file_name))
このメソッドは、指定したフォルダ内から特定の名前のファイルをダウンロードします。指定したフォルダ内に指定した名前のファイルが存在する場合、そのファイルがダウンロードされます。
使用方法
Pythonでは、一つのファイルに記述したクラスや関数を別のファイルから呼び出して使用することができます。これをモジュールと呼びます。
まず、上記で定義したGoogleDriveAPI
クラスを含むPythonファイル(たとえば、gdrive_api.py
)を作成します。
その後、別のPythonファイル(たとえば、main.py
)から以下のようにgdrive_api.py
をインポートしてGoogleDriveAPI
クラスを使用します。
from gdrive_api import GoogleDriveAPI
# GoogleDriveAPI クラスのインスタンスを作成
drive = GoogleDriveAPI()
# 例えば、特定のフォルダ名からフォルダIDを取得
folder_id = drive.get_folder_id("MyFolder")
print(folder_id)
この場合、gdrive_api.py
とmain.py
は同じディレクトリにある必要があります。
フォルダ構成が以下のようになっている場合、
.
├── main.py
└── lib
└── gdrive_api.py
以下のようにインポートします。
from lib.gdrive_api import GoogleDriveAPI
# GoogleDriveAPI クラスのインスタンスを作成
drive = GoogleDriveAPI()
# 例えば、特定のフォルダ名からフォルダIDを取得
folder_id = drive.get_folder_id("MyFolder")
print(folder_id)
このように、Pythonのモジュールシステムを利用することで、一つのファイルに記述したクラスや関数を他のファイルから呼び出して使用することができます。
例1:フォルダ名とファイル名を入力してファイルをダウンロードする
GoogleDriveAPI
クラスのdownload_from_file_name
メソッドを使って、指定したフォルダ名とファイル名からファイルをダウンロードする例を以下に示します。
まずは、gdrive_api.py
をインポートします。次に、GoogleDriveAPI
クラスのインスタンスを作成し、そのインスタンスのdownload_from_file_name
メソッドを呼び出します。
from gdrive_api import GoogleDriveAPI
# GoogleDriveAPI クラスのインスタンスを作成
drive = GoogleDriveAPI()
# フォルダ名とファイル名を指定
folder_name = "MyFolder"
file_name = "MyFile.txt"
# ダウンロード先のディレクトリを指定(オプション、指定しなければ現在のディレクトリにダウンロードされます)
target_dir = "/path/to/download/directory"
# ファイルをダウンロード
drive.download_from_file_name(folder_name, file_name, target_dir)
このスクリプトを実行すると、MyFolder
という名前のフォルダ内にあるMyFile.txt
という名前のファイルが、指定したディレクトリにダウンロードされます。
注意点としては、このスクリプトを実行するにはgdrive_api.py
が同じディレクトリに存在しているか、またはPythonのパスが正しく設定されている必要があります。また、GoogleDriveAPI
の認証が必要な場合は、適切に認証を行ってください。
例2:フォルダの中で作成日が一番新しいファイルをダウンロードする
GoogleDriveAPI
クラスのget_file_id
メソッドとgdrive_download
メソッドを使って、指定したフォルダ内で作成日が一番新しいファイルをダウンロードする例を以下に示します。
from gdrive_api import GoogleDriveAPI
# GoogleDriveAPI クラスのインスタンスを作成
drive = GoogleDriveAPI()
# フォルダ名を指定
folder_name = "MyFolder"
# 作成日が一番新しいファイルのIDを取得
file_id = drive.get_file_id(folder_name=folder_name)
# ファイル名を指定(この例ではダウンロードするファイルの元の名前を使います)
file_name = file_id # ここでは便宜上、ファイルIDをそのままファイル名として利用します。実際には適切な名前を指定してください。
# ダウンロード先のディレクトリを指定(オプション、指定しなければ現在のディレクトリにダウンロードされます)
target_dir = "/path/to/download/directory"
# ファイルをダウンロード
drive.gdrive_download(file_id=file_id, output_name=file_name, target_dir=target_dir)
このスクリプトを実行すると、MyFolder
という名前のフォルダ内で作成日が一番新しいファイルが、指定したディレクトリにダウンロードされます。
以上になります。
上記以外にもいろいろなメソッドが考えられるので、ぜひご自身の用途にあわせて試してみてください。
ちなみにこのモジュールを作ったのは1年くらい前ですが、私の環境では今でもきちんと動いています。
もし、ご自身の環境で動かない場合はご連絡ください。
Discussion