Найти тему
КиберMamedov 💻🔥

Скачать файловую структуру с Google Диска через Python: простой и быстрый способ

Подпишись на канал
Подпишись на канал

Насчет быстрого способа я возможно замахнулся, же придется копировать много кода из статьи 🙂.

Для работы с Google Drive в Python будем использовать библиотеку pydrive. Это удобная библиотека, которая позволяет взаимодействовать с Google Drive через Python.

Прежде всего, нужно установить pydrive. Вы можете установить его с помощью pip:

pip install pydrive

Создайте файл nautilus.py.

Теперь импортируйте необходимые классы и библиотеки. Из модуля auth библиотеки pydrive импортируем класс для прохождения аутентификации GoogleAuth. Для работы с файлами на Google Диске импортируем класс GoogleDrive из модуля drive библиотеки pydrive. Также для работы с файловой системой на компьютере, где будет запускаться программа необходимо импортировать библиотеку os.

from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
import os

Создайте экземпляр GoogleAuth и выполните аутентификацию с использованием локального веб-сервера. Для этого напишите следующий код:

gauth = GoogleAuth()
gauth.LocalWebserverAuth()

Метод LocalWebserverAuth класса GoogleAuth запускает локальный веб-сервер, который позволяет запустить браузер и пройти аутентификацию по одной из учетных записей в Google.

Но чтобы это сделать, в первую очередь необходимо создать приложение в Google Console.

  1. Перейдите в Консоль API (APIs Console) и создайте свой собственный проект;
  2. Найдите 'API Google Drive', выберите соответствующую запись и нажмите 'Включить';
  3. Выберите "Учетные данные" в левом меню, нажмите "Создать учетные данные", затем выберите "Идентификатор клиента OAuth". Важно: обязательно опубликуйте проект4
  4. Теперь вам нужно задать название продукта и экран согласия -> нажмите "Настроить экран согласия" и следуйте инструкциям. После завершения:
  • Выберите "Тип приложения";
  • Веб-приложение;
  • Введите подходящее имя;
  • Введите http://localhost:8080 для "Авторизованные источники JavaScript";
  • Введите http://localhost:8080/ для "Авторизованных URI перенаправления". - Нажмите 'Сохранить';
  1. Нажмите "Скачать JSON" справа от идентификатора клиента, чтобы загрузить client_secret_<действительно длинный идентификатор>.json;
  2. Загруженный файл содержит всю информацию об аутентификации вашего приложения;
  3. Переименуйте файл в "client_secrets.json", это важно. Затем поместите его в свою рабочую директорию, где создали файл nautilus.py.

Теперь аутентификация пройдет успешно, запустите приложение, после чего запустится страница браузера, далее разберетесь.

Аутентификация производится, следовательно, можно переходить к работе с функцией по работе с файлами. Но для этого важно вначале создать экземпляр класса по работе с диском.

drive = GoogleDrive(gauth)

Теперь определим функцию:

def download_file(folder_id='root', current_path=[], base_folder='download/'):
# Код для перечисления и загрузки файлов будет здесь

return

  • folder_id='root' - это идентификатор папки на Google Диске, который указывает из какой папки выбирать все файлы. Значение root означает корневая папка диска;
  • current_path=[] - у может быть много подпапок и используя рекурсию мы будем их извлекать. Для этого нужен данный аргумент, чтобы хранить последовательность папок;
  • base_folder='download/' - папка в которую нужно начинать загружать файлы на компьютер, где запущена программа.

Получаем список файлов с диска:

file_list = drive.ListFile({'q': f"'{folder_id}' in parents and trashed=false"}).GetList()

Используя цикл пробежимся по каждому файлу:

for file in file_list:
# Код для обработки каждого файла будет здесь

Если файл является папкой, создадим соответствующую локальную папку и рекурсивно вызовите функцию download_file:

if "folder" in file_type:
# Код для создания локальной папки и загрузки файлов внутри нее будет здесь

Если файл не является папкой, тогда загрузим его и сохраним на компьютере:

else:
# Код для загрузки файла и его сохранения локально будет здесь

В результате, в конце кода вызываем функцию download_file с соответствующими параметрами, чтобы начать процесс загрузки:

download_file()

Получился вот такой каркас:

from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
import os
# Аутентификация
gauth = GoogleAuth()
gauth.LocalWebserverAuth()
# Работа с диском
drive = GoogleDrive(gauth)
def download_file(folder_id='root', current_path=[], base_folder='download/'):
file_list = drive.ListFile({'q': f"'{folder_id}' in parents and trashed=false"}).GetList()
for file in file_list:
# Код для обработки каждого файла будет здесь
if "folder" in file_type:
# Код для создания локальной папки и загрузки файлов внутри нее будет здесь
else:
# Код для загрузки файла и его сохранения локально будет здесь
return
download_file() # Вызываем функцию

Теперь наполняем заготовку кодом. Начнем с первых строк цикла:

for file in file_list:
file_id = file['id']
file_name = file["title"]
file_type = file["mimeType"]
file = drive.CreateFile({'id': file_id})
target_folder_path = os.path.join(base_folder, *current_path)

В данном участке кода происходит итерация по списку файлов file_list, полученных из Google Диска. Для каждого файла из списка выполняются следующие действия:

  1. Получение идентификатора файла (file_id) из словаря file;
  2. Получение имени файла (file_name) из словаря file;
  3. Получение типа файла (file_type) из словаря file.

Далее создается объект файла file с использованием drive.CreateFile({'id': file_id}). Этот объект будет использоваться для выполнения дальнейших операций с файлом, таких как загрузка или получение его содержимого.

Затем определяется путь к целевой папке (target_folder_path) с помощью функции os.path.join(). Путь к целевой папке формируется путем объединения базовой папки (base_folder) и текущего пути (current_path), используя оператор * для распаковки элементов списка current_path.

Например, если базовая папка (base_folder) равна 'download/', а текущий путь (current_path) равен ['folder1', 'folder2'], то target_folder_path будет равен 'download/folder1/folder2'.

Этот путь к целевой папке будет использоваться для сохранения загружаемого файла или для создания вложенной папки, если файл является папкой.

Теперь переходим к наполнению условия, которое отвечает за то, что мы попали не на файл, а на папку.

if "folder" in file_type:
current_path.append(file_name)
target_folder_path = os.path.join(target_folder_path, file_name)
try:
os.mkdir(target_folder_path)
except FileExistsError:
print("Папка уже существует")
download_file(f'{file_id}', current_path)
current_path.pop()

В данном участке кода проверяется, является ли файл папкой. Если тип файла содержит строку "folder", то выполняются следующие действия:

  1. Добавление имени папки (file_name) в конец списка current_path с помощью метода append(). Это позволяет отслеживать текущий путь к папке при рекурсивном обходе вложенных папок;
  2. Формирование нового пути к целевой папке (target_folder_path) с помощью функции os.path.join(). В этом случае, имя папки (file_name) добавляется к текущему пути (target_folder_path), чтобы указать путь к создаваемой вложенной папке;
  3. Попытка создания новой папки с помощью функции os.mkdir(). Если папка уже существует, возникнет исключение FileExistsError, и будет выведено сообщение "Папка уже существует";
  4. Рекурсивный вызов функции download_file() с новыми параметрами: идентификатором папки (file_id) и обновленным текущим путем (current_path). Это позволяет загрузить файлы, находящиеся внутри этой папки;
  5. Удаление последнего элемента из списка current_path с помощью метода pop(). Это выполняется для возврата к предыдущему пути после завершения загрузки файлов в текущей папке.

Таким образом, данный участок кода обеспечивает рекурсивную загрузку файлов из вложенных папок на Google Диске. Он обрабатывает каждую папку, создает соответствующую локальную папку, загружает файлы внутри этой папки и затем переходит к следующей папке.

Теперь осталось наполнить самую интересную часть кода в else:

else:
path = os.path.join(target_folder_path, file_name)
if file_type == 'application/vnd.google-apps.document':
GoogleFiles(file_id, file_type, path)
elif file_type == 'application/vnd.google-apps.spreadsheet':
GoogleFiles(file_id, file_type, path)
elif file_type == 'application/vnd.google-apps.presentation':
GoogleFiles(file_id, file_type, path)
else:
try:
file.GetContentFile(path)
except:
print("Невозможно загрузить этот файл")

