Простой способ взаимодействия с Google Таблицами через Python (Gspread)

Друзья, привет. После последней публикации многие из вас выразили желание получить от меня пошаговые инструкции по работе с Google Таблицами (Google Sheets) через Python. Как вы догадались, вот инструкция.

Существует достаточно много методов взаимодействия с Google Sheets при помощи Python. Сегодня я рассмотрю, как мне кажется, самый простой и самый доступный модуль — Gspread.

Подготовка

Для того чтобы использовать Gspread, нам необходимо выполнить небольшую подготовку:

  1. Создать аккаунт в Google (если у вас его еще нет).

  2. Получить авторизационные данные для программного подключения к гугл таблицам.

Получаем авторизационные данные

На этом этапе большинство новичков и останавливаются. Поэтому я сейчас возьму вас за руку и проведу по всем этапам с подробным описанием каждого действия. Если вы уже настраивали доступ к таблицам, то просто пропускайте данный этап.

  • Авторизуемся в Google.

  • Переходим на Google Cloud Platform и проверяем, что выполнена авторизация в аккаунт Google: Google Cloud Platform API Dashboard

Примерно такой вид должен быть и у вас3Создаем новый проект.

Примерно такой вид должен быть и у вас

  • Создаем новый проект.

    Для этого переходим по ссылке: Создание проекта. На стартовом экране указываем имя проекта и нажимаем на «CREATE»:

Указываем только имя, а после жмем на

Указываем только имя, а после жмем на «CREATE»

  • Выпускаем токен для доступа к таблицам.

    Переходим в «Credentials» по ссылке: Google Cloud Credentials. Обратите внимание, после перехода по вашей ссылке у вас автоматически должен сформироваться путь, содержащий название вашего проекта. Если это не так, убедитесь, что выбран корректный проект.

    У меня путь к данному разделу выглядит так:

    https://console.cloud.google.com/apis/credentials? project=yakvenalex-habr-project

    Жмем на

    Жмем на «CREATE SERVICE ACCOUNT»

    Наша задача на этом этапе — создать и загрузить credentials. Нас интересует «Service Accounts». Жмем на «CREATE SERVICE ACCOUNT».

    В открывшемся окне указываем имя своего аккаунта и нажимаем на «DONE».

Зачем нужен Service Account?

Service Account (сервисный аккаунт) — это специальный тип аккаунта, который используется для автоматизированного доступа к сервисам Google. Он позволяет вашему приложению или скрипту взаимодействовать с Google Sheets от имени этого аккаунта, без необходимости использования ваших личных учетных данных. Это обеспечивает безопасность и удобство при интеграции и автоматизации работы с Google Таблицами.

8daf914a964c612afa005fe13953013e.jpg

  • Создаем ключ.

    Для этого нажимаем на три вертикальные точки возле созданного аккаунта и выбираем «Manage keys».

    Обратите внимание на Email. Можете его сразу скопировать, так как далее он нам пригодится.

    Обратите внимание на Email. Можете его сразу скопировать, так как далее он нам пригодится.

    Затем нажимаем «Add key» и выбираем «Create new key».

    e9227f80a0b8b90ab54b982860c6ca83.jpg

    На следующем экране выбираем JSON и нажимаем «CREATE».

    4918318a9f4e5732b1ac92b4035a292e.jpg

    Начнется загрузка JSON файла. Этот JSON файл мы будем использовать с библиотекой Gspread для авторизации.

  • Копируем email аккаунта.

    Его можно взять из JSON файла (значение ключа client_email) или с главной страницы аккаунта: Service Accounts.

    В моем случае это: yakvenalex-habr-account@yakvenalex-habr-project.iam.gserviceaccount.com

    В моем случае это: yakvenalex-habr-account@yakvenalex-habr-project.iam.gserviceaccount.com

    Далее вы будете давать доступ к таблицам Google не от своего имени, а от имени этого созданного аккаунта (email).

  • Активируем Google Sheets API. Для этого переходим по ссылке Google Sheets API и нажимаем на «ENABLE». Обратите внимание. Под каждый сервисный аккаунт необходимо выполнять активацию плагина Google Sheets API

