はじめまして。
ニフティ株式会社 新卒3年目の南川です。
今回はパソコン版Googleドライブのファイルやフォルダのパス(「G:/.」の形式)をブラウザからアクセス可能なURL(「https://drive.google.com/.」の形式)に変換する方法について話したいと思います。
パソコン版Googleドライブ
パソコン版Googleドライブを利用することで、クラウドとパソコンの間でファイルを同期することができ、Googleドライブのファイルをブラウザだけでなくエクスプローラーからアクセスできるようになります。
https://support.google.com/googleone/answer/10838124
ローカル上に仮想的にマウントされたファイルやフォルダは、Windows上では以下のようなパスで表記することができます。
例:G:\共有ドライブ\Drv_ほげほげグループ\ミーティング資料\2022-MM-DD_ふがふが.pptx
背景
Slack上では、Googleドライブ上のファイルやフォルダを先ほどのようなパス(「G:.*」の形式)で共有されることがあります。
Slackで検索すると、1万件以上ヒットします。
しかし、このようなパスで共有された場合、以下の問題が発生します。
Macユーザーがアクセスできない
「G:\」から始まるパスはWindowsユーザーであれば、基本的にエクスプローラーのアドレスバーに貼り付けることでファイルやフォルダを開くことができますが、Macユーザーはそのままだと開くことができません。
ファイル名や配置場所が変わった時にアクセスできなくなる
ファイル名や配置場所(つまりパス)が変わると、そのファイルやフォルダにアクセスすることができなくなります。一方、GoogleドライブのURLの場合、ファイル名が変わったり、ファイルを移動したりしてもアクセスすることができます。
こうした背景から、Googleドライブのパスをメンションで送ると、それをブラウザからアクセスできるURLに変換するSlack BOTを作成しました。
SlackBOTはAWS上で稼働しており、LambdaとAPI Gatewayで構成されています。また、Chaliceというサーバーレスアプリケーションフレームワークを使ってリソースを構築しています。
本記事では、このSlack BOTを作るうえでメインとなる「パソコン版GoogleドライブのファイルやフォルダのパスをURLに変換する方法」について説明していきます。
今回作るもの
今回は「Windowsにおけるパソコン版Googleドライブのファイルやフォルダのパス」を入力とし、「ブラウザからアクセス可能なGoogleドライブのURL」を出力するプログラムをPythonで実装します。
実行例は以下の通りです。 「hogehoge…」「fugafuga…」の部分はそれぞれファイル、フォルダのFile ID(後述)になります。
1 2 3 4 5 |
$ python main.py 'G:\共有ドライブ\Drv_ほげほげグループ\ミーティング資料\2022-MM-DD_ふがふが.pptx' https://drive.google.com/file/d/hogehoge... $ python main.py 'G:\共有ドライブ\Drv_ほげほげグループ\ミーティング資料\' https://drive.google.com/drive/folders/fugafuga... |
File ID
各ファイルやフォルダには、File IDと呼ばれる一意のIDを持っており、このFile IDはファイル名が変更されても固定されています。
File ID
A unique opaque ID for each file. File IDs are stable throughout the life of the file, even if the file name changes.https://developers.google.com/drive/api/guides/about-files#file_characteristics
先ほどの実行例の通り、Google DriveのファイルやフォルダのURLにはFile IDが含まれています。
全体の流れ
パソコン版GoogleドライブのファイルやフォルダのパスからGoogleドライブのURLに変換する大まかな流れは以下の通りです。
具体例とともに流れを説明していきます。
GoogleドライブのAPIを叩くためのサービスリソースを構築
Googleドライブのファイルやドライブを取得するAPIを叩くために、ログインを行いサービスリソースを構築します。ログインに成功するとtoken.jsonを作成し、2回目以降はこのファイルを用いるためログインが不要になります。
split_path = (Googleドライブのパスを整形して、階層ごとに分割したものの配列)
例えば、 ”G:\共有ドライブ\Drv_ほげほげグループ\ミーティング資料\2022-MM-DD_ふがふが.pptx” というGoogleドライブのパスが入力されたとします。これに対して先頭の ”G:\共有ドライブ\” の部分を除去して、階層ごとに分割を行います。こうして得られた配列は [“Drv_ほげほげグループ”, “ミーティング資料”, “2022-MM-DD_ふがふが.pptx”] となり、これをsplit_pathに代入します。
parent = (split_path[0]と同名の共有ドライブ)
先ほど得られた配列split_pathの先頭の要素(例における “Drv_ほげほげグループ”)と同名の共有ドライブを検索し、これによって得られたものをparentに代入します。
for name in split_path[1:] ~ forループ終了
split_pathの1番目以降の配列(例における [“ミーティング資料”, “2022-MM-DD_ふがふが.pptx”])の各要素nameに対して、parent直下のnameと同じファイル(フォルダ)を検索し、その結果をparentに代入します。
例に沿って説明すると、まず、 “Drv_ほげほげグループ” 直下の “ミーティング資料” という名前のファイルを検索し、それをfileとし、それをparentに代入します。次に “ミーティング資料” 直下の “2022-MM-DD_ふがふが.pptx” という名前のファイルを検索し、それをfileとし、それをparentに代入します。
ちなみに、Googleドライブにおいてフォルダはファイルの一種です。
File types
Google Drive describes files by types. This list shows all available file types:
・・・
Folder
A container you can use to organize other types of files on Drive.https://developers.google.com/drive/api/guides/about-files#types
return fileをもとに作成したURL
最後に、forループ内で最終的に得られたfileのFile IDをもとに作成したGoogleドライブのURLを返します。
手順
(1) 必要なパッケージをインストールする
Googleクライアントライブラリをpip installします。
1 |
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib |
(2) ソースコードを書く
「main.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 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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
import os.path import sys from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build # Scopeを変更した場合、token.jsonを削除すること。 SCOPES = [ 'https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/drive.readonly' ] def build_service(): """ APIを叩くためのサービスリソースを構築する。 ref. https://developers.google.com/drive/api/quickstart/python Returns ------- Resource サービスリソース ref. https://developers.google.com/drive/api/v3/reference#resource-types ref. https://developers.google.com/resources/api-libraries/documentation/drive/v3/python/latest/index.html """ credentials = None # token.jsonにはアクセストークンとリフレッシュトークンを保存される。 # 認証フローが初めて完了したときに自動的に作成される。 if os.path.exists('token.json'): credentials = Credentials.from_authorized_user_file( 'token.json', SCOPES) # 使用可能な(有効な)認証情報がない場合は、ユーザーにログインさせる。 if not credentials or not credentials.valid: if credentials and credentials.expired and credentials.refresh_token: credentials.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( 'credentials.json', SCOPES) credentials = flow.run_local_server(port=0) # 次回の実行のためにtoken.jsonを保存する with open('token.json', 'w') as token: token.write(credentials.to_json()) service = build('drive', 'v3', credentials=credentials) return service def convert_google_drive_path(service, gdrive_path): """ GoogleドライブのパスをURLに変換する。 Parameters ---------- service : Resource サービスリソース gdrive_path : str Googleドライブのパス Returns ------- str 変換後のURL """ # 検索に不要な部分を切り取る gdfs_prefix = 'G:\\共有ドライブ\\' if not gdrive_path.startswith(gdfs_prefix): return None gdrive_path = gdrive_path[len(gdfs_prefix):] if gdrive_path.endswith(os.sep): gdrive_path = gdrive_path[:-len(os.sep)] split_path = gdrive_path.split(os.sep) # 共有ドライブを取得する drive = search_drive(service, split_path[0]) if not drive: print(f'Drive is not found. {split_path[0]}') return None drive_id = drive.get('id') parent_id = drive_id for name in split_path[1:]: # 指定したフォルダ内のファイル(フォルダ)を探す file = search_file(service, drive_id, parent_id, name) if not file: print( f'File is not found. "{drive_id=}", "{parent_id=}", "{name=}"') return None parent_id = file.get('id') file_id = file.get('id') # ファイルかフォルダかによって出力するURLを変える mimeType = file.get('mimeType') base_url = 'https://drive.google.com/file/d/' # フォルダの場合は'application/vnd.google-apps.folder'となる # ref. https://developers.google.com/drive/api/guides/mime-types if mimeType == 'application/vnd.google-apps.folder': base_url = 'https://drive.google.com/drive/folders/' return base_url + file_id def search_drive(service, drive_name): """ 共有ドライブを取得する。 Parameters ---------- service : Resource サービスリソース drive_name : str 取得する共有ドライブ名 Returns ------- dict 取得した共有ドライブの情報 ref. https://developers.google.com/drive/api/v3/reference/drives """ query = f'name = "{drive_name}"' # 指定した名前の共有ドライブを検索 # ref. https://developers.google.com/drive/api/v3/reference/drives/list # ref. https://developers.google.com/resources/api-libraries/documentation/drive/v3/python/latest/drive_v3.drives.html#list response = service.drives().list(q=query).execute() result = response.get('drives', []) if len(result) == 0: return None return result[0] def search_file(service, drive_id, parent_id, file_name): """ 指定したフォルダ内のファイルを取得する。 Parameters ---------- service : Resource サービスリソース drive_id : str 共有ドライブのID parent_id : str 親フォルダのID file_name : str 取得するファイル名 Returns ------- dict 取得したファイルの情報 ref. https://developers.google.com/drive/api/v3/reference/files """ # クエリ:「親フォルダがparent_id」AND「名前がfile_name」 # ref. https://developers.google.com/drive/api/v3/reference/query-ref query = f"'{parent_id}' in parents and name = '{file_name}'" # ファイルを検索 # ref. https://developers.google.com/drive/api/v3/reference/files/list # ref. https://developers.google.com/resources/api-libraries/documentation/drive/v3/python/latest/drive_v3.files.html#list response = service.files().list(q=query, corpora='drive', driveId=drive_id, includeItemsFromAllDrives=True, supportsAllDrives=True).execute() return next(iter(response.get('files', [])), None) if __name__ == "__main__": service = build_service() google_drive_path = sys.argv[1] if len(sys.argv) >= 2 \ else input('input Google Drive path(G:\.*) > ') print(convert_google_drive_path(service, google_drive_path)) |
ソースコードの説明についてはコメントを参照ください。
(3) 認証情報を用意する
以下のページを参考にGCPプロジェクトを作成し、APIを有効化します。
参考:Create a Google Cloud project | Google Workspace for Developers | Google Developer
次に、以下のページを参考にOAuth クライアントIDを作成し、「credentials.json」という名前で保存します。
参考:Create access credentials | Google Workspace for Developers | Google Developers
そして、「credentials.json」を手順(2)で作成したmain.pyと同じ場所に配置します。
(4) 動作確認をする
以下のコマンドを実行すると、GoogleドライブのURLが出力されます。
1 |
python main.py <"G:\共有ドライブ\"から開始するファイルまたはフォルダのパス> |
初回実行時は認証画面がブラウザ上で開かれるので、認証をする必要があります。 認証成功すると認証情報ファイルtoken.jsonが生成されます。
二回目以降は生成されたtoken.jsonをもとに認証を行います。
終わりに
今回は “G:\共有ドライブ\” から始まるパソコン版GoogleドライブのパスをURLに変換するプログラムをPythonで実装しました。
似たようなことをやりたい方がいたら、ぜひ参考にしていただけると幸いです。