Esse codelab simula um possível fluxo de trabalho corporativo: arquivamento de imagens, análises e geração de relatórios. Imagine que sua organização tenha uma série de imagens ocupando espaço em um recurso restrito. Você quer arquivar esses dados, analisar essas imagens e, o mais importante, gerar um relatório resumindo os locais arquivados e os resultados da análise, agrupados e prontos para consumo pelo gerenciamento. O Google Cloud oferece as ferramentas para que isso aconteça, usando APIs de duas linhas de produtos, G Suite e Google Cloud Platform (GCP).
No nosso cenário, o usuário da empresa terá imagens no Google Drive. É recomendável fazer backup dessas informações para "armazenamento mais antigo", como as classes de armazenamento disponíveis no Google Cloud Storage. O Google Cloud Vision permite que os desenvolvedores integrem facilmente recursos de detecção de visão nos aplicativos, incluindo detecção de objetos e pontos de referência, reconhecimento ótico de caracteres (OCR) etc. Por fim, uma planilha do ( Planilhas Google). é uma ferramenta de visualização útil para resumir tudo isso para seu chefe.
Depois de concluir este codelab para criar uma solução que aproveite todo o Google Cloud, esperamos que você tenha inspiração para criar algo ainda mais relevante para sua organização ou seus clientes.
O que você aprenderá
- Como usar o Cloud Shell
- Como autenticar solicitações de API
- Como instalar a biblioteca de cliente das APIs do Google para Python
- Como ativar as APIs do Google
- Como fazer o download de arquivos do Google Drive
- Como fazer upload de objetos/blobs no Cloud Storage
- Como analisar dados com o Cloud Vision
- Como gravar linhas no Planilhas Google
Pré-requisitos
- Uma Conta do Google (as contas do G Suite podem exigir a aprovação do administrador).
- Um projeto do Google Cloud Platform com uma conta de faturamento do GCP ativa
- Familiaridade com comandos de terminal/shell do sistema operacional
- Habilidades básicas em Python (2 ou 3), mas você pode usar qualquer linguagem compatível
Ter experiência com os quatro produtos do Google Cloud listados acima seria útil, mas não obrigatório. Se você tiver tempo para se familiarizar com eles separadamente, fique à vontade para fazer codelabs para cada um deles antes de fazer o exercício aqui:
- Introdução ao Google Drive (usando as APIs do G Suite) (Python)
- Como usar o Cloud Vision com Python (Python)
- Criar ferramentas de relatórios personalizadas com a API Sheets (JS/Node)
- Fazer upload de objetos para o Google Cloud Storage, sem necessidade de codificação.
Pesquisa
Como você usará este tutorial?
Como você classificaria sua experiência com o Python?
Como você classificaria sua experiência com o uso dos serviços do Google Cloud Platform?
Como você classificaria sua experiência com o uso dos serviços para desenvolvedores do G Suite?
Você gostaria de ver mais codelabs "orientados a empresas" do que os relativos à introdução de recursos do produto?
Configuração de ambiente personalizada
- Faça login no Console do Cloud e crie um novo projeto ou reutilize um existente. Crie uma se você ainda não tiver uma conta do Gmail ou do G Suite.
Lembre-se do código do projeto, um nome exclusivo em todos os projetos do Google Cloud. O nome acima já foi escolhido e não servirá para você. Faremos referência a ele mais adiante neste codelab como PROJECT_ID
.
- Em seguida, será necessário ativar o faturamento no Console do Cloud para usar os recursos do Google Cloud.
A execução deste codelab não será muito cara, se for o caso. Siga todas as instruções na seção "Limpeza", que orienta você sobre como encerrar recursos para não incorrer em cobranças além deste tutorial. Novos usuários do Google Cloud estão qualificados para o programa de avaliação gratuita de US$ 300.
Iniciar Cloud Shell
Resumo
Embora você possa desenvolver o código localmente em seu laptop, um objetivo secundário deste codelab é ensinar como usar o Google Cloud Shell, um ambiente de linha de comando executado na nuvem por meio de seu navegador da web moderno.
Ativar o Cloud Shell
- No Console do Cloud, clique em Ativar o Cloud Shell.
Se você nunca tiver iniciado o Cloud Shell, verá uma tela intermediária (abaixo da dobra) com a descrição do que ele é. Se esse for o caso, clique em Continuar e você não o verá novamente. Esta é uma tela única:
Leva apenas alguns instantes para provisionar e se conectar ao Cloud Shell.
Essa máquina virtual contém todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Praticamente todo o seu trabalho neste codelab pode ser feito em um navegador ou no seu Chromebook.
Depois de se conectar ao Cloud Shell, você já estará autenticado e o projeto já estará configurado com seu ID do projeto.
- Execute o seguinte comando no Cloud Shell para confirmar que você está autenticado:
gcloud auth list
Resposta ao comando
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
gcloud config list project
Resposta ao comando
[core] project = <PROJECT_ID>
Se o projeto não estiver configurado, faça a configuração usando este comando:
gcloud config set project <PROJECT_ID>
Resposta ao comando
Updated property [core/project].
Esse codelab requer o uso da linguagem Python (embora muitas linguagens sejam compatíveis pelas bibliotecas cliente das APIs do Google, portanto, sinta-se à vontade para criar algo equivalente em sua ferramenta de desenvolvimento favorita e simplesmente usar o Python como pseudocódigo). Esse codelab é compatível com o Python 2 e 3, mas recomendamos migrar para o 3.x assim que possível.
O Cloud Shell é uma conveniência disponível para usuários do Console do Cloud e não requer um ambiente de desenvolvimento local. Portanto, esse tutorial pode ser feito na nuvem com um navegador da Web. O Cloud Shell será útil principalmente se você estiver desenvolvendo ou planejando continuar com os produtos e as APIs do GCP. Mais especificamente para este codelab, o Cloud Shell já instalou as duas versões do Python.
O Cloud Shell também tem o IPython instalado... é um interpretador Python de nível superior que recomendamos, especialmente se você fizer parte da comunidade de ciência de dados ou machine learning. Se você estiver, o IPython é o interpretador padrão para o Jupyter Notebooks e o Colab, os notebooks do Jupyter hospedados pelo Google Research.
O IPython favorece um interpretador do Python 3 primeiro, mas retorna para o Python 2, caso 3.x não esteja disponível. O IPython pode ser acessado no Cloud Shell, mas também pode ser instalado em um ambiente de desenvolvimento local. Saia com ^D (Ctrl-d) e aceite a oferta para sair. A saída de exemplo de ipython
terá esta aparência:
$ 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]:
Se IPython não for sua preferência, o uso de um interpretador interativo padrão do Python (o Cloud Shell ou seu ambiente de desenvolvimento local) é perfeitamente aceitável (também saia com ^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. >>>
O codelab também pressupõe que você tenha a ferramenta de instalação pip
(gerenciador de pacotes Python e resolvedor de dependências). Ele vem com as versões 2.7.9 ou superiores ou 3.4+. Se você tiver uma versão mais antiga do Python, consulte este guia para ver instruções de instalação. Dependendo das suas permissões, talvez seja necessário ter sudo
ou acesso de superusuário, mas esse não é o caso. Também é possível usar explicitamente pip2
ou pip3
para executar pip
para versões específicas do Python.
O restante do codelab considera que você está usando o Python 3. Instruções específicas serão fornecidas para o Python 2 se forem muito diferentes de 3.x.
*Crie e use ambientes virtuais
Esta seção é opcional e só é necessária para as pessoas que devem usar um ambiente virtual para este codelab (conforme a barra lateral de aviso acima). Se você tiver apenas o Python 3 no seu computador, basta emitir este comando para criar um virtualenv chamado my_env
(você pode escolher outro nome, se quiser):
virtualenv my_env
No entanto, se você tiver o Python 2 e o 3 no computador, recomendamos instalar um virtualenv Python 3, que você pode fazer com o -p flag
da seguinte maneira:
virtualenv -p python3 my_env
Insira o virtualenv recém-criado "ativando" assim:
source my_env/bin/activate
Para confirmar que você está no ambiente, observe que o prompt do shell agora está precedido pelo nome do seu ambiente, ou seja,
(my_env) $
Agora, você deverá ser capaz de pip install
quaisquer pacotes necessários, executar o código nessa e assim por diante etc. Outro benefício é que, se você expuser o conteúdo completamente, entrar em uma situação em que a instalação do Python esteja corrompida etc., poderá explodir esse ambiente inteiro sem afetar o restante do sistema.
Esse codelab requer o uso da biblioteca de cliente de APIs do Google para Python. Portanto, é um processo de instalação simples ou você não precisa fazer nada.
Recomendamos que você use o Cloud Shell por conveniência. É possível concluir todo o tutorial em um navegador da Web na nuvem. Outro motivo para usar o Cloud Shell é que muitas ferramentas de desenvolvimento conhecidas e as bibliotecas necessárias já estão pré-instaladas.
Instalar bibliotecas de cliente
(opcional): é possível pular essa etapa se você estiver usando o Cloud Shell ou um ambiente local onde já instalou as bibliotecas de cliente. Só será necessário fazer isso se você estiver desenvolvendo localmente e não tiver instalado (ou tiver certeza). A maneira mais fácil é usar pip
(ou pip3
) para fazer a instalação (incluindo a atualização de pip
se necessário):
pip install -U pip google-api-python-client oauth2client
Confirmar instalação
Esse comando instala a biblioteca de cliente e os pacotes de que ela depende. Se você está usando o Cloud Shell ou seu próprio ambiente, verifique se a biblioteca de cliente está instalada importando os pacotes necessários e confirme se não há erros de importação (ou saída):
python3 -c "import googleapiclient, httplib2, oauth2client"
Se você usar o Python 2 no Cloud Shell, verá um aviso de que o uso dele foi suspenso:
******************************************************************************* 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. *******************************************************************************
Agora que você pode executar o comando "test" de importação (sem erros/saída), estará pronto para começar a falar com as APIs do Google.
Resumo
Como este é um codelab intermediário, você presume que já tem experiência na criação e no uso de projetos no console. Se você nunca usou as APIs do Google e as APIs do G Suite, consulte o codelab de introdução às APIs do G Suite. Além disso, se você souber como criar (ou reutilizar as já existentes)conta de usuário (exceto conta de serviço ), solte aclient_secret.json
no diretório de trabalho, pular o próximo módulo e pular para "Ativar APIs do Google".
Você pode pular esta seção se já tiver criado as credenciais de autorização da conta de usuário e estiver familiarizado com o processo. Isso é diferente da autorização da conta de serviço cuja técnica é diferente. Continue abaixo.
Introdução à autorização (além de alguma autenticação)
Para fazer solicitações às APIs, seu aplicativo precisa ter a autorização adequada. Autenticação, uma palavra semelhante, descreve credenciais de login. Você se autentica ao fazer login na sua Conta do Google com um login e uma senha. Após a autenticação, a próxima etapa é seu código é autorizado para acessar dados, como arquivos blob no Cloud Storage ou os arquivos pessoais de um usuário no Google Drive.
As APIs do Google permitem vários tipos de autorização, mas o mais comum para os usuários da API G Suite é a autorização do usuário, porque o aplicativo de exemplo neste codelab acessa dados pertencentes a usuários finais. Esses usuários finais precisam permitir que seu app acesse os dados. Isso significa que seu código precisa receber as credenciais do OAuth2 da conta de usuário.
Para obter as credenciais do OAuth2 para autorização do usuário, volte para o gerenciador de API e selecione a guia "Credenciais" no painel de navegação à esquerda:
Quando chegar lá, você verá todas as suas credenciais em três seções separadas:
O primeiro é para chaves de API, os dois IDs de cliente do OAuth 2.0 e as últimas contas de serviço do OAuth2,que são usadas no do meio.
Como criar credenciais
Na página "Credenciais", clique no botão + Criar credenciais na parte superior. Você verá uma caixa de diálogo em que escolheria "ID do cliente OAuth":
Na tela seguinte, você tem duas ações: configurar a "tela de consentimento" de autorização do seu aplicativo e escolher o tipo de aplicativo:
Se você não definiu uma tela de consentimento, verá o aviso no console e precisará fazer isso agora. (Ignore esta etapa se a tela de consentimento já tiver sido configurada.)
Tela de consentimento OAuth
Clique em "Configurar tela de consentimento" ao selecionar um aplicativo "Externo" (ou "Interno", se você for cliente do G Suite):
Para este exercício, não importa qual você escolher, porque você não está publicando seu exemplo de codelab. A maioria das pessoas seleciona a opção "Externo" para acessar uma tela mais complexa. No entanto, você só precisa preencher o campo "Nome do aplicativo" na parte superior:
Agora, você só precisa do nome de um aplicativo. Escolha alguém que reflita o codelab que você está fazendo e clique em Salvar.
Como criar um ID do cliente OAuth (autenticação de conta de usuário)
Volte para a guia "Credenciais" para criar um ID do cliente do OAuth2. Aqui você verá vários IDs de cliente OAuth que podem ser criados:
Estamos desenvolvendo uma ferramenta de linha de comando, que é Outro. Portanto, escolha essa opção e clique no botão Criar. Escolha o nome de um ID de cliente que reflita o aplicativo que você está criando ou simplesmente use o nome padrão, que geralmente é "Outro cliente N".
Salvar suas credenciais
- Será exibida uma caixa de diálogo com as novas credenciais. Clique em OK para fechar.
- Na página "Credenciais", role para baixo até a seção "IDs de cliente do OAuth2" e clique no ícone de download , na parte inferior direita do seu ID do cliente recém-criado.
- Isso abre uma caixa de diálogo para salvar um arquivo chamado
client_secret-
LONG-HASH-STRING
.apps.googleusercontent.com.json
, provavelmente na pasta Downloads. Recomendamos encurtar para um nome mais fácil, comoclient_secret.json
, que é o que o app de exemplo usa. Em seguida, salve-o no diretório/pasta em que você criará o app de amostra neste codelab.
Resumo
Agora você já pode ativar as APIs do Google usadas neste codelab. Além disso, para o nome do aplicativo na tela de consentimento do OAuth, escolhemos "Demonstração da API Vision". Por isso, esperamos ver em algumas das capturas de tela a seguir.
Introdução
Este codelab usa quatro (4) APIs do Google Cloud, um par do GCP (Cloud Storage e Cloud Vision) e outro par do G Suite (Google Drive e Planilhas Google). Veja abaixo as instruções para ativar apenas a API Vision. Quando você souber como ativar uma API, precisará ativar as outras três por conta própria.
Antes de começar a usar as APIs do Google, você precisa ativá-las. No exemplo abaixo, mostramos o que você faria para ativar a API Cloud Vision. Neste codelab, você pode estar usando uma ou mais APIs e deve seguir etapas semelhantes para ativá-las antes do uso.
No Cloud Shell
Com o Cloud Shell, é possível ativar a API com o seguinte comando:
gcloud services enable vision.googleapis.com
No Console do Cloud
Você também pode ativar a API Vision no Gerenciador de API. No Console do Cloud, acesse o Gerenciador de APIs e selecione "Biblioteca".
Na barra de pesquisa, comece a digitar "vision" e selecione a API Vision quando ela aparecer. Ao digitar, você pode ter esta aparência:
Selecione a API Cloud Vision para acessar a caixa de diálogo que aparece abaixo e clique no botão "Ativar":
Custo
Muitas APIs do Google podem ser usadas sem taxas, mas o uso de GCP (produtos e APIs) não é gratuito. Ao ativar a API Vision (conforme descrito acima), você será solicitado a fornecer uma conta de faturamento ativa. As informações de preços da API Vision precisam ser referenciadas pelo usuário antes da ativação. Lembre-se de que determinados produtos do Google Cloud Platform (GCP) têm um nível "Sempre gratuito" que você precisa exceder para incorrer no faturamento. Para os fins do codelab, cada chamada para a API do Vision é contabilizada nesse nível gratuito. Desde que você permaneça dentro dos limites agregados (em cada mês), não haverá cobranças.
Algumas APIs do Google, por exemplo, O G Suite tem o uso coberto por uma assinatura mensal, por isso não há cobrança direta para o uso das APIs Gmail, Google Drive, Agenda, Documentos, Planilhas e Apresentações, por exemplo. Diferentes produtos do Google são cobrados de maneira diferente. Portanto, consulte a documentação da sua API para obter essa informação.
Resumo
Agora que o Cloud Vision foi ativado, ative as outras três APIs (Google Drive, Cloud Storage, Planilhas Google) da mesma forma... no Cloud Shell, use gcloud services enable
ou no Console do Cloud:
- Voltar para a biblioteca de APIs
- Inicie uma pesquisa digitando algumas letras do nome dela
- Selecione a API desejada.
- Ativar
Ensaboe, enxágue e repita. Para o Cloud Storage, há várias opções. Escolha a "API Google Cloud Storage JSON". A API Cloud Storage também espera uma conta de faturamento ativa.
Este é o começo de uma parte de código de tamanho médio, por isso, queremos um pouco de práticas rápidas e garantir uma infraestrutura comum, estável e funcional antes de processar o aplicativo principal. Verifique se o client_secret.json
está disponível no seu diretório atual e na inicialização ipython
e insira o seguinte snippet de código ou salve-o em analyze_gsimg.py
e execute-o no shell (o último é preferível porque continuaremos adicione à amostra de código):
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)
Esse componente básico inclui blocos de código para importações de módulo/pacote, processamento de credenciais de autenticação de usuário e criação de endpoints de serviço de API. As principais partes do código a serem analisadas:
- A importação da função
print()
torna esta amostra compatível com o Python 2-3, e as importações de biblioteca do Google trazem todas as ferramentas necessárias para a comunicação com as APIs do Google. - A variável
SCOPES
representa as permissões a serem solicitadas pelo usuário. Por enquanto, há apenas uma permissão: ler dados do Google Drive. - O restante do código de processamento de credenciais é lido nos tokens OAuth2 em cache, possivelmente atualizando para um novo token de acesso com o token de atualização, se o token de acesso original tiver expirado.
- Se nenhum token tiver sido criado ou se um token de acesso válido for recuperado por outro motivo, o usuário precisará seguir o fluxo de OAuth de três pernas (3LO): criar a caixa de diálogo com permissões solicitadas e solicitar que o usuário a aceite. Depois disso, o aplicativo continua. Do contrário,
tools.run_flow()
gera uma exceção e a execução é interrompida. - Depois que o usuário concede a permissão, um cliente HTTP é criado para se comunicar com o servidor, e todas as solicitações são assinadas com as credenciais do usuário para fins de segurança. Em seguida, um endpoint do serviço para a API Google Drive (versão 3) é criado com esse cliente HTTP e atribuído a
DRIVE
.
Como executar o aplicativo
Na primeira vez que você executar o script, ele não terá autorização para acessar os arquivos do usuário no Google Drive. O resultado será assim com a execução pausada:
$ 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
Se você estiver executando no Cloud Shell, pule para a seção "Do Cloud Shell" e role de volta para analisar as telas relevantes em "Do ambiente de desenvolvimento local" quando apropriado.
Do ambiente de desenvolvimento local
O script de linha de comando é pausado quando uma janela do navegador é aberta. Talvez você veja uma página de aviso com aparência assustador:
Essa é uma preocupação legítima, porque você está tentando executar um app que acessa os dados do usuário. Como este é apenas um app de demonstração, e você é o desenvolvedor, esperamos que tenha confiança o suficiente para prosseguir. Para entender melhor, coloque-se no lugar do usuário: é necessário permitir que o código de outra pessoa acesse seus dados. Se você pretende publicar um app como esse, passe pelo processo de verificação para que os usuários não vejam a tela.
Após clicar no link "ir para acessar o aplicativo sem segurança", você verá uma caixa de diálogo de permissões do OAuth2, que tem uma aparência semelhante à mostrada a seguir. Estamos sempre melhorando a interface do usuário, portanto, não se preocupe se ela não corresponder exatamente:
A caixa de diálogo do fluxo do OAuth2 reflete as permissões que o desenvolvedor está solicitando (por meio da variável SCOPES
). Nesse caso, ele pode visualizar e fazer o download a partir do Google Drive do usuário. No código do aplicativo, esses escopos de permissão aparecem como URIs, mas são traduzidos para o idioma especificado pela localidade do usuário. Aqui o usuário precisa conceder autorização explícita para as permissões solicitadas. Caso contrário, uma exceção é lançada, para que o script não prossiga mais.
Você pode até abrir mais uma caixa de diálogo para confirmar sua confirmação:
OBSERVAÇÃO: alguns usam vários navegadores da Web conectados a contas diferentes. Assim, essa solicitação de autorização pode acessar a guia/janela incorreta do navegador, e talvez seja necessário recortar o link desta solicitação em um navegador que esteja conectado com a conta correta.
No Cloud Shell
No Cloud Shell, nenhuma janela do navegador é exibida, deixando você travado. Na parte inferior, você recebeu esta mensagem de diagnóstico:
If your browser is on a different machine then exit and re-run this application with the command-line parameter --noauth_local_webserver
Você terá que pressionar ^C (Ctrl-C ou outro pressionamento de tecla para interromper a execução do script) e executá-lo no seu shell com a sinalização extra. Ao executá-lo dessa forma, você receberá a seguinte resposta:
$ 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:
Ignorar o aviso porque sabemos que o storage.json
ainda não foi criado e que, de acordo com as instruções em outra guia do navegador com esse URL, você terá uma experiência quase idêntica ao descrito acima em ambientes de desenvolvimento local. (Veja as capturas de tela. acima). Ao final, você verá uma tela final com o código de verificação para inserir no Cloud Shell:
Copie e cole este código na janela do terminal.
Resumo
Além de "Authentication successful
", não há resposta adicional. Lembre-se de que isso é apenas a configuração, você ainda não fez nada. O que você concluiu fez com sucesso sua jornada para algo mais probabilidade de ser executado corretamente na primeira vez. A melhor parte é que você solicitou autorização apenas uma vez. Todas as execuções sucessivas pulam porque suas permissões foram armazenadas em cache. Agora, vamos fazer com que o código faça um trabalho real, resultando em uma saída real.
Solução de problemas
Se você receber um erro em vez da resposta, é possível que isso tenha ocorrido por causa de uma ou mais causas:
Na etapa anterior, recomendamos criar o código como analyze_gsimg.py
e editá-lo. Também é possível simplesmente cortar e colar tudo diretamente no iPython ou shell padrão do Python. No entanto, é mais trabalhoso, porque continuaremos criando o aplicativo por partes.
Suponha que seu aplicativo tenha sido autorizado e que o endpoint do serviço da API tenha sido criado. Em seu código, ele é representado pela variável DRIVE
. Agora vamos encontrar um arquivo de imagem no seu Google Drive e
defina-o como uma variável chamada NAME
. Insira o comando a seguir mais uma função drive_get_img()
logo abaixo do código da Etapa 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
A coleção files()
do Drive tem um método list()
que realiza uma consulta (o parâmetro q
) do arquivo especificado. O parâmetro fields
é usado para especificar em quais valores de retorno você tem interesse. Por que incomodar tudo e diminuir os itens se você não se importar com os outros valores? Se você não conhece bem as máscaras de campo para filtrar valores de retorno da API, confira esta postagem do blog e este vídeo. Caso contrário, execute a consulta e pegue o atributo files
retornado, padronizando uma matriz de lista vazia se não houver correspondências.
Se não houver resultados, o restante da função será ignorado e None
será retornado (implicita). Caso contrário, pegue a primeira resposta correspondente (rsp[0]
), retorne o nome do arquivo, o MIMEtype dele, o carimbo de data/hora da última modificação e, por fim, seu payload binário, recuperado pela função get_media()
(por meio do código do arquivo), também na coleção files()
. (Os nomes dos métodos podem ser ligeiramente diferentes com outras bibliotecas de cliente da linguagem.)
A parte final é o corpo "principal" que direciona todo o aplicativo:
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)
Ao somar uma imagem chamada section-work-card-img_2x.jpg
no Google Drive e definir como FILE
, após a execução do script, você verá um resultado confirmando a leitura do arquivo no Google Drive (mas não salvo no seu computador):
$ python3 analyze_gsimg.py Downloaded 'section-work-card-img_2x.jpg' (image/jpeg, 2020-02-27T09:27:22.095Z, size: 27781)
Solução de problemas
Se você não receber a resposta bem-sucedida, como no exemplo acima, a causa pode ser uma ou mais causas:
Resumo
Nesta seção, você aprendeu como (em duas chamadas de API separadas) se conecta à consulta da API Drive para um arquivo específico e faz o download dele. O caso de uso comercial: arquive seus dados do Drive e analise-os, como ferramentas do GCP. O código do seu aplicativo nesse estágio deve corresponder ao que está no repositório em step1-drive/analyze_gsimg.py
.
Leia mais sobre como fazer o download de arquivos no Google Drive aqui ou confira esta postagem de blog e vídeo. Esta parte do codelab é praticamente idêntica a todo o codelab de APIs do G Suite. Em vez de fazer o download de um arquivo, ele mostra os cem primeiros arquivos/pastas no Google Drive do usuário e usa um escopo mais restritivo.
A próxima etapa é adicionar suporte ao Google Cloud Storage. Para isso, é necessário importar outro pacote do Python, io
. Certifique-se de que a seção superior das importações agora esteja assim:
from __future__ import print_function
import io
Além do nome de arquivo do Drive, precisamos de informações sobre onde armazenar esse arquivo no Cloud Storage, especificamente o nome do "bucket" em que você pretende colocá-lo e os prefixos "pasta pai". Mais sobre isso em um momento:
FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = '' # YOUR IMG FILE PREFIX
Uma palavra em buckets... O Cloud Storage fornece armazenamento de blobs morfos. Ao fazer o upload de arquivos nesse arquivo, ele não entende o conceito de tipos de arquivo, as extensões etc., a maneira como o Google Drive faz isso. Eles são apenas "blobs" no Cloud Storage. Além disso, não há um conceito de pastas ou subdiretórios no Cloud Storage.
Sim, há barras (/
) nos nomes de arquivos para representar a abstração de várias subpastas. Porém, no fim do dia, todos os blobs entram em um bucket e "/
" são apenas caracteres nos nomes de arquivos. Confira a página de convenções de nomenclatura de intervalos e objetos para mais informações.
A etapa 1 acima solicitou o escopo somente leitura do Drive. Na época, isso é tudo o que você precisa. Agora é necessária a permissão para fazer upload (leitura/gravação) no Cloud Storage. Altere SCOPES
de uma única variável de string para uma matriz (tuple de Python [ou lista]) de escopos de permissão para que fique assim:
SCOPES = (
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/devstorage.full_control',
)
Agora, crie um endpoint do serviço para o Cloud Storage logo abaixo do que aparece no Drive. Observe que alteramos levemente a chamada para reutilizar o mesmo objeto cliente HTTP, já que não há necessidade de criar um novo quando ele puder ser um recurso compartilhado.
# create API service endpoints
HTTP = creds.authorize(Http())
DRIVE = discovery.build('drive', 'v3', http=HTTP)
GCS = discovery.build('storage', 'v1', http=HTTP)
Agora, adicione esta função (após drive_get_img()
) que seja carregada no Cloud Storage:
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()
A chamada objects.().insert()
requer o nome do bucket, os metadados do arquivo e o próprio blob binário. Para filtrar os valores de retorno, a variável fields
solicita apenas os nomes de bucket e de objeto retornados da API. Para saber mais sobre essas máscaras de campo em solicitações de leitura da API, confira esta postagem e o vídeo.
Agora integre o uso de gcs_blob_upload()
no aplicativo principal:
# 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)
A variável gcsname
mescla os nomes de "subdiretórios pai" anexados com o próprio nome de arquivo e, quando prefixado com o nome do bucket, concede a impressão que você está arquivando o arquivo em "/bucket/parent.../filename
". Ajuste esse bloco logo depois da primeira função print()
logo acima da cláusula else
para que todo o "principal" fique assim:
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)
Digamos que nós especifiquemos um bucket chamado "vision-demo
" com "analyzed_imgs
" como um "diretório pai". Depois de definir essas variáveis e executar o script novamente, section-work-card-img_2x.jpg
será transferido do Drive para o Cloud Storage, certo? NÃO!
$ 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">
Observe que, durante o download do Google Drive, o upload para o Cloud Storage falhou. Por quê?
Isso acontece quando autorizamos esse aplicativo originalmente para a Etapa 1, autorizamos apenas o acesso somente leitura ao Google Drive. Adicionamos o escopo de leitura e gravação ao Cloud Storage, mas o usuário não precisa autorizar esse acesso. Para que isso funcione, precisamos remover o arquivo storage.json
que não tem este escopo e ser executado novamente.
Após autorizar novamente e confirmar a operação dentro de storage.json
, você verá a seguinte resposta:
$ 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'
Resumo
Esse é um resultado importante que mostra, em poucas linhas de código, como transferir arquivos entre os dois sistemas de armazenamento com base na nuvem. O caso de uso empresarial aqui é fazer backup de um recurso possivelmente restrito para um armazenamento mais antigo, conforme mencionado anteriormente. O Cloud Storage oferece diferentes classes de armazenamento dependendo se você acessa seus dados regularmente, mensais, trimestrais ou anuais.
É claro que os desenvolvedores perguntam periodicamente por que o Google Drive e o Cloud Storage existem. Afinal, eles não são armazenamentos de arquivos na nuvem? Por isso, criamos este vídeo. Seu código nesse estágio deve corresponder ao que está no repositório em step2-gcs/analyze_gsimg.py
.
Sabemos que agora é possível mover dados entre o GCP e o G Suite, mas ainda não fizemos nenhuma análise. Portanto, é hora de enviar a imagem ao Cloud Vision para anotações de rótulos ou a detecção de objetos. Para isso, precisamos codificar os dados em Base64, o que significa outro módulo Python, base64
. A seção de importação superior deve ter esta aparência:
from __future__ import print_function
import base64
import io
Por padrão, a API Vision retorna todos os rótulos encontrados. Para manter a consistência, vamos solicitar os cinco principais itens (ajustáveis pelo usuário, claro). Usaremos uma variável constante TOP
para isso. adicione-o a todas as outras constantes:
FILE = 'YOUR_IMG_ON_DRIVE'
BUCKET = 'YOUR_BUCKET_NAME'
PARENT = '' # YOUR IMG FILE PREFIX
TOP = 5 # TOP # of VISION LABELS TO SAVE
Como nos passos anteriores, precisamos de outro escopo de permissão, desta vez para a API Vision. Atualize SCOPES
com a string dela:
SCOPES = (
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/devstorage.full_control',
'https://www.googleapis.com/auth/cloud-vision',
)
Agora, crie um endpoint de serviço para o Cloud Vision para que ele se alinhe aos demais:
# 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)
Agora adicione esta função que envia o payload da imagem para o 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'])
A chamada images().annotate()
requer os dados e os recursos da API desejados. O limite de cinco principais rótulos também faz parte do payload (mas totalmente opcional). Se a chamada for bem-sucedida, o payload retornará os cinco principais rótulos de objetos, além da pontuação de confiança de um objeto na imagem. Se não houver resposta, atribua um dicionário Python vazio para que a seguinte instrução if
não falhe. Essa função simplesmente agrupa esses dados em uma string CSV para uso eventual no nosso relatório.
As cinco linhas que chamam vision_label_img()
precisam ser colocadas logo após o upload bem-sucedido no Cloud Storage:
# 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)
Com essa adição, o driver principal deve ficar assim:
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)
A exclusão de storage.json
para atualizar os escopos e executar novamente o aplicativo atualizado resultará em uma saída semelhante à seguinte, observando a adição do 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
Resumo
Nem todo mundo tem o conhecimento sobre machine learning para criar e treinar os próprios modelos de ML para analisar dados. A equipe do Google Cloud disponibilizou alguns dos modelos pré-treinados do Google para uso geral e os colocou por trás de APIs. Isso ajudou a democratizar a IA e o ML para todos.
Se você é um desenvolvedor e pode chamar uma API, use o machine learning. O Cloud Vision é apenas um dos serviços de API que você pode usar para analisar os dados. Saiba mais sobre outras pessoas. Agora, seu código precisa corresponder ao conteúdo que está no repositório em step3-vision/analyze_gsimg.py
.
Agora você conseguiu arquivar os dados corporativos e analisá-los, mas o que está faltando é um resumo desse trabalho. Vamos organizar todos os resultados em um único relatório que você pode enviar para seu chefe. O que é mais apresentado para gerenciamento do que uma planilha?
Nenhuma importação adicional é necessária para a API Google Sheets. Além disso, a única nova informação necessária é o ID de arquivo de uma planilha existente já formatada e aguardando uma nova linha de dados, portanto, a constante SHEET
. Recomendamos que você crie uma nova planilha semelhante a esta:
O URL dessa planilha terá esta aparência: https://docs.google.com/spreadsheets/d/
FILE_ID
/edit
. Pegue esse recurso FILE_ID
e atribua-o como SHEET
.
Também encontramos uma pequena função chamada k_ize()
, que converte bytes em kilobytes, definindo-a como um lambda
Python, já que é uma simples linha. Ambas integradas com as outras constantes são assim:
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
Como nos passos anteriores, precisamos de outro escopo de permissão, desta vez, leitura e gravação para a Sheets API. Agora, SCOPES
são todos os quatro itens necessários:
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',
)
Agora, crie um endpoint do serviço no Planilhas Google perto dos outros, para que ele fique assim:
# 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)
A funcionalidade do sheet_append_row()
é simples: escolha uma linha de dados e o código de uma planilha para depois adicionar essa linha a essa planilha:
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')
A chamada de spreadsheets().values().append()
exige o ID de arquivo do Planilhas, um intervalo de células, a forma como os dados devem ser inseridos e os próprios dados. O ID do arquivo é simples, o intervalo de células é fornecido na notação A1. Um intervalo de "Sheet1
" significa toda a Planilha Google. Isso sinaliza à API para anexar a linha após todos os dados da planilha. Há duas opções de como os dados devem ser adicionados à planilha, "RAW
" (insira os dados completos) ou "USER_ENTERED
" (grave os dados como se um usuário o inseriu no teclado com o aplicativo Planilhas Google, preservando os recursos de formatação de célula).
Se a chamada for bem-sucedida, o valor de retorno não tem nada útil. Portanto, optamos por receber o número de células atualizadas pela solicitação da API. Veja abaixo o código que chama essa função:
# 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')
O arquivo do Planilhas Google tem colunas que representam dados como qualquer "subdiretório" pai, o local do arquivo arquivado no Cloud Storage (bucket + nome do arquivo), o MIMEtype do arquivo, o tamanho do arquivo (originalmente em bytes, mas é convertido em kilobytes com k_ize()
) e a string de rótulos do Cloud Vision. Observe também que o local arquivado é um hiperlink. Seu gerente pode clicar para confirmar que o backup foi feito com segurança.
Ao adicionar o bloco de código acima logo depois de exibir os resultados do Cloud Vision, a parte principal que conduz o app estará concluída, embora estruturalmente um pouco complexo:
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)
Excluir storage.json
pela última vez e executar novamente o aplicativo atualizado resultará em saída semelhante à seguinte, observando a adição do 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
Embora seja útil, a linha extra da resposta é visualizada analisando o arquivo do Planilhas Google com a última linha (linha 7 no exemplo abaixo) adicionada ao conjunto de dados existente.
Resumo
Nas três primeiras etapas deste tutorial, você se conectou às APIs do G Suite e do GCP para mover dados e analisá-los, representando 80% de todo o trabalho. Porém, no final do dia, nada disso significa que você não pode apresentar o gerenciamento de tudo que fez. Para visualizar melhor os resultados, resumir todos os resultados em um relatório gerado fala os volumes.
Para aumentar ainda mais a utilidade da análise, além de escrever os resultados em uma planilha, uma melhoria possível seria indexar os cinco principais rótulos para cada imagem, de modo que um banco de dados interno fosse criado, permitindo que os funcionários autorizados busquem imagens por pesquisa mas isso é um exercício para os leitores.
No momento, nossos resultados estão em uma planilha e acessíveis ao gerenciamento. O código do seu aplicativo nesse estágio deve corresponder ao que está no repositório em step4-sheets/analyze_gsimg.py
. A etapa final é limpar o código e transformá-lo em um script utilizável.
(opcional) O aplicativo funciona muito bem. Podemos melhorar isso? Sim, principalmente o aplicativo principal, que parece uma bagunça. Vamos colocar isso em uma função própria e deixá-la permitindo a entrada do usuário em vez de constantes fixas. Faremos isso com o módulo argparse
. Além disso, vamos iniciar uma guia do navegador da Web para exibir a planilha depois de gravar a linha de dados nela. Isso é possível com o módulo webbrowser
. Divida essas importações com as outras para que as importações principais fiquem assim:
from __future__ import print_function
import argparse
import base64
import io
import webbrowser
Para poder usar esse código em outros aplicativos, precisamos suprimir a saída. Portanto, vamos adicionar uma sinalização DEBUG
para que isso aconteça, adicionando esta linha ao final da seção de constantes próximas ao topo:
DEBUG = False
Agora, sobre o corpo principal. Como estávamos desenvolvendo esta amostra, você deve começar a se sentir "desconfortável" à medida que nosso código adiciona outro nível de aninhamento com cada serviço adicionado. Se você sentiu isso, não está sozinho, como isso aumenta a complexidade do código, conforme descrito nesta postagem do blog de testes do Google.
Seguindo essa prática recomendada, vamos reorganizar a parte principal do app em uma função e return
em cada "ponto de interrupção" em vez de aninhar. Isso retorna None
se qualquer etapa falhar e True
se for bem-sucedida:
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
É mais simples e claro, deixando-a mais complicada para a cadeia de if-else
, reduzindo a complexidade do código conforme descrito acima. A última peça do quebra-cabeça é criar um driver principal "real", permitindo a personalização do usuário e minimizando a saída, a menos que desejado:
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)
Se todas as etapas forem concluídas, o script iniciará um navegador da Web na planilha especificada na qual a nova linha de dados foi adicionada.
Resumo
Não é necessário excluir storage.json
porque não houve alteração no escopo. Execute novamente o aplicativo atualizado, revelando uma nova janela do navegador aberta na planilha modificada, menos linhas de saída e a emissão de uma opção -h
mostra as opções aos usuários, incluindo -v
para restaurar as linhas agora identificadas de saída. antes:
$ 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
As outras opções permitem que os usuários escolham vários nomes de arquivo do Drive, subdiretórios, nomes de bucket e do Cloud Storage, resultados principais "N" do Cloud Vision e IDs de arquivo do Planilhas Google. Com essas últimas atualizações, a versão final do seu código agora deve corresponder ao que está no repositório em final/analyze_gsimg.py
, bem como aqui, em sua totalidade:
## Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''
analyze_gsimg.py - analyze G Suite 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)
Faremos o possível para manter o conteúdo deste tutorial atualizado, mas haverá momentos em que o repositório terá a versão mais recente do código.
Certamente havia muito aprendizado neste codelab, e você conquistou isso em um dos codelabs mais longos. Como resultado, você resolveu um possível cenário corporativo com cerca de 130 linhas de Python, aproveitando todo o Google Cloud (GCP + G Suite) e transferindo dados entre ele para criar uma solução funcional. Fique à vontade para explorar o repositório de código aberto (em inglês) para ver todas as versões desse app. Veja mais informações abaixo.
Limpeza
- O uso das APIs do GCP não é gratuito enquanto as APIs do G Suite são cobertas pela taxa de assinatura mensal do G Suite ou pela taxa mensal de zero como usuário do Gmail. Portanto, não há uma limpeza/desativação de API necessária para os usuários do G Suite No GCP, acesse o painel do Console do Cloud e verifique se há cobranças estimadas no "cartão" de faturamento.
- Para o Cloud Vision, o número permitido de chamadas de API é permitido por mês. Se você mantiver esses limites, não será necessário encerrar nada nem desativar/excluir seu projeto. Veja mais informações sobre o faturamento e a cota gratuita da API Vision na página de preços da API.
- Alguns usuários do Cloud Storage recebem uma quantidade gratuita de armazenamento por mês. Se as imagens arquivadas usando este codelab não fizerem com que você exceda essa cota, não haverá cobranças. Veja mais informações sobre o faturamento do GCS e a cota gratuita na página de preços. É possível visualizar e excluir facilmente blobs do navegador do Cloud Storage.
- O uso do Google Drive também pode ter uma cota de armazenamento. Se você a ultrapassar (ou estiver próximo a ele), considere a possibilidade de usar a ferramenta criada neste codelab para arquivar essas imagens no Cloud Storage para dar mais espaço no Google Drive. Veja mais informações sobre o armazenamento no Google Drive na página de preços apropriada para usuários do G Suite Basic ou do Gmail/consumidor.
Embora a maioria dos planos do G Suite Business e Enterprise tenham armazenamento ilimitado, suas pastas do Drive podem ficar desorganizadas e/ou pesadas, e o aplicativo que você criou neste tutorial é uma ótima forma de arquivar arquivos irrelevantes e limpar seu Google Drive.
Versões alternativas
final/analyze_gsimg.py
é a "última" versão oficial em que você está trabalhando neste tutorial, mas não é o fim da história. Um problema com a versão final do aplicativo é que ele usa as bibliotecas de autenticação mais antigas que foram suspensas. Escolhemos esse caminho porque, no momento em que este artigo foi escrito, as bibliotecas de autenticação mais recentes não eram compatíveis com diversos elementos importantes: gerenciamento de armazenamento de tokens OAuth e segurança de threads.
Bibliotecas de autenticação atuais (mais recentes)
No entanto, em algum momento, as bibliotecas de autenticação mais antigas não serão mais compatíveis. Por isso, recomendamos que você revise as versões que usam as bibliotecas de autenticação mais recentes (atuais) na pasta alt
do repositório, mesmo se elas não são threadsafe (mas você pode criar sua própria solução). Procure arquivos com *newauth*
no nome.
Bibliotecas de cliente do produto do GCP
O Google Cloud recomenda que todos os desenvolvedores usem as bibliotecas de cliente dos produtos ao utilizar APIs do GCP. As APIs que não são do GCP não têm essas bibliotecas no momento. O uso das bibliotecas de plataforma de nível inferior possibilita um uso consistente da API e têm melhor legibilidade. Assim como na recomendação acima, versões alternativas que usam bibliotecas de cliente de produtos do GCP estão disponíveis na pasta alt
do repositório para análise. Procure arquivos com *-gcp*
no nome.
Autorização da conta de serviço
Ao trabalhar puramente na nuvem, geralmente não há dados de usuários (ou humanos) de usuários. Por isso, as contas de serviço e a autorização da conta de serviço são usadas principalmente com o GCP. No entanto, os documentos do G Suite geralmente são de propriedade de usuários (humanos). Por isso, este tutorial usa autorização de conta de usuário. Isso não significa que não será possível usar as APIs do G Suite com contas de serviço. Desde que essas contas tenham o nível de acesso apropriado, elas certamente poderão ser usadas em aplicativos. Assim como nas versões acima, as versões alternativas que usam a autorização da conta de serviço estão disponíveis na pasta alt
do repositório para análise. Procure arquivos com *-svc*
no nome.
Catálogo de versões alternativas
Veja abaixo todas as versões alternativas de final/analyze_gsimg.py
, cada uma com uma ou mais das propriedades acima. No nome de arquivo de cada versão, procure...
- "
oldauth
" para versões que usam as bibliotecas de autenticação mais antigas (além definal/analyze_gsimg.py
) - "
newauth
" para aqueles que usam as bibliotecas de autenticação atuais/mais recentes - "
gcp
" para aqueles que usam bibliotecas de cliente de produto do GCP, ou seja, google-cloud-storage etc. - ".
svc
" para aqueles que usam uma conta de serviço ("conta svc") em vez de uma conta de usuário
Aqui estão todas as versões:
Filename | Descrição |
| A amostra principal usa as bibliotecas de autenticação mais antigas |
| Igual a |
| Igual a |
| Igual a |
| Igual a |
| Igual a |
| Igual a |
| Igual a |
Com o final/analyze_gsimg.py
original, você tem todas as combinações possíveis da solução final, independentemente do seu ambiente de desenvolvimento da API do Google, e pode escolher a mais adequada às suas necessidades. Veja também alt/README.md
para uma explicação semelhante.
Estudo adicional
Veja abaixo algumas ideias de como esse exercício pode ser iniciado. O problema definido para a solução atual pode ser expandido, permitindo que você faça estas melhorias:
- (várias imagens em pastas) Em vez de processar uma imagem, digamos que você tenha ela nas pastas do Google Drive.
- (várias imagens em arquivos ZIP) Em vez de uma pasta de imagens, que tal os arquivos ZIP que contêm arquivos de imagem? Se estiver usando Python, considere o módulo
zipfile
. - (analisar rótulos do Vision) Agrupe imagens semelhantes, comece procurando os rótulos mais comuns, os dois mais comuns e assim por diante.
- (criar gráficos) Acompanhamento 3, gerar gráficos com a API Sheets com base na análise e na categorização da API Vision
- (categorizar documentos) Em vez de analisar imagens com a API Cloud Vision, digamos que você tenha arquivos PDF para categorizar com a API Cloud Natural Language. Usando as soluções acima, esses PDFs podem estar em pastas do Drive ou em arquivos ZIP no Drive.
- (criar apresentações) Use a API Slides para gerar uma apresentação com o conteúdo do relatório do Planilhas Google. Para ter mais ideias, confira esta postagem do blog e este vídeo sobre como gerar slides de dados de planilha.
- (exportar como PDF) Exportar a planilha e/ou a apresentação de slides como PDF, no entanto, esse não é um recurso das APIs nem das Planilhas Google. Dica: API Google Drive. Crédito extra: combine os PDFs do Planilhas e do Apresentações em um PDF mestre com ferramentas como Ghostscript (Linux ou Windows) ou
Combine PDF Pages.action
(Mac OS X).
Saiba mais
Codelabs
- Introdução às APIs do G Suite (API Google Drive) (Python)
- Como usar o Cloud Vision com Python (Python)
- Criar ferramentas de relatórios personalizadas (API Google Sheets) (JS/Node)
- Fazer upload de objetos para o Google Cloud Storage, sem necessidade de codificação.
Geral
G Suite
- Página inicial da API Google Drive
- Página inicial da API Google Sheets
- Visão geral e documentação do desenvolvedor do G Suite
Google Cloud Platform (GCP)
- Página inicial do Google Cloud Storage
- Página inicial e demonstração ao vivo do Google Cloud Vision
- Documentação da API Cloud Vision
- Documentos de rotulagem de imagens da API Vision
- Python no Google Cloud Platform
- Bibliotecas de cliente de produtos do GCP
- Documentação do GCP
Licença
Este conteúdo está sob a licença Atribuição 2.0 Genérica da Creative Commons.