Ждем пару секунд после клика на

Ждем пару секунд после клика на «ENABLE».

  • Создаем первую таблицу и даем доступ для Service Account email.

    Таблицу я назову «Habr table».

    Переходим в Google Sheets: Google Sheets.

    Нажимаем на »+» для создания новой таблицы:

    Возможно у вас будет написано на русском, но смысл не меняется.

    Возможно у вас будет написано на русском, но смысл не меняется.

    Даем имя таблице (двойной клик на «Untitled spreadsheet») и вводим новое название таблицы:

    14253ecb6092a2ced4951908de386649.jpg

    Открываем доступ к таблице созданному аккаунту. Для этого жмем на «Share», вставляем email созданного аккаунта и нажимаем «Send».

    da615e18ddb8269ac15ee38378f8d7f4.jpg

    Проверяем доступы, повторно нажав на «Share».

    559ec01a4b59ef8088a199f87cd6dbae.jpg

    Видим, что пользователь добавлен. Это значит, что можно приступать к написанию кода.

    Далее я покажу, как подключиться к Google Sheets и выполнить основные операции: чтение, запись, удаление и обновление данных.

Gspread: Коротко о главном

Gspread — это популярная Python-библиотека для работы с Google Таблицами, выпущенная сторонними разработчиками (не Google). Она упрощает взаимодействие с Google Sheets, предоставляя удобные методы для чтения, записи и обновления данных. С Gspread можно легко автоматизировать задачи, связанные с таблицами, например, собирать данные, создавать отчеты или интегрировать Google Sheets с другими сервисами. Библиотека поддерживает авторизацию через сервисные аккаунты, что делает её безопасным и мощным инструментом для разработчиков.

Начинаем писать код.

Я создам отдельный проект в Pycharm под написание кода и предлагаю вам сделать так же.

Назову проект GspreadTech. Во внутрь проекта помещу скачанный JSON и создам файл main.py, в котором и буду вести всю разработку. Страница проекта на моем GitHub: GspreadTech.

Установка:

pip install -r req.txt

Выполним импорты:

from gspread import Client, Spreadsheet, Worksheet, service_account
  • Client:

    • Это класс, представляющий клиентскую сессию для взаимодействия с Google Sheets. Используется для аутентификации и управления доступом к таблицам.

  • Spreadsheet:

    • Это класс, представляющий конкретную таблицу в Google Sheets. С его помощью можно управлять рабочими листами внутри таблицы и выполнять операции с данными.

  • Worksheet:

    • Это класс, представляющий конкретный рабочий лист внутри таблицы. Используется для выполнения операций с данными, таких как чтение, запись и обновление.

  • service_account:

    • Это функция для аутентификации с использованием сервисного аккаунта. С её помощью создается клиентская сессия (Client) для взаимодействия с Google Sheets от имени сервисного аккаунта.

Напишем функцию, которая позволит выполнить инициализацию клиента для работы с Google Sheets через загруженный JSON:

def client_init_json() -> Client:
    """Создание клиента для работы с Google Sheets."""
    return service_account(filename='yakvenalex-habr-project.json')

Существуют и другие методы авторизации, но для экономии вашего и своего времени в данной статье они не будут расмотрены. Подробнее можно посмотреть в документации Gspread.

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

Такой ответ говорит нам о том, что авторизация прошла корректно.

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

Теперь подключимся к нашей таблице и получим с нее данные. Далее я рассмотрю 2 способа подключения: через id таблицы и через ссылку.

Для получения ссылки на таблицу (а из нее ещё и ID) просто копируем ссылку с браузера на таблицу. В моем случае вид такой:

ID хранится в ссылке на таблицу после d/

ID хранится в ссылке на таблицу после d/

