Прежде всего создаем сервисный аккаунт в консоли Google Cloud и для email сервисного аккаунта открываем доступ на редактирование необходимых папок. Не забудьте добавить в папку файлы, если их там нет, потому что файл нам понадобится, когда мы будем выполнять первый пример - скачивание файлов из Google Drive.
Как сделать сервисный аккаунт - https://youtu.be/Lxxge05UP8M
Сначала устанавливаем клиентскую библиотеку Google API для Python pip install --upgrade google-api-python-client
!pip install --upgrade google-api-python-client
И импортируем нужные модули или отдельные функции из библиотек.
Ниже будет небольшое описание импортируемых модулей. Это для тех кто хочет понимать, что импортирует, но большинство просто может скопировать импорты и вставить в ноутбук :)
Модуль service_account (https://github.com/googleapis/google-auth-library-python/blob/master/google/oauth2/service_account.py) из google.oauth2 понадобится нам для авторизации с помощью сервисного аккаунта. Классы MediaIoBaseDownload и MediaFileUpload, как ясно из названий, пригодятся, чтобы скачать или загрузить файлы. Эти классы импортируются из googleapiclient.http (https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/http.py) Функция build из googleapiclient.discovery (https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/discovery.py) позволяет создать ресурс для обращения к API, то есть это некая абстракция над REST API (https://developers.google.com/drive/api/v3/about-sdk), чтобы удобнее обращаться к методам API
from google.oauth2 import service_account
from googleapiclient.http import MediaIoBaseDownload,MediaFileUpload
from googleapiclient.discovery import build
import pprint
import io
pp = pprint.PrettyPrinter(indent=4)
Указываем Scopes. Scopes - это перечень возможностей, которыми будет обладать сервис, созданный в скрипте. Ниже приведены Scopes, которые относятся к API Google Drive (из официальной документации https://developers.google.com/identity/protocols/googlescopes). 01_drive_scopes.png Как видно, разные Scope предоставляют разный уровень доступа к данным. Нас интересует Scope "https://www.googleapis.com/auth/drive", который позволяет просматривать, редактировать, удалять или создавать файлы на Google Диске.
Также указываем в переменной SERVICE_ACCOUNT_FILE путь к файлу с ключами сервисного аккаунта.
SCOPES = ['https://www.googleapis.com/auth/drive']
SERVICE_ACCOUNT_FILE = '/home/makarov/local-grove-232309-ba66e9c14c1f.json'
Создаем Credentials (учетные данные), указав путь к сервисному аккаунту, а также заданные Scopes. А затем создаем сервис, который будет использовать 3ю версию REST API Google Drive, отправляя запросы из-под учетных данных credentials.
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
service = build('drive', 'v3', credentials=credentials)
Теперь можно получить список файлов и папок, к которым имеет доступ сервис. Для этого выполним запрос list, выдающий список файлов, со следующими параметрами:
results = service.files().list(pageSize=10,
fields="nextPageToken, files(id, name, mimeType)").execute()
Получили вот такие результаты
pp.pprint(results)
{ 'files': [ { 'id': '1xq6Ler0ypPimEFMxer_wtYlklNB7MCBoM6aSZft5dAc', 'mimeType': 'application/vnd.google-apps.spreadsheet', 'name': 'Sheet from csv'}, { 'id': '0B7TyWvrAtxvgc3RhcnRlcl9maWxl', 'mimeType': 'application/pdf', 'name': 'Getting started'}, { 'id': '1uuecd6ndiZlj3d9dSVeZeKyEmEkC7qyr', 'mimeType': 'application/vnd.google-apps.folder', 'name': 'Folder'}, { 'id': '1HKC4U1BMJTsonlYJhUKzM-ygrIVGzdBr', 'mimeType': 'text/csv', 'name': 'Data.csv'}, { 'id': '10MM2f3V98wTu7GsoZSxzr9hkTGYvq_Jfb2HACvB9KjE', 'mimeType': 'application/vnd.google-apps.spreadsheet', 'name': 'Sheet'}, { 'id': '1fWhi4Jigrwl5oY_iJw9KJ-Eqr7D71UkY', 'mimeType': 'application/octet-stream', 'name': 'Dashboard.pbix'}, { 'id': '1NL1V_32OtQfB7zqe6t-xvTbbLQYXv6WYn5oPCpbPlPM', 'mimeType': 'application/vnd.google-apps.presentation', 'name': 'Great data'}, { 'id': '1RCTL5RyvTO2N5YYZHvB6xVqOgmpMVi5Z', 'mimeType': 'text/plain', 'name': 'Query_2.sql'}, { 'id': '1zV9hyUJpCKiz2KNksyLH4JPMeX0H3e93', 'mimeType': 'text/csv', 'name': 'data (4).csv'}, { 'id': '1oVSIGdm37p73MRb6ZFu5b0Nu2WsNallG', 'mimeType': 'text/csv', 'name': 'data.csv'}], 'nextPageToken': '~!!~AI9FV7TeUokGsREbeTF26g2i2s7iQLD3phGpP-R1uTPIKLMTborvzj2sMiYYIkLRq8gV6ddiGSB8F635Lib8dAAeWnkTCQ_ydfDKHa6hhVAsy_DEfjNKvGDdKBwm0s4ME2HFox_glSWG4tIFLHqDJwaIOWCeeWhoTd95UgCAXqRyYuP7bn8LaIRmTY6aRRcP9gUMDvbCrmJ1OuqeGZZrYBwy04s3mkmgbASixrLIlE6W11_zayVIsbQ7awocrjqA4LkJvrIixB8Khe6wJuDbr0K7SBVkSN2BSgGMno85mKKB1jrzRz_BO9Veu9w6UNo4BBP7622LjUjYGeOKAULsJZVrLhMSZQXreh3ma26YzZ9x8knqZd74pY07737ermtZ-JRm2gDFwGLF'}
print(len(results.get('files')))
10
Получив из результатов nextPageToken мы можем передать его в следущий запрос в параметре pageToken, чтобы получить результаты следующей страницы. Если в результатах будет nextPageToken, это значит, что есть ещё одна или несколько страниц с результатами
nextPageToken = results.get('nextPageToken')
results_for_next_page = service.files().list(pageSize=10,
fields="nextPageToken, files(id, name, mimeType)",
pageToken=nextPageToken).execute()
print (results_for_next_page.get('nextPageToken'))
~!!~AI9FV7TeUokGsREbeTF26g2i2s7isfxV23d97r1_sbiwijI5J8KEW4bunZPFAOkihbDOmmzPJWXnGAfGeKjXPaslWWpbf3wmA3v2aRuZDAIxU_hFQceBEsE5H5b442BMyN53F0bCL-IYD7Hm3CjuVhtBLJfAwP7WEQxktiTNBfKS03TxdPcSwgo_fI2YH5MGPoy29AJetqc28uC0QAhZ_ARrEIIJY6H27AxAlzeY8AGQgv2wOruRZv_C2YlwshsLcDBEpTQ8nMTrGhFE78hW1X7lhUqvxR9orrM6E39fNY8mLhksNLsSyneBl0Iq0vhcC5xzh3dxPZPENQp3-ONJtY4QQ6qGIO2f6t68UQDxAo5Z_nZGO1DjfNonUTzulNKihzLiBPYqr9TN
Таким образом, мы можем сделать цикл, который будет выполняться до тех пор, пока в результатах ответа есть nextPageToken. Внутри цикла будем выполнять запрос для получения результатов страницы и сохранять результаты к первым полученным результатам
results = service.files().list(pageSize=10,
fields="nextPageToken, files(id, name, mimeType)").execute()
nextPageToken = results.get('nextPageToken')
while nextPageToken:
nextPage = service.files().list(pageSize=10,
fields="nextPageToken, files(id, name, mimeType, parents)",
pageToken=nextPageToken).execute()
nextPageToken = nextPage.get('nextPageToken')
results['files'] = results['files'] + nextPage['files']
print(len(results.get('files')))
25
Дальше давайте рассмотрим какие ещё поля можно использовать для списка возвращаемых файлов. Как я уже писал выше, со всеми полями можно ознакомиться по ссылке https://developers.google.com/drive/api/v3/reference/files. Давайте рассмотрим самые полезные из них:
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name, mimeType, parents, createdTime, permissions, quotaBytesUsed)").execute()
Отобразим один файл из результатов с расширенным списком полей. Как видно permissions содержит информацию о двух юзерах, один из которых имеет role = owner, то есть владелец файла, а другой с role = writer, то есть имеет право записи.
pp.pprint(results.get('files')[0])
{ 'createdTime': '2019-02-20T10:58:34.010Z', 'id': '1xq6Ler0ypPimEFMxer_wtYlklNB7MCBoM6aSZft5dAc', 'mimeType': 'application/vnd.google-apps.spreadsheet', 'name': 'Sheet from csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ'], 'permissions': [ { 'deleted': False, 'displayName': 'mamby-pamby', 'emailAddress': 'mamby-pamby@dulcet-order-232309.iam.gserviceaccount.com', 'id': '08455172423570013795', 'kind': 'drive#permission', 'role': 'writer', 'type': 'user'}, { 'deleted': False, 'displayName': 'Alexey Makarov', 'emailAddress': 'ax.makarov@gmail.com', 'id': '15194547887078870598', 'kind': 'drive#permission', 'photoLink': 'https://lh3.googleusercontent.com/a-/AAuE7mC0AQU5BwnLOBCG2aXOFeYMZLn4DzvfXC3NFTLp7g=s64', 'role': 'writer', 'type': 'user'}, { 'deleted': False, 'displayName': 'namby-pamby', 'emailAddress': 'namby-pamby@tensile-verve-232214.iam.gserviceaccount.com', 'id': '13173508647601803619', 'kind': 'drive#permission', 'role': 'writer', 'type': 'user'}, { 'deleted': False, 'displayName': 'mamby-pamby@local-grove-232309.iam.gserviceaccount.com', 'emailAddress': 'mamby-pamby@local-grove-232309.iam.gserviceaccount.com', 'id': '10834532000195393990', 'kind': 'drive#permission', 'role': 'owner', 'type': 'user'}], 'quotaBytesUsed': '0'}
Очень удобная штука, позволяющая сократить количество результатов в запросе, чтобы получать только то, что действительно нужно - это возможность задать параметры поиска для файлов. Например, мы можем задать в какой папке искать файлы, зная её id:
results = service.files().list(
pageSize=5,
fields="nextPageToken, files(id, name, mimeType, parents, createdTime)",
q="'1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ' in parents").execute()
pp.pprint(results['files'])
[ { 'createdTime': '2019-02-20T10:58:34.010Z', 'id': '1xq6Ler0ypPimEFMxer_wtYlklNB7MCBoM6aSZft5dAc', 'mimeType': 'application/vnd.google-apps.spreadsheet', 'name': 'Sheet from csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:27.052Z', 'id': '1RCTL5RyvTO2N5YYZHvB6xVqOgmpMVi5Z', 'mimeType': 'text/plain', 'name': 'Query_2.sql', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:08.602Z', 'id': '1zV9hyUJpCKiz2KNksyLH4JPMeX0H3e93', 'mimeType': 'text/csv', 'name': 'data (4).csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:08.602Z', 'id': '1oVSIGdm37p73MRb6ZFu5b0Nu2WsNallG', 'mimeType': 'text/csv', 'name': 'data.csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:08.602Z', 'id': '1dLFxGYn198eIpdrPoD47b0BhHT9umWeJ', 'mimeType': 'text/csv', 'name': 'data (2).csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}]
С особенностями функционирования поиска можно ознакомиться в документации (https://developers.google.com/drive/api/v3/search-parameters). Ещё один удобный способ поиска нужных файлов - по имени. Вот пример запроса, где мы ищем все файлы, содержащие в названии "data":
results = service.files().list(
pageSize=10,
fields="nextPageToken, files(id, name, mimeType, parents, createdTime)",
q="name contains 'data'").execute()
pp.pprint(results['files'])
[ { 'createdTime': '2019-02-19T15:38:59.907Z', 'id': '1HKC4U1BMJTsonlYJhUKzM-ygrIVGzdBr', 'mimeType': 'text/csv', 'name': 'Data.csv', 'parents': ['1uuecd6ndiZlj3d9dSVeZeKyEmEkC7qyr']}, { 'createdTime': '2019-02-19T15:22:16.418Z', 'id': '1NL1V_32OtQfB7zqe6t-xvTbbLQYXv6WYn5oPCpbPlPM', 'mimeType': 'application/vnd.google-apps.presentation', 'name': 'Great data', 'parents': ['1uuecd6ndiZlj3d9dSVeZeKyEmEkC7qyr']}, { 'createdTime': '2019-02-19T15:16:08.602Z', 'id': '1zV9hyUJpCKiz2KNksyLH4JPMeX0H3e93', 'mimeType': 'text/csv', 'name': 'data (4).csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:08.602Z', 'id': '1oVSIGdm37p73MRb6ZFu5b0Nu2WsNallG', 'mimeType': 'text/csv', 'name': 'data.csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:08.602Z', 'id': '1dLFxGYn198eIpdrPoD47b0BhHT9umWeJ', 'mimeType': 'text/csv', 'name': 'data (2).csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:08.602Z', 'id': '1bYubUxVCNXm0l2R8bckITKCeymxaBBX8', 'mimeType': 'text/csv', 'name': 'data (5).csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:08.602Z', 'id': '1udviwdMiKrzTAXb4SulJQ10-mczy5m3V', 'mimeType': 'text/csv', 'name': 'data (3).csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-19T15:16:07.316Z', 'id': '1HLIzBEdsb3uozK6OTuX1Mexhy0kSKnVv', 'mimeType': 'text/csv', 'name': 'data (1).csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}]
Условия поиска можно комбинировать. Возьмем условие поиска в папке и совместим с условием поиска по названию:
results = service.files().list(
pageSize=10,
fields="nextPageToken, files(id, name, mimeType, parents, createdTime)",
q="'1uuecd6ndiZlj3d9dSVeZeKyEmEkC7qyr' in parents and name contains 'data'").execute()
pp.pprint(results['files'])
[ { 'createdTime': '2019-02-19T15:38:59.907Z', 'id': '1HKC4U1BMJTsonlYJhUKzM-ygrIVGzdBr', 'mimeType': 'text/csv', 'name': 'Data.csv', 'parents': ['1uuecd6ndiZlj3d9dSVeZeKyEmEkC7qyr']}, { 'createdTime': '2019-02-19T15:22:16.418Z', 'id': '1NL1V_32OtQfB7zqe6t-xvTbbLQYXv6WYn5oPCpbPlPM', 'mimeType': 'application/vnd.google-apps.presentation', 'name': 'Great data', 'parents': ['1uuecd6ndiZlj3d9dSVeZeKyEmEkC7qyr']}]
Теперь рассмотрим как скачивать файлы из Google Drive. Для этого нам понадобится создать запрос request для получения файла. После этого задаем интерфейс fh для записи в файл с помощью библиотеки io, указав в filename название файла (таким образом, можно сохранять файлы из Google Drive сразу с другим названием). Затем создаем экземпляр класса MediaIoBaseDownload, передав наш интерфейс для записи файла fh и запрос для скачивания файла request. Следующим шагом скачиваем файл по небольшим кусочкам (чанкам) с помощью метода next_chunk.
Если из предыдущего описания вам мало что понятно, не запаривайтесь, просто укажите свой file_id и filename, и всё у вас будет в порядке.
file_id = '1HKC4U1BMJTsonlYJhUKzM-ygrIVGzdBr'
request = service.files().get_media(fileId=file_id)
filename = '/home/makarov/File.csv'
fh = io.FileIO(filename, 'wb')
downloader = MediaIoBaseDownload(fh, request)
done = False
while done is False:
status, done = downloader.next_chunk()
print ("Download %d%%." % int(status.progress() * 100))
Download 100%.
Файлы Google Sheets или Google Docs можно конвертировать в другие форматы, указав параметр mimeType в функции export_media (обратите внимание, что в предыдущем примере скачивания файла мы использоали другую функцию get_media). Например, файл Google Sheets можно конвертировать и скачать в виде файла Excel.
file_id = '10MM2f3V98wTu7GsoZSxzr9hkTGYvq_Jfb2HACvB9KjE'
request = service.files().export_media(fileId=file_id,
mimeType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
filename = '/home/makarov/Sheet.xlsx'
fh = io.FileIO(filename, 'wb')
downloader = MediaIoBaseDownload(fh, request)
done = False
while done is False:
status, done = downloader.next_chunk()
print ("Download %d%%." % int(status.progress() * 100))
Download 100%.
Затем скачанный файл можно загнать в датафрейм. Это достаточно простой способ получить данные из Google Sheet в pandas-dataframe, но есть и другие способы, например, воспользоваться библиотекой gspread (https://riptutorial.com/pandas/example/24056/collect-google-spreadsheet-data-into-pandas-dataframe).
import pandas as pd
df = pd.read_excel('/home/makarov/Sheet.xlsx')
df.head(5)
id | count | |
---|---|---|
0 | .p43DqAlRavBbg1oDFSFzu | 98 |
1 | 5NAEhw4KIcYpCdNzA/6xcu | 24 |
2 | HGnqYBm9w1egayK9eDHgBe | 12 |
3 | h52zj8PC1XJ1naMxhUyqE. | 11 |
4 | JbPx9urrduW6335HK4ljiu | 10 |
Рассмотрим простой пример загрузки файла в папку. Во-первых, нужно указать folder_id - id папки (его можно получить в адресной строке браузера, зайдя в папку, либо получив все файлы и папки методом list). Также нужно указать название name, с которым файл загрузится на Google Drive. Это название может быть отличным от исходного названия файла. Параметры folder_id и name передаем в словарь file_metadata, в котором задаются метаданные загружаемого файла. В переменной file_path указываем путь к файлу. Создаем объект media, в котором будет указание по какому пути находится загружаемый файл, а также указание, что мы будем использовать возобновляемую загрузку, что позволит нам загружать большие файлы. Google рекомендует (https://developers.google.com/drive/api/v3/manage-uploads) использовать этот тип загрузки для файлов больше 5 мегабайт. Затем выполняем функцию create, которая позволит загрузить файл на Google Drive.
folder_id = '1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ'
name = 'Script_2.py'
file_path = '/home/makarov/Script.py'
file_metadata = {
'name': name,
'parents': [folder_id]
}
media = MediaFileUpload(file_path, resumable=True)
r = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
pp.pprint(r)
{'id': '1i4k58QfB7TxhUcAHYo_vGHR1SjRy3Gub'}
Как видно выше, при вызове функции create возвращается id созданного файла. Можно удалить файл, вызвав функцию delete. Но мы этого делать не будет так как файл понадобится в следующем примере
service.files().delete(fileId='1GDvLONfizF6VR7DKQaZsg_Wpdh2GFD5f').execute()
''
Сервисный аккаунт может удалить ли те файлы, которые были с помощью него созданы. Таким образом, даже если у сервисного аккаунта есть доступ на редактирование папки, то он не может удалить файлы, созданные другими пользователями. Понять что файл был создан помощью сервисного аккаунта можно задав поисковое условие с указанием email нашего сервисного аккаунта. Узнать email сервисного аккаунта можно вызвав атрибут signer_email у объекта credentials
print (credentials.signer_email)
mamby-pamby@local-grove-232309.iam.gserviceaccount.com
results = service.files().list(
pageSize=10,
fields="nextPageToken, files(id, name, mimeType, parents, createdTime)",
q="'mamby-pamby@local-grove-232309.iam.gserviceaccount.com' in owners").execute()
pp.pprint(results['files'][0:3])
[ { 'createdTime': '2019-02-20T11:09:17.156Z', 'id': '1i4k58QfB7TxhUcAHYo_vGHR1SjRy3Gub', 'mimeType': 'text/x-python', 'name': 'Script_2.py', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-20T10:58:34.010Z', 'id': '1xq6Ler0ypPimEFMxer_wtYlklNB7MCBoM6aSZft5dAc', 'mimeType': 'application/vnd.google-apps.spreadsheet', 'name': 'Sheet from csv', 'parents': ['1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ']}, { 'createdTime': '2019-02-20T09:35:18.684Z', 'id': '0B7TyWvrAtxvgc3RhcnRlcl9maWxl', 'mimeType': 'application/pdf', 'name': 'Getting started', 'parents': ['0ALTyWvrAtxvgUk9PVA']}]
Дальше - больше. С помощью API Google Drive мы можем загрузить файл с определенным mimeType, чтобы Drive понял к какому типу относится файл и предложил соответсвующее приложение для его открытия.
folder_id = '1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ'
name = 'Sample data.csv'
file_path = '/home/makarov/sample_data_1.csv'
file_metadata = {
'name': name,
'mimeType': 'text/csv',
'parents': [folder_id]
}
media = MediaFileUpload(file_path, mimetype='text/csv', resumable=True)
r = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
pp.pprint(r)
{'id': '1BxoPJhwEfp2SrNupf_gSbqyjcscqizDC'}
Но ещё более классная возможность - это загрузить файл одного типа с конвертацией в другой тип. Таким образом, мы можем залить csv файл из примера выше, указав для него тип Google Sheets. Это позволит сразу же конвертировать файл для открытия в Гугл Таблицах. Для этого надо в словаре file_metadata указать mimeType "application/vnd.google-apps.spreadsheet".
folder_id = '1mCCK9QGQxLDED8_pgq2dyvkmGRXhWEtJ'
name = 'Sheet from csv'
file_path = '/home/makarov/notebooks/sample_data_1.csv'
file_metadata = {
'name': name,
'mimeType': 'application/vnd.google-apps.spreadsheet',
'parents': [folder_id]
}
media = MediaFileUpload(file_path, mimetype='text/csv', resumable=True)
r = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
pp.pprint(r)
{'id': '1HpM2Pm6wP5RhizcRyVpcainG9ThZ_TL1bnSbodebiww'}
Ещё одна часто необходимая функция - это создание папок. Тут всё просто, создание папки также делается с помощью метода create, надо только в file_metadata указать mimeType "application/vnd.google-apps.folder"
folder_id = '1uuecd6ndiZlj3d9dSVeZeKyEmEkC7qyr'
name = 'New Folder'
file_metadata = {
'name': name,
'mimeType': 'application/vnd.google-apps.folder',
'parents': [folder_id]
}
r = service.files().create(body=file_metadata,
fields='id').execute()
pp.pprint(r)
{'id': '1VSa_V3hBIkHASHc1pl8ykoChn968AGu5'}
В этой статье мы рассмотрели лишь немногие возможности API Google Drive, но одни из самых необходимых: