Criando uma API de filmes em cartaz usando Python e Heroku

0

     Hoje vou sair ligeiramente da minha zona de conforto e me arriscar no mundo Python. Não é uma linguagem que possuo intimidade mas por ter tido contato com algumas bibliotecas relacionadas a Inteligência Artificial, fiquei interessado em aprender mais sobre a mesma. Neste post vou mostrar como disponibilizar uma API REST criada com Python e o microframework web Flask no serviço Heroku, e o melhor: de forma 100% gratuita!

       Essa história recente com o python começou com uma necessidade pessoal. Eu queria criar uma página web que exibisse diariamente os filmes que estão em cartaz nos cinemas brasileiros, a ideia em si é bem simples e jurava que ia encontrar algum site ou blog que liberasse um Feed RSS, serviço REST ou web service de forma atualizada e de fácil consulta, mas me decepcionei com o que encontrei que muitas vezes trazia apenas o que existia em outros países, ou era algum projeto no Github que estava há alguns anos sem atualização ou simplesmente não funcionava.

   Partindo deste ponto, cansei de procurar e resolvi criar um serviço REST que retornasse um JSON com a lista de filmes em cartaz nos cinemas brasileiros. Para isso precisava pegar estas informações de algum site/blog que estivesse sempre atualizado. Então a missão é:

  1. Encontrar uma lista de filmes pública, de fácil acesso e atualizada.
  2. Preparar o ambiente com VsCode, Python e /módulos necessários.
  3. Código web do Flask
  4. Fazer o parse do HTML da fonte usando Python para separar apenas as informações pertinentes aos filmes em cartaz.
  5. Transformar essa saída em um JSON
  6. Disponibilizar o código-fonte no Github
  7. Publicar no Heroku para acesso público

     Pode parecer muita coisa mas é simples, o esquema da imagem abaixo mostra o que desejo conseguir.

esquemaHeroku

1. ENCONTRAR A LISTA DE FILMES ONLINE

     Existem vários no país mas acabei escolhendo o AdoroCinema por já conhecer e gostar bastante, a lista neste link, por exemplo, traz os filmes em cartaz. No momento que crie esse post, Guardiões da Galáxia 2 no topo. I’m groot!

2. PREPARAR O AMBIENTE

     Já temos a fonte dos dados precisamos carregar essa página no nosso app Python, percorrer o HTML e coletar os dados que queremos. A lib que vai fazer esse trabalho pesado para nós é a BeautifulSoup, ela vai além de apenas ler um HTML, ela permite fazer parse de outros formatos também e gerar uma árvore dos dados, serve ao nosso propósito.

     Fiz tudo usando o Visual Studio Code versão 1.11 mas fique a vontade para usar outro editor de código. A versão do Python que tenho instalada é a 3.5.3 e as versões das bibliotecas usadas foram as seguintes:

beautifulsoup4==4.5.3
Flask==0.12

     Para instalar as duas libs acima, usei o gerenciador de módulos pip, que faz o mesmo trabalho que o npm no Node.js. Basta executar os comando abaixo na linha de comando:

pip install beautifulsoup4
pip install Flask

     E para quem usar o VSCode recomendo a extensão donjayamanne.python para auxiliar no desenvolvimento, mas é um extra. Para instalar execute o comando abaixo no VSCode ou use o gerenciador de extensões:

ext install python

     Pode ser que no momento que estejam lendo este post já existam versões mais recentes do Python, do Visual Studio Code e das libs mostradas. Caso queira testar se o python está funcionando, crie um arquivo “teste.py” com o código abaixo:

#1
from bs4 import BeautifulSoup
#2
import urllib.request
#3
soup = BeautifulSoup('Extremely bold',"html.parser")
#4
tag = soup.b
#5
print(tag['class'])

Agora execute usando o comando “python teste.py” no cmd ou pelo VSCode. A saída irá mostrar: [‘boldest’]. O que foi feito aqui foi:

#1 – Importar a biblioteca beautifulSoup
#2 – Importar a biblioteca urllib( chamada http)
#3 – Passar manualmente um HTML para ser feito o parse para o método BeautifulSoup
#4 – Carregar a tag do DOM como um objeto para a variável python “tag
#5 – Exibir o atributo class do objeto capturado. Neste caso “boldest

3. CÓDIGO WEB DO FLASK

     O exemplo do tópico 2 mostra o funcionamento geral do que iremos fazer a partir daqui, mas existem alguns detalhes adicionais que vou explicar no código a seguir.

#PARTE1
from flask import Flask, jsonify, request
from bs4 import BeautifulSoup
from urllib.request import urlopen, Request
import os

#PARTE2
app = Flask(__name__)

#PARTE3
@app.route('/api/v1/filmes', methods=['GET'])
def filmes():
  #MEU CÓDIGO AQUI
  return"Tudo pronto!"

#PARTE4
if __name__ == '__main__':
  port = int(os.environ.get('PORT', 5000))
  app.run(host='127.0.0.1', port=port)

#PARTE1:  carregamos todos os módulos necessários, existem três carinhas aqui que ainda não tinha comentado: jsonify e os. Eles já vem com a instalação padrão do Python e usaremos para transformar um array para o formato JSON e para acessar as portas do nosso ambiente no Heroku respectivamente.

#PARTE2: esse trecho simplesmente cria um app web usando Flask. A variável app recebe o objeto Flask. Usaremo a variável app mais abaixo para criar a rota e iniciar nosso serviço.

#PARTE3: com app.route dizemos qual o caminho de acesso e qual verbo HTTP será utilizado. Neste caso estamos apenas retornando uma lista de elementos então será criado um GET no caminho “/api/v1/filmes“, que fica ao seu critério escolher. Em sequencia definimos uma função que será chamada ao acessar esse caminho, dentro dela terá apenas um return com a string “Tudo pronto!” por enquanto, no próximo tópico veremos o código que ficará neste local.

#PARTE4: agora definimos alguns parâmetros de ambiente, a variável port recebe uma porta disponível ou a 5000 por padrão, a lib os cuida disso. Precisei deixar dessa forma para funcionar no Heroku. O mesmo vale para o host, quando rodo local uso 127.0.0.1(localhost) mas quando publico no Heroku preciso colocar 0.0.0.0 para funcionar, nos bastidores ele irá setar o host correto.

     Simples né? Isto foi apenas para testar e montar nosso ambiente base, vamos agora carregar o HTML do site AdoroCinema e capturar informações de verdade.

4. FAZER O PARSE DO HTML 

Vamos agora carregar os dados dos filmes direto da página online do AdoroCinema, a estrutura HTML do link http://www.adorocinema.com/filmes/numero-cinemas/ é esta:

filme.png

O site traz vários blocos como este mostrado na imagem acima, um embaixo do outro. Se conseguirmos trazer as informações de um bloco trazer dos outros é trivial. Os dados que queremos são:

  • Nome do filme
  • Data de Lançamento
  • Sinopse
  • Imagem do poster

      Veja que cada um dos elementos eu circulei em vermelho e coloquei ao lado o nome da variável referente a eles que será utilizado no código em Python. Para capturar os dados precisamos da tag do DOM no HTML para passar como parâmetro para a biblioteca BeautifulSoup, a ferramenta perfeita para isso é o Inspetor de Elementos do navegador, todos os browsers atuais possuem um, seja Chrome, Firefox ou MS Edge. Veja um exemplo:

tagNome.png

A linhas que irão capturar os valores de cada tag e armazenar em um vetor são:

#PARTE1
html_doc = urlopen("http://www.adorocinema.com/filmes/numero-cinemas/").read()
soup = BeautifulSoup(html_doc, "html.parser")

#PARTE2
data = []
for dataBox in soup.find_all("div",class_="data_box"):
  nomeObj = dataBox.find("h2", class_="tt_18 d_inline").find("a", class_="no_underline")
  imgObj = dataBox.find(class_="img_side_content")
  sinopseObj = dataBox.find("div", class_="content").find("p")
  dataObj = dataBox.find("ul", class_="list_item_p2v tab_col_first").find("div", class_="oflow_a")

#PARTE3
  data.append({ 'nome': nomeObj.text.strip(),
                'poster' : imgObj.img['src'].strip(),
                'sinopse' : sinopseObj.text.strip(),
                'data' : dataObj.text.strip()})
obs: o código de acesso a cada tag depende do html do site, isto significa que qualquer mudança deve ser feita também no código. Acesse a versão atualizada aqui.
Destrinchando este código em 3 partes, temos:
#PARTE1:  carregamos o HTML do site na variável html_doc e o resultado do parse do BeautifulSoup na variável soup.
#PARTE2: criamos um array vazio chamado data, criamos um laço com for  para percorrer todos os divs que possuam o atributo class igual a data_Box. Ou seja, cada um dos filmes da lista. Para cada dados que desejamos (nome, sinopse, data e imagem) chamamos o método find da variável dataBox criada no for, o parâmetro do find é a tag do DOM onde a informação se encontra. Veja que se tomarmos como exemplo o nome do filme (nomeObj), ele pode ser encontrado no HTML dentro da tag
#PARTE3: por último mas não menos importante, jogamos o objeto montado dentro do array data. Isto será feito várias vezes até que o laço chegue ao fim, ou seja, não tenha mais filmes na lista.

5. GERAR A SAÍDA EM JSON

O pior já passou, agora é só transformar nosso vetor data que possui a lista de objetos(filmes), para o formato JSON. No return colocamos a linha abaixo:

return jsonify({'filmes': data})
   Feito tudo isso execute a aplicação na linha de comando com “python web.py”. Renomei seu arquivo para web.py pois será necessário agora que enviaremos para o Heroku. No último tópico mostrarei como ficou a lista de filmes em JSON.

6. COMPARTILHANDO NO GITHUB

     Antes de colocar no Heroku o projeto foi disponibilizado no Github no endereço abaixo. O motivo é que uma das formas que o Heroku permite fazer o deploy da app é através da integração com o Github o que é bem mais prático.

https://github.com/rodrigorf/filmes-cartaz-json

7. PUBLICANDO A API NO HEROKU

      Antes de colocarmos no Heroku precisamos criar três arquivos de configuração que o servidor precisa, acabei descobrindo isso após me bater bastante ao subir o projeto da primeira vez. Os arquivos na raíz do projeto são Procfilerequirements.txt e runtime.txt.

  • requirements.txt -> informa os módulos que devem ser importados no servidor para que a aplicação funcione, são eles: beautifulsoup4==4.5.3 e Flask==0.12.
  • runtime.txt -> informa a versão do python que queremos rodar: python-3.4.3.
  • Procfile -> arquivo que contém o comando de execução e o nome da app, desta forma -> web: python web.py

      A aplicação esta versionada e já retorna a lista de filmes em formato JSON, isto já deve servir para muitas pessoas mas a ideia era criar algo que outros possam usar sem precisar necessariamente clonar um repositório no Github e compilar uma app em Python. Meu objetivo principal é ter uma URL pública que outros desenvolvedores possam utilizar também seja em Python, C#, Node.js, Java, ou qualquer linguagem. Vamos agora publicar em nossa hospedagem, o http://heroku.com, siga os passos abaixo:

  1. Crie uma conta no site do Heroku.
  2. Faça o login no seu painel em https://dashboard.heroku.com
heroku1_newApp

Tela inicial do painel do Heroku

3. Clique em “Create new app”.
4. Na tela seguinte informe um nome e a região do servidor.
5. Ao avançar seremos levados diretamente a tela de deploy da app. Aqui faremos a conexão com o Github conforme imagem abaixo.

heroku2_Deploy

Escolhemos conectar com Github para pegar o código-fonte da app.

6. Clique em Github e autorize a conexão do Heroku. A aba abaixo é mostrada, procure o projeto em seus repositórios e clique em “Connect”.

heroku3_git.png

Conexão com o repositório do app que criamos

Lembrando que para que for fazer isso deverá primeiro clonar o projeto no Github.

     7. Estando tudo certo e devidamente conectados com o Github já podemos fazer o deploy. O deploy pode ser automático, ou seja, todo push feito no branch dispara uma publicação, ou ser feito de forma manual. No nosso caso será manualmente.

heroku4_deploy

     8. Ao clicar em “Deploy Branch” o processo de carregamento do aplicativo será iniciado e um log do processo é mostrado, o Python é configurado, o PIP bem como os módulos existentes são instalados no servidor para que a aplicação funcione.

heroku5_log

Caso tudo termine sem erros, é apresentada uma mensagem de sucesso e um botão para exibir a aplicação.

heroku6_fim

     Clique em “View” para abrir a URL da sua aplicação, algo do tipo https://minhaapp.herokuapp.com. Verá uma mensagem de “NOT FOUND” isto ocorre pois criamos uma rota específica para nosso GET que retorna os filmes, lembra? o caminho da rota é “/api/v1/filmes”, então fica:

https://minhaapp.herokuapp.com/api/v1/filmes

     Finalmente é apresentado nosso JSON com a lista dos filmes carregadas em tempo real. Olha só que legal:

tela_json

     Caso a exibição no Chrome não esteja do seu gosto, eu instalei uma extensão chamada Json Viewer que permite aplicar alguns temas e formatar melhor o JSON. Bom é isso galera, o post ficou um pouco extenso pois tentei descrever da melhor forma possível cada passo.

     Em breve quero deixar o projeto mais flexível para facilitar o carregamento a partir de outras fontes de filmes, a ideia basicamente é criar um arquivo JSON de configuração onde o usuário informa apenas a URL e os XPATH de onde cada dado será puxado, por exemplo, para o AdoroCinema ficaria assim:

{
 "url":"http://www.adorocinema.com/filmes/numero-cinemas/",
 "pathNome":"//*[@id='col_main']/div[6]/div/div/div[1]/h2/a",
 "pathData":"//*[@id='col_main']/div[6]/div/div/ul/li[1]/div",
 "pathSinopse":"//*[@id='col_main']/div[6]/div/div/p",
 "pathPoster":"//*[@id='col_main']/div[6]/div/a/img"
}

     Infelizmente a BeautifulSoup não dá suporte direto para XPath, talvez consiga fazer isso com a lib lxml, mas o bacana desse modelo é que qualquer um com um navegador moderno pode capturar o xpath a partir do inspetor de elementos e criar um JSON de configuração, permitindo que a própria comunidade crie as mais variadas fontes de filmes sem muito esforço.

     Quem quiser contribuir com o projeto fique a vontade para dar um fork lá e vamos trabalhando juntos. Até a próxima.

Compartilhe.

Sobre o autor

Programador, apaixonado por games, empreendedor. Formado em Ciência da Computação com especialização em desenvolvimento para web, trabalhei com projetos .Net C#, NodeJs e Python nestes 10 anos de experiência no setor privado, em orgão públicos e na minha startup, o EstoqueMestre. http://rodrigoreisf.com.br