Обратите внимание. В ссылке на таблицу уже указывется ee ID.

Пишем код для доступа к таблице:

def get_table_by_url(client: Client, table_url):
    """Получение таблицы из Google Sheets по ссылке."""
    return client.open_by_url(table_url)


def get_table_by_id(client: Client, table_url):
    """Получение таблицы из Google Sheets по ID таблицы."""
    return client.open_by_key(table_url)

Тестируем:

def test_get_table(table_url: str, table_key: str):
    """Тестирование получения таблицы из Google Sheets."""
    client = client_init_json()
    table = get_table_by_url(client, table_url)
    print('Инфо по таблице по ссылке: ', table)
    table = get_table_by_id(client, table_key)
    print('Инфо по таблице по id: ', table)


if __name__ == '__main__':
    test_get_table(table_link, table_id)

Результат выполнения кода:

Инфо по таблице по ссылке:  
Инфо по таблице по id:  

Теперь перейдем к более интересному опыту взаимодействия с гугл таблицами.

Для начала давайте узнаем количество листов в таблице и их название.

def get_worksheet_info(table: Spreadsheet) -> dict:
    """Возвращает количество листов в таблице и их названия."""
    worksheets = table.worksheets()
    worksheet_info = {
        "count": len(worksheets),
        "names": [worksheet.title for worksheet in worksheets]
    }
    return worksheet_info


def main():
    # Создаем клиента и открываем таблицу
    client = client_init_json()
    table = get_table_by_id(client, table_id)

    # Получаем информацию о листах
    info = get_worksheet_info(table)
    print(f"Количество листов: {info['count']}")
    print("Названия листов:")
    for name in info['names']:
        print(name)


if __name__ == '__main__':
    main()

Смотрим на результат:

Количество листов: 1
Названия листов:
Sheet1

Если я добавлю новые листы или изменю название, то при повторном вызове получу актуальную информацию. Дело в том, что каждое обращение клиента к таблице происходит в live-режиме.

Очень важно после изменения имени листа указать это в коде. У меня из практики были случаи, когда клиенты переименовывали лист, а после возвращались ко мне с претензиями, мол не работает. Внимательно.

Создал 2 листа. Далее в них буду работать.

Создал 2 листа. Далее в них буду работать.

Количество листов: 2
Названия листов:
РабочийЛист1
РабочийЛист2

Для того чтоб клиент понимал с каким листом работать, в дальнейшем, необходимо будет явно указывать лист. Речь про такие операции, как: добавление записи, удаление записи, удаление листа и прочее (рассмотрим далее). А сейчас я покажу как создать новый лист через нашего клиента:

def create_worksheet(table: Spreadsheet, title: str, rows: int, cols: int):
    """Создание листа в таблице."""
    return table.add_worksheet(title, rows, cols)

Тестируем

def test_create_worksheet():
    client = client_init_json()
    table = get_table_by_id(client, table_id)
    rez = create_worksheet(table, 'НовыйРабочийЛист', rows=15, cols=10)
    print(rez)


test_create_worksheet()

Результат:

Обратите внимание. Листу присвоеy id. Смотрим в таблицу:

Обратите внимание на количество строк.

Обратите внимание на количество строк.

Для удаления листа можно использовать такую функцию:

def delete_worksheet(table: Spreadsheet, title: str):
    """Удаление листа из таблицы."""
    table.del_worksheet(table.worksheet(title))

Пример использования:

def test_dell_worksheet():
    client = client_init_json()
    table = get_table_by_id(client, table_id)
    delete_worksheet(table, title='НовыйРабочийЛист')


test_dell_worksheet()

На этом примере вы могли заметить конструкцию table.worksheet (title). Благодаря такому простому обращению мы указываем клиенту текущий рабочий лист. Далее, когда мы будем добавлять данные и удалять их мы будем использовать похожее указание на текущий рабочий лист.

