🙌

[Python]GoogleDriveAPIの基本的な使い方

2021/05/04に公開

導入方法

導入方法はこれまでの記事で紹介しています。参考にしてみてください。

https://zenn.dev/wtkn25/articles/python-googledriveapi-auth

準備

以降の記述は、ここまで書いていることを前提にしています。あとは鍵などの事前準備ができていることですね。

from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools

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()))

【create】フォルダ作成

まずはフォルダの作成から。

drive_service = build('drive', 'v3', http=http_auth)

'''ここから'''
file_metadata = {
    'name': 'フォルダ名',
    'mimeType': 'application/vnd.google-apps.folder'
    'parents': ['格納するフォルダのID']
}
drive_service = drive.files().create(body=file_metadata,
                            fields='id').execute()

#fieldに指定したidをfileから取得できる
print ('Folder ID: %s' % file.get('id'))

ファイル作成時の戻り値が、フォルダのIDを含んでいるため取得して表示できます。ちなみにこれをうまく利用すると、**作成したフォルダのIDを取得しておいて、そのフォルダにファイルをアップロードをする。**というプログラムとかも書けちゃいますね。

参考[フォルダ作成]
https://developers.google.com/drive/api/v3/folder

【MediaFileUpload, create】ファイルをアップロードする

次にファイルをアップロードする方法です。

from apiclient.http import MediaFileUpload

#~省略~
drive_service = build('drive', 'v3', http=http_auth)

'''ここから'''
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'))

別途、importしたMediaFileUploadというクラスを用いて操作します。おおよそbodyにGoogleDrive側の情報、media_bodyにアップロードしたいファイルの情報を入れる。って感じですね。

参考
https://developers.google.com/drive/api/v3/manage-uploads
https://developers.google.com/drive/api/v3/folder

【delete】ファイル,フォルダの削除

drive_service = build('drive', 'v3', http=http_auth)

'''ここから'''
file = drive_service.files().delete(
    fileId='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
).execute()

#fieldに指定したidをfileから取得できる
print('File ID: %s' % file.get('id'))

deleteメソッドのキーワード引数に削除したいファイルまたはフォルダのIDを代入して実行してください。

例に漏れず、ファイル削除時の戻り値がフォルダのIDを含んでいるため、取得して表示できますね。

【list】ファイルやフォルダの一覧を取得する

色々わかりづらい感じのlistについてまとめます。

drive_service = build('drive', 'v3', http=http_auth)

results = drive_service.files().list(
    q="name = 'テスト' or name contains '試験'"
    pageSize=10, 
    fields="files(id, name)"
).execute()
items = results.get('files', [])

if not items:
    print('No files found.')
else:
    print('Files:')
    for item in items:
        print(u'{0} ({1})'.format(item['name'], item['id']))

listに指定できるキーワード引数

引数名 内容
corpora String どこを対象に検索するのか指定するということでしょうかね 「user」、「domain」、「drive」、および「allDrives(効率的に非推奨?)」のいずれかを指定します。 デフォで「user」らしい 参考[corporaについての言及] https://www.milk-island.net/translate/ggd/drive/api/v3/enable-shareddrives.html
spaces String コーパス内でクエリするスペースのカンマ区切りリストでサポートされている値は「drive」、「appDataFolder」、および「photos」 つまりどういうことだ。これの指定よってドライブ以外にもappDataFolderとかも検索対象に含められるということだろうか。
q String 検索条件を指定できる https://developers.google.com/drive/api/v3/search-files https://developers.google.com/drive/api/v3/reference/query-ref 複数条件があっても一つの文字列で表現する。条件のつなぎ方は「 and 」や「 or 」 【例】q = “name contains ‘hello’ and name contains ‘goodbye'”
driveId String 検索する共有ドライブのID。corporaに「drive」か「AllDrives」を指定しているときにつけるみたい。 指定しなくても共有ドライブ探してるっぽいけどそういうことではないのかな?
fields String レスポンスにどのような値を含めるか指定できる。代表格はidやname パフォーマンスを考慮して必要なフィールドだけ選ばせているみたい。 【例】fields=”nextPageToken, files(id, name)” この例であれば、nextPageTokenキーの値、filesキー内のidキーの値、nameキーの値を取得 ほかにどんな値が取得できるから以下から探して下さいhttps://developers.google.com/resources/api-libraries/documentation/drive/v3/python/latest/drive_v3.files.html#list
orderBy String 【例】orderBy=”folder,modifiedTime desc” フォルダでソートし、修正時間の降順でさらにソートしている 項目はカンマ区切りで、降順ならスペースdescでできる 使い方がよくわからない指定項目もあるけど、以下の指定ができる ‘createdTime’ ‘folder’ ‘modifiedByMeTime’ ‘modifiedTime’ ‘name’ ‘name_natural’ ‘quotaBytesUsed’ ‘recency’ ‘sharedWithMeTime’ ‘starred’ ‘viewedByMeTime’
pageSize integer 1ページ当たりの処理(表示)件数。
pageToken String 次のページを呼び出すのに必要なトークン

参考[listに指定できるキーワード引数]https://developers.google.com/drive/api/v3/reference/files/list

ページという概念

GoogleDriveAPiでファイルを検索する時に、1回のリクエストに対して返せるレスポンスに含める情報には限界があります。そのため、レスポンスは「10個あったからその情報を返すよ。でもまだあるかも!」という感じになっています。その、「まだ返せる情報があるかどうか」を持っているのがnextPageTokenです。Noneが入っているならば、検索結果は出し尽くしたということですね。

以下のプログラムではnextPageTokenがある間はリクエストし続けています。そうすることで条件に合うす全てのフォルダを検索できるということですね。

pageSizeの限界値はわかりませんが、そういうことです。

page_token = None
while True:
    response = drive.files().list(
        spaces='drive',
        fields='nextPageToken, files(id, name)',
        pageToken=page_token
    ).execute()
    for file in response.get('files', []):
        print('Found file: %s (%s)(%s)' %
              (file.get('name'), file.get('id'), file.get('parent')))
    page_token = response.get('nextPageToken', None)
    if page_token is None:
        break

参考[nextPageTokenに関して言及があったところ]
https://developers.google.com/drive/api/v3/reference/files/list

【余談】q(query)のおすすめの書き方

listメソッドのキーワード引数qに渡すのは、1つの文字列です。そのため、変数で動的に変化させたい条件があった時、複数の条件で検索したい時に、文字連結の連続でわかりにくくなってしまいます。

そのため、以下のように配列で複数の条件を書きますそうしたら最後にそれを「and」で連結してしまいます。「or」を混在させたい場合はちょっと厄介ですが、1つの例として参考にしてみてください。

condition_list = [
    "'xxxxxxxxxxxxxxxxxxx' in parents",
    "name = '%s'" % name
]
conditions = " and ".join(condition_list)

results = drive.files().list(
    q=conditions
    pageSize=10, fields="nextPageToken, files(id, name)"
).execute()
items = results.get('files', [])

Discussion