はじめに
この記事では、PythonでGoogleドライブを操作するパッケージの中からPyDrive2(GitHub)を取り上げます。
PyDrive2からDrive APIを介してGoogleドライブを操作し、指定したドキュメントを複製する方法を備忘録としてまとめます。
PyDrive2について
PyDrive2はPyDriveのメンテナンスされたフォークです。
PyDriveの更新は1年以上前から止まっており、2020年1月にPyDrive2がリリースされています。
もう少し詳しい説明や、PyDrive2を使う際の認証まわりの設定については以下をご確認ください。
PythonでGoogleドライブを操作するパッケージPyDrive2の認証まわり
なお、PyDriveやPyDrive2は、Drive API v2にアクセスします。
Drive APIにはv3もあるため、ご注意ください。
動作環境
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.6
BuildVersion: 18G3020
$ python -V
Python 3.8.1
$ pip list | grep PyDrive2
PyDrive2 1.4.10
PythonでGoogleドライブを操作するパッケージPyDrive2の認証まわりに沿って、ファイルを配置しています。
.
├── my_client_secrets.json
├── saved_credentials.json
└── settings.yaml
やりたいこと
Googleドキュメントの議事録テンプレートの複製をPythonスクリプトにやらせます。
背景
MTGの際にGoogleドキュメントに共同で議事録をとっています。
MTGのたびに議事録テンプレートを複製するのが退屈に感じ、自動化を考えるようになりました。
そこで、自動化の一歩目として、GoogleドキュメントのIDを指定したら複製するスクリプトを作ってみました。
GoogleドキュメントのIDとは
GoogleドキュメントのURLは以下のようになっています。
https://docs.google.com/document/d/1******************************************o/edit#
このうちの 1******************************************o
の部分がIDです(マスクする意味でアスタリスクを使っています)。
指定したドキュメントを複製するPythonスクリプト
import argparse
from pydrive2.auth import GoogleAuth
from pydrive2.drive import GoogleDrive
def main():
# コマンドラインから渡された引数の解析 (1)
parser = argparse.ArgumentParser()
parser.add_argument("source_id")
args = parser.parse_args()
source_id = args.source_id
# Googleドライブを操作するためのオブジェクトを取得 (2)
gauth = GoogleAuth()
gauth.LocalWebserverAuth()
drive = GoogleDrive(gauth)
# コピー元のファイルの名前に copied_ をつけたファイル名とする (3)
source = drive.CreateFile({"id": source_id})
source.FetchMetadata("title")
dest_title = f"copied_{source['title']}"
# コピーを作成 (4)
copied_file = {"title": dest_title}
f = (
drive.auth.service.files()
.copy(fileId=source_id, body=copied_file)
.execute()
)
# 作成したファイルにアクセスするためのURLを表示 (5)
dest = drive.CreateFile({"id": f["id"]})
dest.FetchMetadata("alternateLink")
print(dest["alternateLink"])
if __name__ == "__main__":
main()
実行結果
.
├── copy_template.py
├── my_client_secrets.json
├── saved_credentials.json
└── settings.yaml
$ python copy_template.py --help
usage: copy_template.py [-h] source_id
positional arguments:
source_id
optional arguments:
-h, --help show this help message and exit
実行すると、複製した結果のドキュメントのURLが表示されます。
$ python copy_template.py 1******************************************o
https://docs.google.com/document/d/1******************************************c/edit?usp=drivesdk
コード解説
PyDriveのドキュメント1には指定したドキュメントを複製(コピー)する方法は見つけられませんでした。
そこでPyDriveのIssueを検索し、参考にできそうなIssueを見つけました。
Create a copy of the file #85 (Comment)
こちらに沿って実装しています。
複製の仕組み(コード (4))
認証結果を元に、Googleドライブを操作するためのオブジェクト(gdrive
)を作成します。
複製には、複製元のドキュメントのIDと、複製先のドキュメントのタイトルが必要です。
- 複製元のドキュメントのID:コマンドラインから渡される
- 複製先のタイトル:決め打ちでもOK。今回は複製元のドキュメントのタイトルに
copied_
というprefixをつける
Googleドライブのファイルを取得する(コード (3))
gdrive
オブジェクトからGoogleドライブ上のファイルを操作するには、CreateFile
メソッドを使います。
source = drive.CreateFile({"id": source_id})
CreateFile
メソッドの返り値はGoogleDriveFile
インスタンスです。
IDを指定してCreateFile
し、該当のファイルをGoogleDriveFile
インスタンスを介して操作できるようにします。
Googleドライブのファイルのメタデータを取得する(コード (3),(5))
Googleドライブのファイルには様々な情報(メタデータ)がありますが、上記の方法ではIDしか取得できていません。
取得したい情報がある場合は、
-
GoogleDriveFile
のFetchMetadata
メソッドで取得したい属性を指定
- 例:
source.FetchMetadata("title")
- 取得したい属性名をキーにしてアクセス
- 例:
source['title']
という手順になります。
1で取得したい属性を指定する場合は、以下のリファレンスに沿って指定します。
https://developers.google.com/drive/api/v2/reference/files
- title: The title of this file.
- alternateLink: A link for opening the file in a relevant Google editor or viewer.
Googleドライブのファイルをコピーする(コード (4))
GoogleDrive
やGoogleDriveFile
にはファイルのコピーを作成するメソッドがないので、PyDrive2がラップしているgoogle-api-python-client
から、Files: copy
エンドポイントを直接呼び出しているようです。
共有ドライブだと動かない?
共有ドライブにあるドキュメントのIDを指定したい場合は、コピーするコード (4) の変更が必要です。
supportsAllDrives
引数にTrue
を渡します。
# コピーを作成 (4)
copied_file = {"title": dest_title}
f = (
drive.auth.service.files()
.copy(fileId=source_id, body=copied_file, supportsAllDrives=True)
.execute()
)
https://developers.google.com/drive/api/v2/enable-shareddrives によると、
supportsAllDrives — Whether the requesting application supports both My Drives and shared drives. If false, then shared drive items are not included in the response.
なので、True
と指定することで共有ドライブのファイルも操作できます。
この引数を指定しない場合は、File not found
とDrive APIから返ってきます。
supportsAllDrives=True
が指定されていても、マイドライブのファイルはコピーできます。
そのため、常に指定してもいいと思いますが、2020年6月以降は指定不要になるようです。
Files: copy
エンドポイントのドキュメントによると、2020年6月1日以降はsupportsAllDrives引数が廃止され、指定しなくても共有ドライブのファイルを操作できる2そうです。
共有ドライブの操作は、PyDriveにいくつかIssueが見られました3が、PyDrive2では対応済み4のようです。
スコープが絞れるか
PythonでGoogleドライブを操作するパッケージPyDrive2の認証まわり にて述べたように、広めのスコープ(https://www.googleapis.com/auth/drive
)を使っています。
ファイルの複製をする上でこれを絞れるか指定を試したのですが、絞れませんでした5。
Files: copy
エンドポイントのスコープを参照したところ、https://www.googleapis.com/auth/drive
からは絞れなさそうとわかりました。
終わりに
PyDrive2でGoogleドキュメントのIDを指定して複製する方法についてまとめました。
ドキュメントもスプレッドシートもGoogleドライブから見ればどちらもファイルです。
ですので、この方法でドキュメント以外も複製できると考えています(後日検証予定)。
-
PyDrive2のドキュメントとしてPyDriveのドキュメントが案内されています(2020年5月時点) ↩
-
「Deprecated - Whether the requesting application supports both My Drives and shared drives. This parameter will only be effective until June 1, 2020. Afterwards all applications are assumed to support shared drives. (Default: false)」 ↩
-
2020年1月のコミットで取り込まれていました ↩
-
スコープとして
drive.file
やdrive.metadata
やその組合せを試しましたが、The user has not granted the app {appId} {verb} access to the file {fileId} というエラーが解消できていません ↩