Теперь мы готовы работать с данными. Для начала попробуем добавить информацию в любой из листов. Для этого напишем простую функцию, суть которой будет принимать некие данные и вставлять их в свободную строку (вставка идет сверху-вниз). Данный метод называется insert_row, а принимает он список с данными.

def insert_one(table: Spreadsheet, title: str, data: list, index: int = 1):
    """Вставка данных в лист."""
    worksheet = table.worksheet(title)
    worksheet.insert_row(data, index=index)

Обратите внимание. Мы бы могли в эту функцию передавать worksheet, но, с опыта скажу что удобнее явно передавать имя листа. Меньше проблем потом.

Индексы в таблицах начинаются не с 0, как мы привыкли, а с 1. По умолчанию запись будет добавлена в первую строку (индекс 1), но я его решил явно передать чтоб путанницы не было. Если укажем 2, то запись пойдет во вторую строку и так далее.

def test_add_data():
    """Тестирование добавления данных в таблицу."""
    client = client_init_json()
    table = get_table_by_id(client, table_id)
    worksheet_info = get_worksheet_info(table)
    print('Инфо по таблице: ', worksheet_info)
    insert_one(table=table,
               title=worksheet_info['names'][0],
               data=['name', 'address', 'email', 'phone_number', 'birth_date', 'company', 'job'])


test_add_data()

А вот тут давайте остановимся подробнее. Для получения имени листа я воспользовался функцией worksheet_info и достал данные по нулевому индексу. Сделал это просто для демонстрации, а для работы было бы просто достаточно указания названия листа.

data — это список и в данной функции при его помощи мы создаем некий header для нашей таблицы. Выполним код и посмотрим что получилось:

0b486a62c3582c8534fe5d015e3c3549.png

Мы видим что данные добавлены. Теперь было бы не плохо заполнить их информацией и понять, а как вообще добавлять данные массово, не по 1 строке.

Напишем код для создания фейковых пользователей при помощи библиотеки faker.

from faker import Faker


def generate_fake_user():
    fake = Faker('ru_RU')

    return {
        'name': fake.name(),
        'address': fake.address(),
        'email': fake.email(),
        'phone_number': fake.phone_number(),
        'birth_date': fake.date_of_birth().strftime("%d.%m.%Y"),
        'company': fake.company(),
        'job': fake.job()
    }


def get_fake_users(count: int):
    return [generate_fake_user() for _ in range(count)]

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

Сам код поместил в отдельный файл, чтоб не перегружать main.py.

Первый вариант функции (простой)

def add_data_to_worksheet_var_1(table: Spreadsheet, title: str, data: List[Dict], start_row: int = 2) -> None:
    """
    Добавляет данные на рабочий лист в Google Sheets.

    :param table: Объект таблицы (Spreadsheet).
    :param title: Название рабочего листа.
    :param data: Список словарей с данными.
    :param start_row: Номер строки, с которой начнется добавление данных.
    """
    try:
        worksheet = table.worksheet(title)
    except exceptions.WorksheetNotFound:
        worksheet = create_worksheet(table, title, rows=100, cols=20)

    # Преобразуем список словарей в список списков для добавления через insert_rows
    headers = list(data[0].keys())
    rows = [[row[header] for header in headers] for row in data]

    # Вставляем строки с данными в рабочий лист
    worksheet.insert_rows(rows, row=start_row)

В данном коде я прописал условие, при котором, при отсутствии листа, он будет создан. Для этого используется функция, которую мы уже расмотрели.

Далее мы преобразуем список словарей в список списков (rows), подходящий для метода insert_rows, а после уже идет сам метод insert_rows. Тестируем:

def test_add_many_data():
    """Тестирование добавления данных на рабочий лист."""
    client = client_init_json()
    table = get_table_by_id(client, table_id)
    users_data = get_fake_users(10)
    add_data_to_worksheet_var_1(table=table,
                                title='РабочийЛист1',
                                data=users_data)