В данном участке кода происходит обработка файлов, которые не являются папками.

  1. Создается переменная path, в которой формируется путь к целевой папке (target_folder_path) путем объединения этой папки и имени файла (file_name) с помощью функции os.path.join();
  2. Затем происходит проверка типа файла (file_type) с помощью условных операторов if, elif и else;
  3. Если тип файла соответствует 'application/vnd.google-apps.document', 'application/vnd.google-apps.spreadsheet' или 'application/vnd.google-apps.presentation', создается экземпляр класса GoogleFiles с передачей ему идентификатора файла (file_id), типа файла (file_type) и пути (path). Однако, в данном коде не предоставлено определение класса GoogleFiles, поэтому его реализацию добавим чуть позже и сделаем это в отдельном классе. Вся проблема, из-за которой приходится это делать заключается в том, что pydrive не работает с документами, презентациями и таблицами не являющимися файлами, а просто Google Документы. Фактически они не являются физическими файлами, поэтому придется обходить эту ситуацию используя другую библиотеку;
  4. Если тип файла не соответствует указанным выше типам, выполняется блок else. В этом блоке файл загружается и сохраняется локально с помощью метода GetContentFile() объекта file. Если загрузка файла не удалась, будет выведено сообщение "Невозможно загрузить этот файл".

Раз уж мы обращаемся к классу GoogleFiles, то давайте его подключим. Предлагаю назвать файл, в котором будет храниться данный класс downloader_google_files.py.

from downloader_google_files import GoogleFiles

Теперь пойдем в этот и напишем его.

# Аутентификация через Google API
SCOPES = ['https://www.googleapis.com/auth/drive']
creds = None
creds = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json', SCOPES).run_local_server()
service = build('drive', 'v3', credentials=creds)
class GoogleFiles:
def __init__(self, file_id, file_type, path):
self.path = path
if file_type == 'application/vnd.google-apps.document':
mime_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
self.download(file_id, mime_type, path+".docx")
elif file_type == 'application/vnd.google-apps.spreadsheet':
mime_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
self.download(file_id, mime_type, path+".xlsx")
elif file_type == 'application/vnd.google-apps.presentation':
mime_type = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
self.download(file_id, mime_type, path+".pptx")
def download(self, file_id, mime_type, filename):
request = service.files().export_media(fileId=file_id, mimeType=mime_type)
fh = io.FileIO(filename, 'wb')
downloader = MediaIoBaseDownload(fh, request)
done = False
while done is False:
status, done = downloader.next_chunk()

Данный код выполняет аутентификацию через Google API и определяет класс GoogleFiles, который используется для загрузки файлов с Google Диска в определенном формате.

  1. Аутентификация через Google API:
  • В первых строках кода определена переменная SCOPES, которая содержит список разрешений, необходимых для доступа к Google Диску;
  • Затем создается объект creds, который будет содержать учетные данные для аутентификации;
  • С помощью метода InstalledAppFlow.from_client_secrets_file() и передачи ему файла, который мы получили ранее (client_secrets.json) и списка разрешений (SCOPES), создается объект creds, который будет использоваться для аутентификации через локальный сервер;
  • Далее создается объект service с помощью метода build() из модуля googleapiclient.discovery. Этот объект представляет собой экземпляр службы Google Drive API с версией v3 и использует учетные данные creds для аутентификации;
  1. Класс GoogleFiles:
  • Класс GoogleFiles определен для загрузки файлов с Google Диска в определенном формате;
  • В конструкторе класса (__init__) передаются параметры file_id (идентификатор файла), file_type (тип файла) и path (путь для сохранения файла);
  • В зависимости от типа файла (file_type), определяется соответствующий MIME-тип и вызывается метод download() для загрузки файла в указанном формате.
  • Метод download() принимает идентификатор файла (file_id), MIME-тип (mime_type) и имя файла (filename);
  • Внутри метода download() создается запрос для экспорта содержимого файла с помощью метода files().export_media() объекта service. Запросу передаются идентификатор файла, MIME-тип и имя файла;
  • Затем создается файловый объект (fh) с помощью io.FileIO() для сохранения загруженного файла.
  • Создается объект загрузчика (downloader) с помощью MediaIoBaseDownload() для выполнения загрузки файла;
  • Запускается цикл загрузки, в котором выполняется вызов downloader.next_chunk() для загрузки следующего блока данных файла. Цикл продолжается, пока загрузка не завершится полностью;
  • После завершения загрузки файла, метод download() завершается.

Этот код позволяет загружать файлы с Google Диска в форматах DOCX, XLSX и PPTX с использованием класса GoogleFiles.

Пожалуйста, обратите внимание, что в данном коде не предоставлено определение файла с клиентскими секретами (client_secrets.json), поэтому его реализацию нужно будет добавить позже.

Теперь можете запускать. У вас получится два запроса на аутентификацию и ожидайте скачивание вашего диска. Всем удачи.
Подпишись, если статья понравилась.