1. Обзор
В этом практическом занятии рассматривается возможный корпоративный рабочий процесс: архивирование изображений, анализ и создание отчетов. Представьте, что в вашей организации хранится множество изображений, занимающих место на ограниченном ресурсе. Вы хотите заархивировать эти данные, проанализировать изображения и, что наиболее важно, создать отчет, обобщающий места хранения архивированных изображений и результаты анализа, готовый к использованию руководством. Google Cloud предоставляет инструменты для реализации этого, используя API из двух своих продуктовых линеек: Google Workspace (ранее G Suite или Google Apps) и Google Cloud (ранее GCP).
В нашем сценарии у бизнес-пользователя будут изображения на Google Drive . Логично создавать их резервные копии в более «холодных», дешевых хранилищах, таких как классы хранения , доступные в Google Cloud Storage . Google Cloud Vision позволяет разработчикам легко интегрировать функции распознавания изображений в приложения, включая обнаружение объектов и ориентиров, оптическое распознавание символов (OCR) и т. д. Наконец, электронная таблица Google Sheets — это полезный инструмент визуализации для обобщения всей этой информации для вашего руководителя.
После завершения этого практического занятия по созданию решения, использующего все возможности Google Cloud, мы надеемся, что вы вдохновитесь на создание чего-то еще более значимого для вашей организации или ваших клиентов.
Что вы узнаете
- Как использовать Cloud Shell
- Как аутентифицировать запросы API
- Как установить клиентскую библиотеку Google API для Python
- Как включить API Google
- Как скачать файлы с Google Диска
- Как загрузить объекты/блобы в облачное хранилище
- Как анализировать данные с помощью Cloud Vision
- Как записывать строки в Google Таблицы
Что вам понадобится
- Учетная запись Google (для учетных записей Google Workspace может потребоваться подтверждение администратора).
- Проект Google Cloud с активным платежным аккаунтом Google Cloud.
- Знание команд терминала/оболочки операционной системы.
- Базовые навыки работы с Python (уровень 2 или 3), но вы можете использовать любой поддерживаемый язык.
Опыт работы с четырьмя перечисленными выше продуктами Google Cloud будет полезен, но не обязателен. Если у вас есть время, вы можете сначала ознакомиться с ними по отдельности, вы можете выполнить практические задания по каждому из них, прежде чем приступать к выполнению данного упражнения:
- Введение в Google Drive (использование API Google Workspace) (Python)
- Использование Cloud Vision с Python (Python)
- Создавайте персонализированные инструменты отчетности с помощью Sheets API (JS/Node).
- Загрузка объектов в Google Cloud Storage (программирование не требуется)
Опрос
Как вы будете использовать этот учебный материал?
Как бы вы оценили свой опыт работы с Python?
Как бы вы оценили свой опыт использования сервисов Google Cloud?
Как бы вы оценили свой опыт использования сервисов для разработчиков Google Workspace?
Хотели бы вы видеть больше ориентированных на бизнес-задачи практического применения, а не тех, которые посвящены презентации новых функций продукта?
2. Настройка и требования
Настройка среды для самостоятельного обучения
- Войдите в консоль Google Cloud и создайте новый проект или используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .



- Название проекта — это отображаемое имя участников данного проекта. Это строка символов, не используемая API Google. Вы можете изменить её в любое время.
- Идентификатор проекта должен быть уникальным для всех проектов Google Cloud и неизменяемым (его нельзя изменить после установки). Консоль Cloud автоматически генерирует уникальную строку; обычно вам неважно, какая она. В большинстве практических заданий вам потребуется указать идентификатор проекта (обычно он обозначается как
PROJECT_ID). Если сгенерированный идентификатор вас не устраивает, вы можете сгенерировать другой случайный идентификатор. В качестве альтернативы вы можете попробовать свой собственный и посмотреть, доступен ли он. После этого шага его нельзя изменить, и он останется неизменным на протяжении всего проекта. - К вашему сведению, существует третье значение — номер проекта , который используется некоторыми API. Подробнее обо всех трех значениях можно узнать в документации .
- Далее вам потребуется включить оплату в консоли Cloud для использования ресурсов/API Cloud. Выполнение этого практического задания не должно стоить дорого, если вообще что-либо. Чтобы отключить ресурсы и избежать дополнительных расходов после завершения этого урока, вы можете удалить созданные ресурсы или удалить весь проект. Новые пользователи Google Cloud имеют право на бесплатную пробную версию стоимостью 300 долларов США .
Запустить Cloud Shell
Краткое содержание
Хотя вы можете разрабатывать код локально на своем ноутбуке, второстепенная цель этого практического занятия — научить вас использовать Google Cloud Shell , среду командной строки, работающую в облаке через современный веб-браузер.
Активировать Cloud Shell
- В консоли Cloud нажмите «Активировать Cloud Shell» .
.

Если вы никогда раньше не запускали Cloud Shell, вам будет показан промежуточный экран (внизу), описывающий его назначение. В этом случае нажмите «Продолжить» (и вы больше никогда его не увидите). Вот как выглядит этот одноразовый экран:

Подготовка и подключение к Cloud Shell займут всего несколько минут.

Эта виртуальная машина оснащена всеми необходимыми инструментами разработки. Она предоставляет постоянный домашний каталог размером 5 ГБ и работает в облаке Google, что значительно повышает производительность сети и аутентификацию. Большая часть, если не вся, работа в этом практическом задании может быть выполнена с помощью обычного браузера или вашего Chromebook.
После подключения к Cloud Shell вы увидите, что ваша аутентификация пройдена и что проект уже настроен на ваш идентификатор проекта.
- Выполните следующую команду в Cloud Shell, чтобы подтвердить свою аутентификацию:
gcloud auth list
вывод команды
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
- Выполните следующую команду в Cloud Shell, чтобы убедиться, что команда gcloud знает о вашем проекте:
gcloud config list project
вывод команды
[core] project = <PROJECT_ID>
Если это не так, вы можете установить это с помощью следующей команды:
gcloud config set project <PROJECT_ID>
вывод команды
Updated property [core/project].
3. Подтвердите окружение Python.
Для выполнения этого практического задания вам потребуется использовать язык Python (хотя клиентские библиотеки Google API поддерживают множество языков , поэтому вы можете создать что-то эквивалентное в своем любимом инструменте разработки и просто использовать Python в качестве псевдокода). В частности, это практическое задание поддерживает Python 2 и 3, но мы рекомендуем как можно скорее перейти на версию 3.x.
Cloud Shell — это удобный инструмент, доступный пользователям непосредственно из облачной консоли , и для его работы не требуется локальная среда разработки, поэтому этот урок можно выполнить полностью в облаке с помощью веб-браузера. В частности, для этого практического занятия в Cloud Shell уже предустановлены обе версии Python.
В Cloud Shell также установлен IPython : это интерактивный интерпретатор Python более высокого уровня, который мы рекомендуем, особенно если вы работаете в сфере анализа данных или машинного обучения. В этом случае IPython является интерпретатором по умолчанию для Jupyter Notebooks , а также для Colab , Jupyter Notebooks, размещенных Google Research.
IPython в первую очередь использует интерпретатор Python 3, но переключается на Python 2, если версия 3.x недоступна. Доступ к IPython можно получить через Cloud Shell, а также установить в локальной среде разработки. Выход осуществляется нажатием ^D (Ctrl-d), после чего необходимо принять предложение о выходе. Пример вывода при запуске ipython будет выглядеть следующим образом:
$ ipython Python 3.7.3 (default, Mar 4 2020, 23:11:43) Type 'copyright', 'credits' or 'license' for more information IPython 7.13.0 -- An enhanced Interactive Python. Type '?' for help. In [1]:
Если IPython вам не подходит, вполне допустимо использовать стандартный интерактивный интерпретатор Python (либо Cloud Shell, либо локальную среду разработки) (завершить работу можно с помощью ^D):
$ python Python 2.7.13 (default, Sep 26 2018, 18:42:22) [GCC 6.3.0 20170516] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> $ python3 Python 3.7.3 (default, Mar 10 2020, 02:33:39) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
В этом руководстве предполагается, что у вас установлен инструмент установки pip (менеджер пакетов Python и средство разрешения зависимостей). Он входит в комплект версий 2.7.9+ или 3.4+. Если у вас более старая версия Python, см. это руководство для получения инструкций по установке. В зависимости от ваших прав доступа вам может потребоваться доступ с правами суперпользователя ( sudo ), но обычно это не требуется. Вы также можете явно использовать pip2 или pip3 для запуска pip для определенных версий Python.
В оставшейся части практического задания предполагается использование Python 3 — если инструкции для Python 2 будут существенно отличаться от инструкций для Python 3.x, они будут предоставлены отдельно.
[необязательно] Создание и использование виртуальных сред
Этот раздел необязателен и необходим только тем, кому для выполнения этого практического задания требуется виртуальное окружение (согласно предупреждению в боковой панели выше). Если на вашем компьютере установлен только Python 3, вы можете просто выполнить эту команду для создания виртуального окружения с именем my_env (при желании можно выбрать другое имя):
virtualenv my_env
Однако, если на вашем компьютере установлены Python 2 и Python 3, мы рекомендуем установить виртуальное окружение Python 3, что можно сделать с помощью -p flag следующим образом:
virtualenv -p python3 my_env
Чтобы войти в созданное виртуальное окружение, "активируйте" его следующим образом:
source my_env/bin/activate
Убедитесь, что вы находитесь в нужной среде, обратив внимание на то, что в командной строке теперь перед именем вашей среды отображается её название, например:
(my_env) $
Теперь вы сможете pip install , выполнить код в этой среде и т.д. Еще одно преимущество заключается в том, что если вы все испортите, например, ваша установка Python будет повреждена, вы сможете удалить всю эту среду, не затронув остальную часть системы.
4. Установите клиентскую библиотеку Google API для Python.
Для выполнения этого практического задания требуется клиентская библиотека Google API для Python , поэтому либо установка будет простой, либо вам вообще ничего не потребуется.
Ранее мы рекомендовали вам использовать Cloud Shell для удобства. Вы можете пройти весь учебный курс из веб-браузера в облаке. Еще одна причина использовать Cloud Shell — это то, что многие популярные инструменты разработки и необходимые библиотеки уже предустановлены .
*Установите клиентские библиотеки
( необязательно ) Этот шаг можно пропустить, если вы используете Cloud Shell или локальную среду, где клиентские библиотеки уже установлены. Это необходимо сделать только в том случае, если вы разрабатываете локально и не установили (или не уверены, что установили) их. Самый простой способ — использовать pip (или pip3 ) для установки (включая обновление самого pip при необходимости):
pip install -U pip google-api-python-client oauth2client
Подтвердите установку
Эта команда устанавливает клиентскую библиотеку, а также все пакеты, от которых она зависит. Независимо от того, используете ли вы Cloud Shell или собственную среду, убедитесь, что клиентская библиотека установлена, импортировав необходимые пакеты, и подтвердите отсутствие ошибок импорта (и вывода информации):
python3 -c "import googleapiclient, httplib2, oauth2client"
Если вы используете Python 2 (из Cloud Shell), вы получите предупреждение о том, что его поддержка устарела:
******************************************************************************* Python 2 is deprecated. Upgrade to Python 3 as soon as possible. See https://cloud.google.com/python/docs/python2-sunset To suppress this warning, create an empty ~/.cloudshell/no-python-warning file. The command will automatically proceed in seconds or on any key. *******************************************************************************
Как только вы сможете успешно выполнить команду импорта "test" (без ошибок/вывода), вы будете готовы начать взаимодействовать с API Google!
Краткое содержание
Поскольку это практическое занятие среднего уровня сложности, предполагается, что у вас уже есть опыт создания и использования проектов в консоли. Если вы новичок в API Google, и в частности в API Google Workspace, сначала попробуйте пройти вводное практическое занятие по API Google Workspace . Кроме того, если вы знаете, как создавать (или повторно использовать существующие) учетные данные пользователей ( не сервисных учетных записей ), поместите файл client_secret.json в свою рабочую директорию, пропустите следующий модуль и перейдите к разделу «Включение API Google».
5. *Авторизация API-запросов (авторизация пользователя)
Этот раздел можно пропустить, если вы уже создали учетные данные для авторизации пользовательской учетной записи и знакомы с этим процессом. Он отличается от авторизации служебной учетной записи, для которой используется другой метод, поэтому, пожалуйста, продолжайте чтение ниже.
Введение в авторизацию (плюс некоторые аспекты аутентификации)
Для отправки запросов к API вашему приложению необходима соответствующая авторизация . Аутентификация , или, как её ещё называют, авторизация, описывает учетные данные для входа в систему — вы проходите аутентификацию при входе в свою учетную запись Google с помощью логина и пароля. После аутентификации следующим шагом является проверка того, имеете ли вы — или, точнее, ваш код — право доступа к данным, таким как файлы BLOB-объектов в Cloud Storage или личные файлы пользователя в Google Drive.
API Google поддерживают несколько типов авторизации, но наиболее распространенным для пользователей API G Suite является авторизация пользователя , поскольку в примере приложения в этом практическом руководстве используются данные конечных пользователей. Эти конечные пользователи должны предоставить вашему приложению разрешение на доступ к своим данным . Это означает, что ваш код должен получить учетные данные OAuth2 для доступа к учетной записи пользователя.
Чтобы получить учетные данные OAuth2 для авторизации пользователей, вернитесь в менеджер API и выберите вкладку «Учетные данные» в левой панели навигации:

Когда вы туда попадете, вы увидите все свои учетные данные в трех отдельных разделах:

Первый используется для ключей API , второй — для идентификаторов клиентов OAuth 2.0, а последний — для учетных записей служб OAuth2 — мы используем ту, что посередине.
Создание учетных данных
На странице «Учетные данные» нажмите кнопку «+ Создать учетные данные» вверху, после чего откроется диалоговое окно, в котором нужно выбрать «Идентификатор клиента OAuth:».

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

Если вы еще не настроили экран согласия, вы увидите предупреждение в консоли, и вам нужно будет сделать это сейчас. (Пропустите следующие шаги, если экран согласия уже настроен.)
Экран согласия OAuth
Нажмите на кнопку «Настроить экран согласия», где выберите приложение «Внешнее» (или «Внутреннее», если вы являетесь клиентом G Suite):

Обратите внимание, что для целей этого упражнения не имеет значения, какой вариант вы выберете, поскольку вы не публикуете свой пример кода. Большинство людей выберут «Внешний», чтобы перейти к более сложному экрану, но вам на самом деле нужно заполнить только поле «Название приложения» вверху:

На данном этапе вам нужно только название приложения, поэтому выберите такое, которое отражает тематику вашего практического занятия, а затем нажмите «Сохранить» .
Создание идентификатора клиента OAuth (аутентификация учетной записи пользователя)
Теперь вернитесь на вкладку «Учетные данные», чтобы создать идентификатор клиента OAuth2 . Здесь вы увидите различные идентификаторы клиентов OAuth, которые можно создать:

Мы разрабатываем инструмент командной строки под названием «Другое» , поэтому выберите его, а затем нажмите кнопку «Создать ». Выберите имя идентификатора клиента, отражающее создаваемое вами приложение, или просто используйте имя по умолчанию, которое обычно звучит как «Другой клиент N ».
Сохранение ваших учетных данных
- Появится диалоговое окно с новыми учетными данными; нажмите ОК , чтобы закрыть его.

- Вернувшись на страницу «Учетные данные», прокрутите вниз до раздела «Идентификаторы клиентов OAuth2», найдите и нажмите значок загрузки.
в крайнем правом нижнем углу вашего недавно созданного идентификатора клиента. 
- Откроется диалоговое окно для сохранения файла с именем
client_secret-LONG-HASH-STRING.apps.googleusercontent.com.jsonскорее всего, в папку «Загрузки» . Мы рекомендуем сократить имя до более простого, например,client_secret.json(именно такое имя используется в примере приложения), а затем сохранить его в каталог/папку, где вы будете создавать пример приложения в этом практическом задании.
Краткое содержание
Теперь вы готовы включить API Google, используемые в этом практическом занятии. Кроме того, для названия приложения на экране согласия OAuth мы выбрали "Vision API demo", поэтому ожидайте увидеть его на некоторых из будущих скриншотов.
6. Включите API Google.
В этом практическом занятии используются четыре (4) API Google Cloud: пара от Google Cloud (Cloud Storage и Cloud Vision) и еще пара от Google Workspace (Google Drive и Google Sheets). Ниже приведены общие инструкции по включению API Google. После того, как вы поймете, как включить один API, остальные будут включаться аналогично.
Независимо от того, какой API Google вы хотите использовать в своем приложении, его необходимо включить . API можно включить из командной строки или из консоли Cloud. Процесс включения API идентичен, поэтому, включив один API, вы можете аналогичным образом включить и другие.
Вариант 1: интерфейс командной строки gcloud (Cloud Shell или локальная среда)
Хотя включение API из Cloud Console более распространено, некоторые разработчики предпочитают делать все из командной строки. Для этого необходимо найти «имя сервиса» API. Оно выглядит как URL: SERVICE_NAME . Эти .googleapis.com можно найти в таблице поддерживаемых продуктов , или же их можно получить программным путем с помощью Google Discovery API .
Вооружившись этой информацией, используя Cloud Shell (или вашу локальную среду разработки с установленным инструментом командной строки gcloud ), вы можете включить API или сервис следующим образом:
gcloud services enable SERVICE_NAME.googleapis.com
Пример 1: Включение API Cloud Vision
gcloud services enable vision.googleapis.com
Пример 2: Включение бессерверной вычислительной платформы Google App Engine.
gcloud services enable appengine.googleapis.com
Пример 3: Включение нескольких API одним запросом. Например, если в этом практическом занятии участники развертывают приложение с использованием Cloud Translation API в App Engine, Cloud Functions и Cloud Run, командная строка будет выглядеть следующим образом:
gcloud services enable appengine.googleapis.com cloudfunctions.googleapis.com artifactregistry.googleapis.com run.googleapis.com translate.googleapis.com
Эта команда включает App Engine, Cloud Functions, Cloud Run и Cloud Translation API. Кроме того, она включает Cloud Artifact Registry, поскольку именно там система Cloud Build должна регистрировать образы контейнеров для развертывания в Cloud Run.
Также есть несколько команд для запроса API для включения или для определения того, какие API уже включены для вашего проекта.
Пример 4: Запрос доступных API Google для включения в ваш проект.
gcloud services list --available --filter="name:googleapis.com"
Пример 5: Запрос к API Google, включенным для вашего проекта.
gcloud services list
Для получения более подробной информации о вышеуказанных командах см. документацию по включению и отключению служб , а также по перечислению служб .
Вариант 2: Облачная консоль
Вы также можете включить API Google в Менеджере API. В консоли Cloud перейдите в Менеджер API . На этой странице панели управления вы увидите информацию о трафике вашего приложения, графики, отображающие запросы приложения, ошибки, генерируемые вашим приложением, и время ответа вашего приложения:

Ниже приведен список API Google, включенных для вашего проекта:

Чтобы включить (или отключить) API, нажмите кнопку «Включить API и сервисы» вверху страницы:

В качестве альтернативы, перейдите в левую панель навигации и выберите API и сервисы → Библиотека :

В любом случае, вы попадете на страницу библиотеки API :

Введите имя API для поиска и просмотра соответствующих результатов:

Выберите API, который хотите включить, и нажмите кнопку «Включить» :

Процесс активации всех API одинаков независимо от того, какой API Google вы хотите использовать.
Расходы
Многие API Google можно использовать бесплатно, однако использование большинства продуктов и API Google Cloud влечет за собой определенные затраты. При включении API Cloud вам может потребоваться активный платежный аккаунт. Однако некоторые продукты Google Cloud имеют тарифный план «Всегда бесплатно» , превышение которого влечет за собой оплату.
Новые пользователи Google Cloud могут воспользоваться бесплатной пробной версией , которая в настоящее время стоит 300 долларов США и действует в течение первых 90 дней. Codelabs обычно не взимает никаких платежей, поэтому мы рекомендуем отложить использование бесплатной пробной версии до тех пор, пока вы действительно не будете готовы протестировать сервис, тем более что это разовое предложение. Квоты бесплатного уровня не истекают и применяются независимо от того, используете вы бесплатную пробную версию или нет.
Перед включением любого API пользователям следует ознакомиться с информацией о ценах (например, страница с ценами на API Cloud Vision ), особенно обращая внимание на наличие бесплатного уровня и, если он есть, на его размер. Пока вы не превышаете установленные совокупные дневные или месячные лимиты, никаких дополнительных расходов взиматься не должно. Цены и бесплатные уровни различаются в зависимости от API продуктовых групп Google. Примеры:
- Google Cloud — каждый продукт оплачивается по-разному и, как правило, по факту использования; информацию о бесплатном уровне см. выше.
- Google Maps — предлагает набор API и предоставляет пользователям бесплатный ежемесячный кредит в размере 200 долларов США .
- API Google Workspace (ранее G Suite) — предоставляют возможность использования (в пределах определенных лимитов), покрываемую ежемесячной абонентской платой Google Workspace , поэтому за использование API для таких приложений, как Gmail, Google Drive, Calendar, Docs, Sheets или Slides, не взимается прямая плата.
Разные продукты Google оплачиваются по-разному, поэтому обязательно обратитесь к соответствующей документации для получения этой информации.
Краткое содержание
Теперь, когда Cloud Vision включен, аналогичным образом включите остальные три API (Google Drive, Cloud Storage, Google Sheets). В Cloud Shell используйте gcloud services enable , или в Cloud Console:
- Вернуться в библиотеку API
- Начните поиск, введя несколько букв названия.
- Выберите нужный API и
- Давать возможность
Повторите процедуру. Для облачного хранилища есть несколько вариантов: выберите «JSON API Google Cloud Storage». API облачного хранилища также потребует наличия активного платежного аккаунта.
7. Шаг 0: Настройка импорта и кода авторизации.
Это начало фрагмента кода среднего размера, поэтому следование принципам гибкой разработки поможет обеспечить наличие общей, стабильной и работоспособной инфраструктуры, прежде чем приступать к основному приложению. Дважды проверьте наличие client_secret.json в текущем каталоге и либо запустите ipython и введите следующий фрагмент кода, либо сохраните его в файл analyze_gsimg.py и запустите из командной строки (последний вариант предпочтительнее, поскольку мы продолжим добавлять код в пример):
from __future__ import print_function
from googleapiclient import discovery, http
from httplib2 import Http
from oauth2client import file, client, tools
# process credentials for OAuth2 tokens
SCOPES = 'https://www.googleapis.com/auth/drive.readonly'
store = file.Storage('storage.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)
# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE = discovery.build('drive', 'v3', http=HTTP)
Этот основной компонент включает в себя блоки кода для импорта модулей/пакетов, обработки учетных данных аутентификации пользователей и создания конечных точек API-сервисов. Ключевые фрагменты кода, которые следует изучить:
- Импорт функции
print()делает этот пример совместимым с Python 2-3, а импорт библиотек Google обеспечивает доступ ко всем необходимым инструментам для взаимодействия с API Google. - Переменная
SCOPESпредставляет собой запрашиваемые у пользователя разрешения — на данный момент доступно только одно: разрешение на чтение данных из его Google Диска. - Оставшаяся часть кода обработки учетных данных считывает кэшированные токены OAuth2, возможно, обновляя их до нового токена доступа с помощью токена обновления, если срок действия исходного токена доступа истек.
- Если токены не были созданы или получение действительного токена доступа не удалось по другой причине, пользователь должен пройти трехэтапный процесс OAuth2 (3LO): создать диалоговое окно с запрошенными разрешениями и предложить пользователю принять их. После принятия пользователем разрешения приложение продолжает работу, в противном случае
tools.run_flow()выдает исключение, и выполнение останавливается. - После того, как пользователь предоставит разрешение, создается HTTP-клиент для связи с сервером, и все запросы подписываются учетными данными пользователя в целях безопасности. Затем создается конечная точка сервиса к API Google Drive (версия 3), и этот HTTP-клиент назначается сервису
DRIVE.
Запуск приложения
При первом запуске скрипта у него не будет прав доступа к файлам пользователя на Google Диске (вашем). При приостановленном выполнении результат будет выглядеть примерно так:
$ python3 ./analyze_gsimg.py
/usr/local/lib/python3.6/site-packages/oauth2client/_helpers.py:255: UserWarning: Cannot access storage.json: No such file or directory
warnings.warn(_MISSING_FILE_MESSAGE.format(filename))
Your browser has been opened to visit:
https://accounts.google.com/o/oauth2/auth?client_id=LONG-STRING.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.readonly&access_type=offline&response_type=code
If your browser is on a different machine then exit and re-run this
application with the command-line parameter
--noauth_local_webserver
Если вы запускаете приложение из Cloud Shell, перейдите к разделу «Из Cloud Shell», а затем прокрутите страницу назад, чтобы просмотреть соответствующие экраны в разделе «Из локальной среды разработки», когда это необходимо.
Из местной среды развития
Выполнение скрипта командной строки приостанавливается при открытии окна браузера. В результате может появиться пугающее предупреждение, которое выглядит примерно так:

Это вполне обоснованное опасение, поскольку вы пытаетесь запустить приложение, которое получает доступ к пользовательским данным. Поскольку это всего лишь демонстрационное приложение, и вы являетесь разработчиком, надеюсь, вы достаточно уверены в себе, чтобы продолжить. Чтобы лучше понять это, поставьте себя на место пользователя: вас просят разрешить чужому коду доступ к вашим данным. Если вы собираетесь опубликовать подобное приложение, вы пройдете процесс проверки, поэтому ваши пользователи не увидят этот экран.
После нажатия на ссылку «Перейти к небезопасному приложению» откроется диалоговое окно разрешений OAuth2, которое выглядит примерно так, как показано ниже — мы постоянно улучшаем наш пользовательский интерфейс, поэтому не беспокойтесь, если он не совсем совпадает:

Диалоговое окно OAuth2 отображает запрашиваемые разработчиком разрешения (через переменную SCOPES ). В данном случае это возможность просмотра и загрузки файлов с Google Диска пользователя. В коде приложения эти области разрешений отображаются в виде URI, но они переводятся на язык, указанный в локали пользователя. Здесь пользователь должен явно подтвердить запрашиваемое(ые) разрешение(я), в противном случае будет выброшено исключение, и выполнение скрипта не продолжится.
Возможно, появится еще одно диалоговое окно с запросом подтверждения:

ПРИМЕЧАНИЕ : Некоторые пользователи используют несколько веб-браузеров, авторизованных под разные учетные записи, поэтому этот запрос на авторизацию может быть отправлен в неправильную вкладку/окно браузера, и вам, возможно, придется скопировать и вставить ссылку для этого запроса в браузер, авторизованный под правильной учетной записью.
Из Cloud Shell
В Cloud Shell окно браузера не открывается, и вы оказываетесь в тупике. Обратите внимание, что диагностическое сообщение внизу предназначалось именно вам:
If your browser is on a different machine then exit and re-run this application with the command-line parameter --noauth_local_webserver
Вам придётся нажать ^C (Ctrl-C или другую клавишу для остановки выполнения скрипта) и запустить его из командной строки с дополнительным флагом. При таком запуске вы получите следующий вывод:
$ python3 analyze_gsimg.py --noauth_local_webserver
/usr/local/lib/python3.7/site-packages/oauth2client/_helpers.py:255: UserWarning: Cannot access storage.json: No such file or directory
warnings.warn(_MISSING_FILE_MESSAGE.format(filename))
Go to the following link in your browser:
https://accounts.google.com/o/oauth2/auth?client_id=LONG-STRING.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.readonly&access_type=offline&response_type=code
Enter verification code:
(Проигнорируем предупреждение, поскольку знаем, storage.json еще не создан, и) Следуя инструкциям в другой вкладке браузера по этому URL-адресу, вы получите практически идентичный описанный выше опыт для локальных сред разработки (см. скриншоты выше). В конце вас ждет последний экран с кодом подтверждения, который нужно ввести в Cloud Shell:

Скопируйте и вставьте этот код в окно терминала.
Краткое содержание
Помимо сообщения « Authentication successful », не ожидайте никаких дополнительных результатов. Помните, это всего лишь настройка... вы еще ничего не сделали. Вы успешно начали свой путь к чему-то, что с большей вероятностью будет выполнено правильно с первого раза. (Самое приятное, что запрос на авторизацию был отправлен только один раз; все последующие выполнения пропускают его, потому что ваши права доступа кэшированы.) Теперь давайте заставим код выполнять реальную работу, которая приведет к фактическому результату.
Поиск неисправностей
Если вместо отсутствия вывода появляется ошибка, это может быть вызвано одной или несколькими причинами, возможно, следующей:
8. Шаг 1: Загрузите изображение с Google Диска
На предыдущем шаге мы рекомендовали создать код как analyze_gsimg.py и редактировать его оттуда. Также можно просто скопировать и вставить все непосредственно в iPython или стандартную оболочку Python, однако это более громоздко, поскольку мы будем продолжать разрабатывать приложение по частям.
Предположим, ваше приложение авторизовано и создана конечная точка API-сервиса. В вашем коде это представлено переменной DRIVE . Теперь давайте найдем файл изображения на вашем Google Диске и
Присвойте это значение переменной с NAME . Введите её, а также функцию drive_get_img() расположенную чуть ниже кода из шага 0:
FILE = 'YOUR_IMG_ON_DRIVE' # fill-in with name of your Drive file
def drive_get_img(fname):
'download file from Drive and return file info & binary if found'
# search for file on Google Drive
rsp = DRIVE.files().list(q="name='%s'" % fname,
fields='files(id,name,mimeType,modifiedTime)'
).execute().get('files', [])
# download binary & return file info if found, else return None
if rsp:
target = rsp[0] # use first matching file
fileId = target['id']
fname = target['name']
mtype = target['mimeType']
binary = DRIVE.files().get_media(fileId=fileId).execute()
return fname, mtype, target['modifiedTime'], binary
Коллекция файлов Drive files() имеет метод list() , который выполняет запрос (параметр q ) для указанного файла. Параметр fields используется для указания того, какие возвращаемые значения вас интересуют — зачем получать все значения и замедлять работу, если остальные значения вас не интересуют? Если вы новичок в использовании масок полей для фильтрации возвращаемых значений API, ознакомьтесь с этой статьей в блоге и видео . В противном случае выполните запрос и получите возвращаемый атрибут files , по умолчанию используя пустой массив списка, если совпадений нет.
Если результатов нет, остальная часть функции пропускается, и возвращается None (неявно). В противном случае, получите первый соответствующий ответ ( rsp[0] ), верните имя файла, его MIME-тип, метку времени последнего изменения и, наконец, его двоичные данные, полученные функцией get_media() (по идентификатору файла), также в коллекции files() . (Названия методов могут немного отличаться в клиентских библиотеках других языков.)
Заключительная часть — это «основная» составляющая, управляющая всем приложением:
if __name__ == '__main__':
# download img file & info from Drive
rsp = drive_get_img(FILE)
if rsp:
fname, mtype, ftime, data = rsp
print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))
else:
print('ERROR: Cannot download %r from Drive' % fname)
Предположим, что на Google Диске есть изображение с именем section-work-card-img_2x.jpg и оно установлено в FILE . После успешного выполнения скрипта вы должны увидеть сообщение, подтверждающее, что файл удалось прочитать с Google Диска (но не сохранить на вашем компьютере):
$ python3 analyze_gsimg.py Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Поиск неисправностей
Если вы не получаете успешного результата, подобного описанному выше, это может быть вызвано одной или несколькими причинами, возможно, следующей:
Краткое содержание
В этом разделе вы узнали, как (с помощью двух отдельных вызовов API) подключиться к API Google Диска, запросить определенный файл и затем загрузить его. Пример использования: архивирование данных Google Диска и, возможно, их анализ, например, с помощью инструментов Google Cloud. Код вашего приложения на этом этапе должен соответствовать коду из репозитория step1-drive/analyze_gsimg.py .
Подробнее о загрузке файлов с Google Drive можно прочитать здесь или посмотреть эту статью в блоге и видео . Эта часть практического занятия практически идентична всему вводному курсу по API Google Workspace — вместо загрузки файла отображается список из первых 100 файлов/папок на Google Drive пользователя, и используется более узкая область видимости.
9. Шаг 2: Архивирование файла в облачное хранилище.
Следующий шаг — добавить поддержку Google Cloud Storage . Для этого нам нужно импортировать ещё один пакет Python, io . Убедитесь, что верхняя часть ваших импортов теперь выглядит так:
from __future__ import print_function
import io
Помимо имени файла на Google Диске, нам потребуется некоторая информация о том, где этот файл будет храниться в облачном хранилище, а именно имя «корзины», в которую вы собираетесь его поместить, и любые префиксы «родительской папки». Подробнее об этом чуть позже:
FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = '' # YOUR IMG FILE PREFIX
Несколько слов о хранилищах: Cloud Storage предоставляет аморфное хранилище больших двоичных объектов. При загрузке файлов оно не понимает концепцию типов файлов, расширений и т. д., как это делает Google Drive. Для Cloud Storage это просто «большие двоичные объекты». Кроме того, в Cloud Storage нет понятия папок или подкаталогов.
Да, в именах файлов можно использовать косые черты ( / ) для обозначения абстракции нескольких подпапок, но в конечном итоге все ваши большие двоичные объекты помещаются в контейнер, а / черты — это просто символы в именах файлов. Более подробную информацию можно найти на странице, посвященной соглашениям об именовании контейнеров и объектов .
На шаге 1 выше запрашивалась область действия «только для чтения» в Google Диск. На тот момент этого было достаточно. Теперь требуется разрешение на загрузку (чтение и запись) в Cloud Storage. Измените SCOPES с одной строковой переменной на массив (кортеж Python [или список]) областей действия разрешений, чтобы он выглядел следующим образом:
SCOPES = (
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/devstorage.full_control',
)
Теперь создайте конечную точку сервиса для Cloud Storage сразу под конечной точкой для Drive. Обратите внимание, что мы немного изменили вызов, чтобы повторно использовать тот же объект HTTP-клиента, поскольку нет необходимости создавать новый, когда это может быть общий ресурс.
# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE = discovery.build('drive', 'v3', http=HTTP)
GCS = discovery.build('storage', 'v1', http=HTTP)
Теперь добавьте эту функцию (после drive_get_img() ), которая загружает изображение в облачное хранилище:
def gcs_blob_upload(fname, bucket, media, mimetype):
'upload an object to a Google Cloud Storage bucket'
# build blob metadata and upload via GCS API
body = {'name': fname, 'uploadType': 'multipart', 'contentType': mimetype}
return GCS.objects().insert(bucket=bucket, body=body,
media_body=http.MediaIoBaseUpload(io.BytesIO(media), mimetype),
fields='bucket,name').execute()
Для вызова objects.().insert() требуется имя корзины, метаданные файла и сам двоичный объект. Чтобы отфильтровать возвращаемые значения, переменная fields запрашивает только имена корзины и объекта, возвращаемые API. Подробнее об этих масках полей в запросах на чтение API можно узнать в этой статье и видео .
Теперь интегрируем использование функции gcs_blob_upload() в основное приложение:
# upload file to GCS
gcsname = '%s/%s'% (PARENT, fname)
rsp = gcs_blob_upload(gcsname, BUCKET, data, mtype)
if rsp:
print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))
else:
print('ERROR: Cannot upload %r to Cloud Storage' % gcsname)
Переменная gcsname объединяет любые имена «родительских подкаталогов», добавленные к самому имени файла, и, если перед ними добавить имя корзины, создает впечатление, что вы архивируете файл по адресу " /bucket/parent.../filename ". Разместите этот фрагмент кода сразу после первой функции print() , непосредственно перед блоком else , чтобы весь код "main" выглядел следующим образом:
if __name__ == '__main__':
# download img file & info from Drive
rsp = drive_get_img(FILE)
if rsp:
fname, mtype, ftime, data = rsp
print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))
# upload file to GCS
gcsname = '%s/%s'% (PARENT, fname)
rsp = gcs_blob_upload(gcsname, BUCKET, data, mtype)
if rsp:
print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))
else:
print('ERROR: Cannot upload %r to Cloud Storage' % gcsname)
else:
print('ERROR: Cannot download %r from Drive' % fname)
Допустим, мы указываем корзину с именем " vision-demo " и " analyzed_imgs " в качестве "родительской подпапки". После установки этих переменных и повторного запуска скрипта файл section-work-card-img_2x.jpg будет загружен с Google Диска, а затем выгружен в Cloud Storage, верно? НЕТ!
$ python3 analyze_gsimg.py
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Traceback (most recent call last):
File "analyze_gsimg.py", line 85, in <module>
io.BytesIO(data), mimetype=mtype), mtype)
File "analyze_gsimg.py", line 72, in gcs_blob_upload
media_body=media, fields='bucket,name').execute()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/googleapiclient/_helpers.py", line 134, in positional_wrapper
return wrapped(*args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/googleapiclient/http.py", line 898, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://storage.googleapis.com/upload/storage/v1/b/PROJECT_ID/o?fields=bucket%2Cname&alt=json&uploadType=multipart returned "Insufficient Permission">
Внимательно посмотрите: хотя загрузка на Google Диск прошла успешно, загрузка в облачное хранилище не удалась. Почему?
Причина в том, что при первоначальной авторизации этого приложения на шаге 1 мы предоставили только доступ на чтение к Google Drive. Хотя мы добавили область доступа на чтение и запись для Cloud Storage, мы никогда не запрашивали у пользователя подтверждение этого доступа. Чтобы это заработало, нам нужно удалить файл storage.json , в котором отсутствует эта область доступа, и запустить процесс заново.
После повторной авторизации (подтвердите это, просмотрев файл storage.json и увидев там обе области действия) результат будет соответствовать ожиданиям:
$ python3 analyze_gsimg.py
. . .
Authentication successful.
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Uploaded 'analyzed_imgs/section-work-card-img_2x.jpg' to GCS bucket 'vision-demo'
Краткое содержание
Это очень важный момент, демонстрирующий, всего несколькими строками кода, как передавать файлы между двумя облачными системами хранения. В данном случае речь идёт о резервном копировании потенциально ограниченного ресурса в более «холодное» и дешёвое хранилище, как упоминалось ранее. Облачное хранилище предлагает различные классы хранения в зависимости от того, обращаетесь ли вы к своим данным регулярно, ежемесячно, ежеквартально или ежегодно.
Конечно, разработчики время от времени спрашивают нас, зачем существуют Google Drive и Cloud Storage. В конце концов, разве они оба не являются облачным хранилищем файлов? Именно поэтому мы сделали это видео . На данном этапе ваш код должен соответствовать коду из репозитория step2-gcs/analyze_gsimg.py .
10. Шаг 3: Анализ с помощью Cloud Vision
Хотя мы теперь знаем, что данные можно перемещать между Google Cloud и Google Workspace, мы еще не проводили никакого анализа, поэтому пора отправить изображение в Cloud Vision для аннотирования меток, то есть обнаружения объектов. Для этого нам нужно закодировать данные в Base64, а это значит, что нам понадобится еще один модуль Python — base64 . Убедитесь, что ваш раздел импорта теперь выглядит так:
from __future__ import print_function
import base64
import io
По умолчанию API Vision возвращает все найденные метки. Для обеспечения согласованности давайте запросим только 5 самых важных (конечно, пользователь может это изменить). Для этого мы будем использовать константу TOP ; добавьте её под всеми остальными константами:
FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = '' # YOUR IMG FILE PREFIX
TOP = 5 # TOP # of VISION LABELS TO SAVE
Как и на предыдущих шагах, нам потребуется еще одна область разрешений, на этот раз для Vision API. Обновите SCOPES , указав ее строку:
SCOPES = (
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/devstorage.full_control',
'https://www.googleapis.com/auth/cloud-vision',
)
Теперь создайте конечную точку сервиса для Cloud Vision так, чтобы она соответствовала остальным, следующим образом:
# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE = discovery.build('drive', 'v3', http=HTTP)
GCS = discovery.build('storage', 'v1', http=HTTP)
VISION = discovery.build('vision', 'v1', http=HTTP)
Теперь добавьте функцию, которая отправляет данные изображения в Cloud Vision:
def vision_label_img(img, top):
'send image to Vision API for label annotation'
# build image metadata and call Vision API to process
body = {'requests': [{
'image': {'content': img},
'features': [{'type': 'LABEL_DETECTION', 'maxResults': top}],
}]}
rsp = VISION.images().annotate(body=body).execute().get('responses', [{}])[0]
# return top labels for image as CSV for Sheet (row)
if 'labelAnnotations' in rsp:
return ', '.join('(%.2f%%) %s' % (
label['score']*100., label['description']) \
for label in rsp['labelAnnotations'])
Для вызова images().annotate() требуются данные и необходимые функции API. Ограничение на 5 лучших меток также является частью полезной нагрузки (но это совершенно необязательно). В случае успешного вызова полезная нагрузка возвращает 5 лучших меток объектов, а также оценку достоверности присутствия объекта на изображении. (Если ответа нет, присвойте пустой словарь Python, чтобы следующее условие if не завершилось ошибкой.) Эта функция просто объединяет эти данные в строку CSV для последующего использования в нашем отчете.
Эти 5 строк кода, вызывающих функцию vision_label_img() следует разместить сразу после успешной загрузки в облачное хранилище:
# process w/Vision
rsp = vision_label_img(base64.b64encode(data).decode('utf-8'), TOP)
if rsp:
print('Top %d labels from Vision API: %s' % (TOP, rsp))
else:
print('ERROR: Vision API cannot analyze %r' % fname)
После этого весь основной драйвер должен выглядеть следующим образом:
if __name__ == '__main__':
# download img file & info from Drive
rsp = drive_get_img(FILE)
if rsp:
fname, mtype, ftime, data = rsp
print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))
# upload file to GCS
gcsname = '%s/%s'% (PARENT, fname)
rsp = gcs_blob_upload(gcsname, BUCKET, data, mtype)
if rsp:
print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))
# process w/Vision
rsp = vision_label_img(base64.b64encode(data).decode('utf-8'), TOP)
if rsp:
print('Top %d labels from Vision API: %s' % (TOP, rsp))
else:
print('ERROR: Vision API cannot analyze %r' % fname)
else:
print('ERROR: Cannot upload %r to Cloud Storage' % gcsname)
else:
print('ERROR: Cannot download %r from Drive' % fname)
Удаление storage.json для обновления областей видимости и повторный запуск обновленного приложения должны привести к результату, аналогичному приведенному ниже, с добавлением анализа Cloud Vision:
$ python3 analyze_gsimg.py
. . .
Authentication successful.
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Uploaded 'analyzed_imgs/section-work-card-img_2x.jpg' to GCS bucket 'vision-demo'
Top 5 labels from Vision API: (89.94%) Sitting, (86.09%) Interior design, (82.08%) Furniture, (81.52%) Table, (80.85%) Room
Краткое содержание
Не у всех есть необходимые знания в области машинного обучения для создания и обучения собственных моделей машинного обучения для анализа данных. Команда Google Cloud предоставила доступ к некоторым предварительно обученным моделям Google для общего использования и разместила их за API, помогая демократизировать ИИ и машинное обучение для всех.
Если вы разработчик и можете вызывать API, вы можете использовать машинное обучение. Cloud Vision — это лишь один из API-сервисов, которые вы можете использовать для анализа данных. Узнайте о других здесь . Ваш код теперь должен соответствовать коду из репозитория step3-vision/analyze_gsimg.py .
11. Шаг 4: Создайте отчет с помощью Google Таблиц.
К этому моменту вы уже смогли заархивировать корпоративные данные и проанализировать их, но не хватает сводки проделанной работы. Давайте организуем все результаты в единый отчет, который вы сможете передать своему начальнику. Что может быть более презентабельным для руководства, чем электронная таблица?
Для использования API Google Sheets не требуется никаких дополнительных импортов, и единственная необходимая новая информация — это идентификатор файла существующей электронной таблицы, уже отформатированной и ожидающей добавления новой строки данных, отсюда и константа SHEET . Мы рекомендуем создать новую электронную таблицу, которая будет выглядеть примерно так:

URL этой электронной таблицы будет выглядеть следующим образом: https://docs.google.com/spreadsheets/d/ /edit Получите этот FILE_ID и присвойте его в качестве строки для FILE_ID SHEET .
Мы также незаметно добавили небольшую функцию k_ize() , которая преобразует байты в килобайты, определяя её как lambda Python, поскольку это простая однострочная операция. Вместе с другими константами это выглядит так:
k_ize = lambda b: '%6.2fK' % (b/1000.) # bytes to kBs
FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = '' # YOUR IMG FILE PREFIX
SHEET = 'YOUR_SHEET_ID'
TOP = 5 # TOP # of VISION LABELS TO SAVE
Как и на предыдущих этапах, нам нужна еще одна область разрешений, на этот раз чтение/запись для API Google Sheets. Теперь SCOPES есть все 4 необходимых разрешения:
SCOPES = (
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/devstorage.full_control',
'https://www.googleapis.com/auth/cloud-vision',
'https://www.googleapis.com/auth/spreadsheets',
)
Теперь создайте конечную точку службы для Google Таблиц рядом с остальными, чтобы она выглядела следующим образом:
# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE = discovery.build('drive', 'v3', http=HTTP)
GCS = discovery.build('storage', 'v1', http=HTTP)
VISION = discovery.build('vision', 'v1', http=HTTP)
SHEETS = discovery.build('sheets', 'v4', http=HTTP)
Функционал функции sheet_append_row() прост: она берет строку данных и идентификатор листа, а затем добавляет эту строку на данный лист.
def sheet_append_row(sheet, row):
'append row to a Google Sheet, return #cells added'
# call Sheets API to write row to Sheet (via its ID)
rsp = SHEETS.spreadsheets().values().append(
spreadsheetId=sheet, range='Sheet1',
valueInputOption='USER_ENTERED', body={'values': [row]}
).execute()
if rsp:
return rsp.get('updates').get('updatedCells')
Для вызова spreadsheets().values().append() требуется идентификатор файла листа, диапазон ячеек, способ ввода данных и сами данные. Идентификатор файла указан просто, диапазон ячеек задается в формате A1 . Диапазон " Sheet1 " означает весь лист — это сигнал для API добавить строку после всех данных на листе. Существует два варианта добавления данных на лист : " RAW " (ввод строковых данных без изменений) или " USER_ENTERED " (запись данных так, как если бы пользователь ввел их с клавиатуры в приложении Google Sheets, сохраняя при этом все особенности форматирования ячеек).
Если вызов успешен, возвращаемое значение не содержит ничего особо полезного, поэтому мы решили получить количество ячеек, обновленных запросом к API. Ниже приведен код, который вызывает эту функцию:
# push results to Sheet, get cells-saved count
fsize = k_ize(len(data))
row = [PARENT,
'=HYPERLINK("storage.cloud.google.com/%s/%s", "%s")' % (
BUCKET, gcsname, fname), mtype, ftime, fsize, rsp
]
rsp = sheet_append_row(SHEET, row)
if rsp:
print('Updated %d cells in Google Sheet' % rsp)
else:
print('ERROR: Cannot write row to Google Sheets')
В таблице Google Sheets есть столбцы, отображающие такие данные, как родительский «подкаталог», местоположение заархивированного файла в Cloud Storage (корзина + имя файла), MIME-тип файла, размер файла (первоначально в байтах, но преобразованный в килобайты с помощью k_ize() ) и строка меток Cloud Vision. Также обратите внимание, что местоположение заархивированного файла является гиперссылкой, чтобы ваш руководитель мог щелкнуть по ней и убедиться, что резервная копия была безопасно сохранена.
Добавив указанный выше блок кода сразу после отображения результатов Cloud Vision, основная часть приложения теперь полностью готова, хотя и несколько сложна по структуре:
if __name__ == '__main__':
# download img file & info from Drive
rsp = drive_get_img(FILE)
if rsp:
fname, mtype, ftime, data = rsp
print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))
# upload file to GCS
gcsname = '%s/%s'% (PARENT, fname)
rsp = gcs_blob_upload(gcsname, BUCKET, data, mtype)
if rsp:
print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))
# process w/Vision
rsp = vision_label_img(base64.b64encode(data).decode('utf-8'))
if rsp:
print('Top %d labels from Vision API: %s' % (TOP, rsp))
# push results to Sheet, get cells-saved count
fsize = k_ize(len(data))
row = [PARENT,
'=HYPERLINK("storage.cloud.google.com/%s/%s", "%s")' % (
BUCKET, gcsname, fname), mtype, ftime, fsize, rsp
]
rsp = sheet_append_row(SHEET, row)
if rsp:
print('Updated %d cells in Google Sheet' % rsp)
else:
print('ERROR: Cannot write row to Google Sheets')
else:
print('ERROR: Vision API cannot analyze %r' % fname)
else:
print('ERROR: Cannot upload %r to Cloud Storage' % gcsname)
else:
print('ERROR: Cannot download %r from Drive' % fname)
После последнего удаления storage.json и повторного запуска обновленного приложения должен получиться результат, аналогичный приведенному ниже, с добавлением анализа Cloud Vision:
$ python3 analyze_gsimg.py
. . .
Authentication successful.
Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Uploaded 'analyzed_imgs/section-work-card-img_2x.jpg' to GCS bucket 'vision-demo'
Top 5 labels from Vision API: (89.94%) Sitting, (86.09%) Interior design, (82.08%) Furniture, (81.52%) Table, (80.85%) Room
Updated 6 cells in Google Sheet
Дополнительная строка вывода, хотя и полезна, лучше визуализируется при взгляде на обновленную таблицу Google Sheets, где последняя строка (строка 7 в приведенном ниже примере) добавлена к существующему набору данных, добавленному ранее:

Краткое содержание
На первых трех этапах этого руководства вы подключились к API Google Workspace и Google Cloud для перемещения и анализа данных, что составляет 80% всей работы. Однако в конечном итоге все это не имеет никакого значения, если вы не можете представить руководству все проделанную работу. Для лучшей визуализации результатов полезно обобщить все результаты в сгенерированном отчете.
Для дальнейшего повышения полезности анализа, помимо записи результатов в электронную таблицу, одним из возможных улучшений могло бы стать индексирование этих 5 наиболее часто встречающихся меток для каждого изображения, чтобы можно было создать внутреннюю базу данных, позволяющую уполномоченным сотрудникам запрашивать изображения по группам поиска, но мы оставляем это на усмотрение читателей.
На данный момент результаты находятся в таблице и доступны руководству. Код вашего приложения на этом этапе должен соответствовать коду из репозитория step4-sheets/analyze_gsimg.py . Последний шаг — это очистка кода и преобразование его в пригодный для использования скрипт.
12. *Заключительный шаг: рефакторинг
(необязательно) Хорошо иметь работающее приложение, но можно ли его улучшить? Да, особенно основное приложение, которое выглядит как полная неразбериха. Давайте вынесем его в отдельную функцию и будем управлять им, используя пользовательский ввод, а не фиксированные константы. Мы сделаем это с помощью модуля argparse . Кроме того, давайте запустим вкладку веб-браузера для отображения таблицы после того, как запишем в нее строку данных. Это можно сделать с помощью модуля webbrowser . Объединим эти импорты с остальными, чтобы основные импорты выглядели следующим образом:
from __future__ import print_function
import argparse
import base64
import io
import webbrowser
Чтобы использовать этот код в других приложениях, нам нужна возможность подавлять вывод, поэтому давайте добавим флаг DEBUG , добавив эту строку в конец раздела констант ближе к началу:
DEBUG = False
Теперь о главном. В процессе создания этого примера вы, вероятно, начали чувствовать себя «некомфортно», поскольку наш код добавляет еще один уровень вложенности с каждым добавленным сервисом. Если вы это почувствовали, вы не одиноки, поскольку это увеличивает сложность кода, как описано в этой статье блога Google Testing .
Следуя этой рекомендации, давайте реорганизуем основную часть приложения в функцию и return на каждой «точке останова» вместо вложенности (возвращая None если какой-либо шаг завершился неудачей, и True если все шаги прошли успешно):
def main(fname, bucket, sheet_id, folder, top, debug):
'"main()" drives process from image download through report generation'
# download img file & info from Drive
rsp = drive_get_img(fname)
if not rsp:
return
fname, mtype, ftime, data = rsp
if debug:
print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))
# upload file to GCS
gcsname = '%s/%s'% (folder, fname)
rsp = gcs_blob_upload(gcsname, bucket, data, mtype)
if not rsp:
return
if debug:
print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))
# process w/Vision
rsp = vision_label_img(base64.b64encode(data).decode('utf-8'))
if not rsp:
return
if debug:
print('Top %d labels from Vision API: %s' % (top, rsp))
# push results to Sheet, get cells-saved count
fsize = k_ize(len(data))
row = [folder,
'=HYPERLINK("storage.cloud.google.com/%s/%s", "%s")' % (
bucket, gcsname, fname), mtype, ftime, fsize, rsp
]
rsp = sheet_append_row(sheet_id, row)
if not rsp:
return
if debug:
print('Added %d cells to Google Sheet' % rsp)
return True
It's neater and cleaner, leaving behind that recursive if-else chain feeling along with reducing code complexity as described above. The last piece of the puzzle is to create a "real" main driver, allowing for user customization, and minimizing output (unless desired):
if __name__ == '__main__':
# args: [-hv] [-i imgfile] [-b bucket] [-f folder] [-s Sheet ID] [-t top labels]
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--imgfile", action="store_true",
default=FILE, help="image file filename")
parser.add_argument("-b", "--bucket_id", action="store_true",
default=BUCKET, help="Google Cloud Storage bucket name")
parser.add_argument("-f", "--folder", action="store_true",
default=PARENT, help="Google Cloud Storage image folder")
parser.add_argument("-s", "--sheet_id", action="store_true",
default=SHEET, help="Google Sheet Drive file ID (44-char str)")
parser.add_argument("-t", "--viz_top", action="store_true",
default=TOP, help="return top N (default %d) Vision API labels" % TOP)
parser.add_argument("-v", "--verbose", action="store_true",
default=DEBUG, help="verbose display output")
args = parser.parse_args()
print('Processing file %r... please wait' % args.imgfile)
rsp = main(args.imgfile, args.bucket_id,
args.sheet_id, args.folder, args.viz_top, args.verbose)
if rsp:
sheet_url = 'https://docs.google.com/spreadsheets/d/%s/edit' % args.sheet_id
print('DONE: opening web browser to it, or see %s' % sheet_url)
webbrowser.open(sheet_url, new=1, autoraise=True)
else:
print('ERROR: could not process %r' % args.imgfile)
If all steps are successful, the script launches a web browser to the spreadsheet specified where the new data row was added.
Краткое содержание
No need to delete storage.json since no scope changes occurred. Re-run the updated application reveals a new browser window opened to the modified Sheet, fewer lines of output, and issuing a -h option shows users their options, including -v to restore the now-suppressed lines of output seen earlier:
$ python3 analyze_gsimg.py Processing file 'section-work-card-img_2x.jpg'... please wait DONE: opening web browser to it, or see https://docs.google.com/spreadsheets/d/SHEET_ID/edit $ python3 analyze_gsimg.py -h usage: analyze_gsimg.py [-h] [-i] [-t] [-f] [-b] [-s] [-v] optional arguments: -h, --help show this help message and exit -i, --imgfile image file filename -t, --viz_top return top N (default 5) Vision API labels -f, --folder Google Cloud Storage image folder -b, --bucket_id Google Cloud Storage bucket name -s, --sheet_id Google Sheet Drive file ID (44-char str) -v, --verbose verbose display output
The other options let users choose different Drive file names, Cloud Storage "subdirectory" and bucket names, top "N" results from Cloud Vision, and spreadsheet (Sheets) file IDs. With these last updates, the final version of your code should now match what's in the repo at final/analyze_gsimg.py as well as here, in its entirety:
'''
analyze_gsimg.py - analyze Google Workspace image processing workflow
Download image from Google Drive, archive to Google Cloud Storage, send
to Google Cloud Vision for processing, add results row to Google Sheet.
'''
from __future__ import print_function
import argparse
import base64
import io
import webbrowser
from googleapiclient import discovery, http
from httplib2 import Http
from oauth2client import file, client, tools
k_ize = lambda b: '%6.2fK' % (b/1000.) # bytes to kBs
FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = '' # YOUR IMG FILE PREFIX
SHEET = 'YOUR_SHEET_ID'
TOP = 5 # TOP # of VISION LABELS TO SAVE
DEBUG = False
# process credentials for OAuth2 tokens
SCOPES = (
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/devstorage.full_control',
'https://www.googleapis.com/auth/cloud-vision',
'https://www.googleapis.com/auth/spreadsheets',
)
store = file.Storage('storage.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)
# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE = discovery.build('drive', 'v3', http=HTTP)
GCS = discovery.build('storage', 'v1', http=HTTP)
VISION = discovery.build('vision', 'v1', http=HTTP)
SHEETS = discovery.build('sheets', 'v4', http=HTTP)
def drive_get_img(fname):
'download file from Drive and return file info & binary if found'
# search for file on Google Drive
rsp = DRIVE.files().list(q="name='%s'" % fname,
fields='files(id,name,mimeType,modifiedTime)'
).execute().get('files', [])
# download binary & return file info if found, else return None
if rsp:
target = rsp[0] # use first matching file
fileId = target['id']
fname = target['name']
mtype = target['mimeType']
binary = DRIVE.files().get_media(fileId=fileId).execute()
return fname, mtype, target['modifiedTime'], binary
def gcs_blob_upload(fname, bucket, media, mimetype):
'upload an object to a Google Cloud Storage bucket'
# build blob metadata and upload via GCS API
body = {'name': fname, 'uploadType': 'multipart', 'contentType': mimetype}
return GCS.objects().insert(bucket=bucket, body=body,
media_body=http.MediaIoBaseUpload(io.BytesIO(media), mimetype),
fields='bucket,name').execute()
def vision_label_img(img, top):
'send image to Vision API for label annotation'
# build image metadata and call Vision API to process
body = {'requests': [{
'image': {'content': img},
'features': [{'type': 'LABEL_DETECTION', 'maxResults': top}],
}]}
rsp = VISION.images().annotate(body=body).execute().get('responses', [{}])[0]
# return top labels for image as CSV for Sheet (row)
if 'labelAnnotations' in rsp:
return ', '.join('(%.2f%%) %s' % (
label['score']*100., label['description']) \
for label in rsp['labelAnnotations'])
def sheet_append_row(sheet, row):
'append row to a Google Sheet, return #cells added'
# call Sheets API to write row to Sheet (via its ID)
rsp = SHEETS.spreadsheets().values().append(
spreadsheetId=sheet, range='Sheet1',
valueInputOption='USER_ENTERED', body={'values': [row]}
).execute()
if rsp:
return rsp.get('updates').get('updatedCells')
def main(fname, bucket, sheet_id, folder, top, debug):
'"main()" drives process from image download through report generation'
# download img file & info from Drive
rsp = drive_get_img(fname)
if not rsp:
return
fname, mtype, ftime, data = rsp
if debug:
print('Downloaded %r (%s, %s, size: %d)' % (fname, mtype, ftime, len(data)))
# upload file to GCS
gcsname = '%s/%s'% (folder, fname)
rsp = gcs_blob_upload(gcsname, bucket, data, mtype)
if not rsp:
return
if debug:
print('Uploaded %r to GCS bucket %r' % (rsp['name'], rsp['bucket']))
# process w/Vision
rsp = vision_label_img(base64.b64encode(data).decode('utf-8'), top)
if not rsp:
return
if debug:
print('Top %d labels from Vision API: %s' % (top, rsp))
# push results to Sheet, get cells-saved count
fsize = k_ize(len(data))
row = [folder,
'=HYPERLINK("storage.cloud.google.com/%s/%s", "%s")' % (
bucket, gcsname, fname), mtype, ftime, fsize, rsp
]
rsp = sheet_append_row(sheet_id, row)
if not rsp:
return
if debug:
print('Added %d cells to Google Sheet' % rsp)
return True
if __name__ == '__main__':
# args: [-hv] [-i imgfile] [-b bucket] [-f folder] [-s Sheet ID] [-t top labels]
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--imgfile", action="store_true",
default=FILE, help="image file filename")
parser.add_argument("-b", "--bucket_id", action="store_true",
default=BUCKET, help="Google Cloud Storage bucket name")
parser.add_argument("-f", "--folder", action="store_true",
default=PARENT, help="Google Cloud Storage image folder")
parser.add_argument("-s", "--sheet_id", action="store_true",
default=SHEET, help="Google Sheet Drive file ID (44-char str)")
parser.add_argument("-t", "--viz_top", action="store_true",
default=TOP, help="return top N (default %d) Vision API labels" % TOP)
parser.add_argument("-v", "--verbose", action="store_true",
default=DEBUG, help="verbose display output")
args = parser.parse_args()
print('Processing file %r... please wait' % args.imgfile)
rsp = main(args.imgfile, args.bucket_id,
args.sheet_id, args.folder, args.viz_top, args.verbose)
if rsp:
sheet_url = 'https://docs.google.com/spreadsheets/d/%s/edit' % args.sheet_id
print('DONE: opening web browser to it, or see %s' % sheet_url)
webbrowser.open(sheet_url, new=1, autoraise=True)
else:
print('ERROR: could not process %r' % args.imgfile)
We will make every attempt to keep this tutorial's contents up-to-date, but there will be occasions where the repo will have the most recent version of the code.
13. Поздравляем!
There was certainly a lot of learning in this codelab, and you achieved that, surviving one of the longer codelabs. As a result, you tackled a possible enterprise scenario with about ~130 lines of Python, leveraging all of Google Cloud and Google Workspace, and moving data between them to build a working solution. Feel free to explore the open source repo for all versions of this app (more info below).
Уборка
- Use of Google Cloud APIs are not free while Google Workspace APIs are covered by your monthly Google Workspace subscription fee (consumer Gmail users have a monthly fee of zero), so there's no API clean-up/turndown required for Google Workspace users. For Google Cloud, you can go to your Cloud console dashboard and check the Billing "card" for estimated charges.
- For Cloud Vision , you're allowed a fixed number of API calls per month for free. So as long as you stay under those limits, there's no need to shut anything down nor must you disable/delete your project. More information on the Vision API's billing and free quota can be found on its pricing page .
- Some Cloud Storage users receive a free amount of storage per month. If the images you archive using this codelab do not cause you to exceed that quota, you will not incur any charges. More information on GCS billing and free quota can be found on its pricing page . You can view and easily delete blobs from the Cloud Storage browser .
- Your use of Google Drive may also have a storage quota, and if you exceed it (or are close to it), you may actually consider using the tool you built in this codelab to archive those images to Cloud Storage to give yourself more space on Drive. More information on Google Drive storage can be found on the appropriate pricing page for Google Workspace Basic users or Gmail/consumer users.
While most Google Workspace Business and Enterprise plans have unlimited storage, this could cause your Drive folders to be cluttered and/or overwhelming, and the app you built in this tutorial is a great way to archive extraneous files and clean-up your Google Drive.
Альтернативные версии
While final/analyze_gsimg.py is the "last" official version you're working on in this tutorial, it's not the end of the story. One issue with the final version of the app is that it uses the older auth libraries which have been deprecated. We chose this path because, at the time of this writing, the newer auth libraries did not support several key elements: OAuth token storage management and thread safety.
Current (newer) auth libraries
However, at some point, the older auth libraries will no longer be supported, so we encourage you to review versions that use the newer (current) auth libraries in the repo's alt folder even if they aren't threadsafe (but you can build your own solution that is). Look for files with *newauth* in their names.
Google Cloud product client libraries
Google Cloud recommends all developers use the product client libraries when using Google Cloud APIs. Unfortunately non-Google Cloud APIs don't have such libraries at this time. Use of the lower-level libraries allow for consistent API usage and features better readability. Similar to the recommendation above, alternative versions using Google Cloud product client libraries are available in the repo's alt folder for you to review. Look for files with *-gcp* in their names.
Service account authorization
When working purely in the cloud, there generally aren't humans nor (human) user-owned data, so that's why service accounts and service account authorization are primarily used with Google Cloud. However, Google Workspace documents are generally owned by (human) users, so that's why this tutorial uses user account authorization. That doesn't mean it's not possible to use Google Workspace APIs with service accounts. As long as those accounts have the appropriate access level, they can certainly be used in applications. Similar to the above, alternative versions using service account authorization are available in the repo's alt folder for you to review. Look for files with *-svc* in their names.
Alternative version catalog
Below, you'll find all alternative versions of final/analyze_gsimg.py , each having one or more of the properties above. In each version's filename, look for:
- "
oldauth" for versions using the older auth libraries (in addition tofinal/analyze_gsimg.py) - "
newauth" for those using the current/newer auth libraries - "
gcp" for those using Google Cloud product client libraries, ie, google-cloud-storage, etc. - "
svc" for those using a service account ("svc acct") auth instead of a user account
Here are all the versions:
Имя файла | Описание |
| The primary sample; uses the older auth libraries |
| Same as |
| Same as |
| Same as |
| Same as |
| Same as |
| Same as |
| Same as |
Coupled with the original final/analyze_gsimg.py , you have all possible combinations of the final solution, regardless of your Google API development environment, and can choose the one which best suits your needs. Also see alt/README.md for a similar explanation.
Дополнительное исследование
Below are a few ideas of how you can take this exercise a step or two further. The problem set the current solution can handle can be expanded allowing you to make these enhancements:
- ( multiple images in folders ) Instead of processing one image, let's say you had images in Google Drive folders .
- ( multiple images in ZIP files ) Instead of a folder of images, how about ZIP archives containing image files? If using Python, consider the
zipfilemodule . - ( analyze Vision labels ) Cluster similar images together, perhaps start by looking for the most common labels, then the 2nd most common, and so on.
- ( create charts ) Follow-up #3, generate charts with the Sheets API based on the Vision API analysis and categorization
- ( categorize documents ) Instead of analyzing images with the Cloud Vision API, let's say you have PDF files to categorize with the Cloud Natural Language API . Using your solutions above, these PDFs can be in Drive folders or ZIP archives on Drive.
- ( create presentations ) Use the Slides API to generate a slide deck from the contents of the Google Sheet report. For inspiration, check out this blog post & video on generating slides from spreadsheet data.
- ( export as PDF ) Export the spreadsheet and/or slide deck as PDF, however this isn't a feature of either the Sheets nor Slides APIs. Hint : Google Drive API. Extra credit : merge both the Sheets and Slides PDFs into one master PDF with tools like Ghostscript (Linux, Windows) or
Combine PDF Pages.action(Mac OS X).
Узнать больше
Codelabs
- Intro to Google Workspace APIs (Google Drive API) (Python)
- Using Cloud Vision with Python (Python)
- Build customized reporting tools (Google Sheets API) (JS/Node)
- Upload objects to Google Cloud Storage (no coding required)
Общий
Google Workspace
- Google Drive API home page
- Google Sheets API home page
- Google Workspace developer overview & documentation
Google Облако
- Google Cloud Storage home page
- Google Cloud Vision home page & live demo
- Cloud Vision API documentation
- Vision API image labeling docs
- Python on Google Cloud
- Google Cloud product client libraries
- Документация Google Cloud
Лицензия
Данная работа распространяется под лицензией Creative Commons Attribution 2.0 Generic.