test_add_many_data()

Данные добавлены.

Данные добавлены.

При помощи функции get_fake_users мы создали 10 пользователей, а после, без особых проблем, добавили их на рабочий лист с именем 'РабочийЛист1'.

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

def add_data_to_worksheet_var_2(table: Spreadsheet, title: str, data: List[Dict], start_row: int = 2) -> None:
    """
    Добавляет данные на рабочий лист в Google Sheets.

    :param table: Объект таблицы (Spreadsheet).
    :param title: Название рабочего листа.
    :param data: Список словарей с данными.
    :param start_row: Номер строки, с которой начнется добавление данных.
    """
    try:
        worksheet = table.worksheet(title)
    except exceptions.WorksheetNotFound:
        worksheet = create_worksheet(table, title, rows=100, cols=20)

    headers = data[0].keys()
    end_row = start_row + len(data) - 1
    end_col = chr(ord('A') + len(headers) - 1)

    cell_range = f'A{start_row}:{end_col}{end_row}'
    cell_list = worksheet.range(cell_range)

    flat_data = []
    for row in data:
        for header in headers:
            flat_data.append(row[header])

    for i, cell in enumerate(cell_list):
        cell.value = flat_data[i]

    worksheet.update_cells(cell_list)

Новый функционал:

  • Определяет диапазон ячеек, которые будут обновлены, на основе размера данных.

  • Преобразует список словарей в плоский список значений для обновления ячеек.

  • Обновляет ячейки в указанном диапазоне значениями из данных (используя метод update_cells).

Как вы видите, данный метод немного сложнее в синтаксисе, но, в то же время, он более гибкий. Кроме того, на примере данного метода вы увидели как просиходит обновление данных в ячейках (метод update_cells).

Пишем функцию для получения данных:

def extract_data_from_sheet(table: Spreadsheet, sheet_name: str) -> List[Dict]:
    """
    Извлекает данные из указанного листа таблицы Google Sheets и возвращает список словарей.

    :param table: Объект таблицы Google Sheets (Spreadsheet).
    :param sheet_name: Название листа в таблице.
    :return: Список словарей, представляющих данные из таблицы.
    """
    worksheet = table.worksheet(sheet_name)
    rows = worksheet.get_all_records()
    return rows

Как вы видите, все достаточно просто. Для того чтоб извлечь данные из указанного рабочего листа таблицы можно использовать метод get_all_records.

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

Тестируем:

def test_get_data():
    """Тестирование извлечения данных из таблицы Google Sheets."""
    client = client_init_json()
    table = get_table_by_id(client, table_id)
    data = extract_data_from_sheet(table, 'РабочийЛист1')
    for i in data:
        print(i)


test_get_data()

Результат:

Немного не влезло на скрин, но все данные получены.

Немного не влезло на скрин, но все данные получены.

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

Для получения данных из таблицы Google Sheets без использования метода get_all_records(), можно воспользоваться более низкоуровневыми методами библиотеки gspread, такими как get для получения диапазона ячеек. Это позволяет более гибко управлять процессом извлечения данных. Вот пример функции, которая извлекает данные из таблицы, используя метод get:

def extract_data_from_sheet_var_2(table: Spreadsheet, sheet_name: str) -> List[Dict]:
    """
    Извлекает данные из указанного листа таблицы Google Sheets и возвращает список словарей.

    :param table: Объект таблицы Google Sheets (Spreadsheet).
    :param sheet_name: Название листа в таблице.
    :return: Список словарей, представляющих данные из таблицы.
    """
    worksheet = table.worksheet(sheet_name)
    headers = worksheet.row_values(1)  # Первая строка считается заголовками

    data = []
    rows = worksheet.get_all_values()[1:]  # Начинаем считывать с второй строки

    for row in rows:
        row_dict = {headers[i]: value for i, value in enumerate(row)}
        data.append(row_dict)

    return data

Давайте рассмотрим тему с удалением данных из таблицы. Тут будет 2 варианта:

  • Удаление данных из заданного диапазона ячеек на указанном рабочем листе таблицы Google Sheets.

  • Удаление всех данных из указанного рабочего листа таблицы Google Sheets.

def clear_range(table: Spreadsheet, sheet_name: str, start_cell: str, end_cell: str) -> None:
    """
    Удаляет данные из заданного диапазона ячеек на указанном рабочем листе таблицы Google Sheets.

    :param table: Объект таблицы Google Sheets (Spreadsheet).
    :param sheet_name: Название листа в таблице.
    :param start_cell: Начальная ячейка диапазона (например, 'A1').
    :param end_cell: Конечная ячейка диапазона (например, 'B10').
    """
    worksheet = table.worksheet(sheet_name)
    cell_list = worksheet.range(f"{start_cell}:{end_cell}")
    for cell in cell_list:
        cell.value = ''
    worksheet.update_cells(cell_list)
    print(f"Данные в диапазоне {start_cell}:{end_cell} на листе '{sheet_name}' были успешно удалены.")

Давайте удалим данные в диапазоне A10: G11

2cbe8fefbd50298b2840adf2f32a8e69.png

Тестируем:

def test_dell_data():
    """Тестирование удаления данных по указанному диапазону."""
    client = client_init_json()
    table = get_table_by_id(client, table_id)
    clear_range(table=table, sheet_name="РабочийЛист1", start_cell="A10", end_cell="G11")


test_dell_data()

Смотрим:

Данные в диапазоне A10:G11 на листе 'РабочийЛист1' были успешно удалены.

2 нижние строки очистились

2 нижние строки очистились

Теперь удалим все данные с указанного листа:

def clear_sheet(table: Spreadsheet, sheet_name: str) -> None:
    """
    Удаляет все данные из указанного рабочего листа таблицы Google Sheets.

    :param table: Объект таблицы Google Sheets (Spreadsheet).
    :param sheet_name: Название листа в таблице.
    """
    worksheet = table.worksheet(sheet_name)
    worksheet.clear()
    print(f"Все данные на листе '{sheet_name}' были успешно удалены.")

Тестируем удаление всех данных:

def test_dell_all_data():
    """Тестирование удаления данных по указанному диапазону."""
    client = client_init_json()
    table = get_table_by_id(client, table_id)
    clear_sheet(table=table, sheet_name="РабочийЛист1")


test_dell_all_data()

Смотрим:

Все данные на листе 'РабочийЛист1' были успешно удалены.

7464092cc556ddd202f00a381fdd5473.png

Ссылка на полный код: GspreadTeach

Заключение

В заключение, работа с Google Таблицами через Python с использованием библиотеки Gspread предоставляет множество возможностей для автоматизации и управления данными. В данной статье мы рассмотрели основные шаги для настройки окружения, получения доступа к таблицам через сервисный аккаунт Google, а также основные операции работы с данными.

Gspread предлагает удобные методы для чтения, записи и обновления данных, что позволяет разработчикам эффективно интегрировать Google Sheets в свои проекты. С помощью простых функций можно добавлять данные, получать и фильтровать информацию, а также удалять данные как в определенных диапазонах, так и полностью из листов.

Надеюсь, что данное руководство помогло вам разобраться с основами работы с Google Таблицами через Python и вдохновило на создание собственных проектов с использованием этого мощного инструмента. Если это так, то сообщите об этом в комментариях, лайком или подпиской.

До скорого!

P.S. Дорогие друзья! Я хочу выразить искреннюю благодарность каждому из вас за комментарии, лайки, подписки и особенно за подержку рублем. Все это, действительно, бесценно. В связи с тем, что я практически вошел в топ-100 авторов (на данный момент я на 101 месте) и достиг первой сотни подписчиков, я обещаю продолжать делиться своими знаниями и опытом, совершенствуя свой контент. Спасибо вам!

© Habrahabr.ru