O robô entusiasta de poesia
Tags: python
Published: 2021-01-05 16:23:13

Um fantasma de mil anos habita o Twitter: [o fantasma do poeta japonês Ki no Tsurayuki](https://twitter.com/kokinshu_bot) (紀貫之, ca.872 — ca. 945). Na verdade, não é fantasma nenhum; trata-se de um bot que a cada duas horas posta poemas aleatórios extraídos da coleção Kokin Wakashū.

A ideia é tão legal que eu resolvi programar meu próprio robô entusiasta de poesia, só que em vez de poemas japoneses, meu Charles Robaudelaire seria um robô de poesia francesa, extraída do acervo [poetica.fr](https://www.poetica.fr/).

A versatilidade — diversidade de tarefas às quais a ferramenta pode servir — é um dos maiores atrativos da linguagem __Python__. A riqueza de bibliotecas open source é igualmente grande no Javascript, é verdade. Mas esta linguagem sempre me pareceu ligada muito fortemente à manipulação do DOM do navegador. Ser general-purpose desde o princípio, e não por expansão, me fez confiar muito mais em Python.

##Construindo o scraper
O Robaudelaire precisava ter sua biblioteca, isto é, um banco de dados de poemas que serviriam de conteúdo para cada tweet. Para criá-la, havia duas opções: buscar uma API que providenciasse os dados dos poemas em formato JSON, ou extrair essa informação de sites que a contém, mas não providenciam uma API, por meio de um scraper.

Scrapers e crawlers não são exatamente a mesma coisa, mas são parecidos — como um prego e um parafuso. Deixo aqui uma definição rasa, só para esclarecer e seguir com a história: 

Scrapers são scripts que visitam uma página da web e traduzem (parse) o código de forma a deixá-lo interpretável e navegável pela linguagem de programação. Desta forma, é possível buscar, listar, filtrar e manipular os elementos do html como quaisquer objetos. Crawlers são scripts que navegam as várias páginas de um site repetidamente, como um aventureiro que explora as áreas novas de um mapa. Como as duas atividades são fundamentais para o nosso programa, chamarei simplesmente de scraper.

O scraper foi desenvolvido utilizando a biblioteca __Beautiful Soup 4__, que faz exatamente o que está escrito na definição acima, portanto não é necessário repetir. Consiste em três funções: get_poem_list() acessa a página de um autor e retorna a lista de todos os seus poemas; get_poem_detail() acessa um poema individual e retorna o texto completo e o nome do autor devidamente formatado para o tweet (get_poem_detail() não será usada para construir a biblioteca); scrape_site() acessa o acervo e, utilizando get_poem_list() em cada um dos autores, retorna a lista total de poemas, com nome do autor, título e link.

{% gist https://gist.github.com/escribapetrus/66cc5a2d4c6158fea29186a15de2bb52 %}

A função get_poem_list() opera como um procedimento interno da função principal scrape_site(), que além fazer o scrape da página inicial para montar a lista “library” de autores e urls, funciona como um crawler que navega a página de cada autor, por meio da função map(…).
Rodando o scraper, preenchemos a biblioteca com 3.000 poemas de 150 autores. Restava apenas salvá-la no banco de dados como o arquivo .csv a ser consultado para criar os tweets. Em caso de dúvida sobre como salvar o objeto em .csv, segue o snippet.

{% gist https://gist.github.com/escribapetrus/0d8cff852b8669072826657cd96ea407 %}

##Construindo o objeto Poem
Tendo a biblioteca em mãos, era hora de fazer os tweets — primeiro em texto, antes de postar no Twitter. Após algumas tentativas, decidi que a melhor abordagem seria criar instâncias/objetos de uma classe Poem, que teria em si não apenas os atributos obtidos da biblioteca, mas também dois métodos importantes: get_poem() e get_tweet().

Você deve ter percebido que a biblioteca que salvamos no banco de dados não contém os texto dos poemas. É claro que o Robaudelaire precisa desses textos para citar nos tweets. Para isso, cada instância do objeto Poem possui o método get_poem(), que chama a função get_poem_details() dos scrapers e retorna o poema completo.

{% gist https://gist.github.com/escribapetrus/a8f7ecc68c9f1bbc5cb63c58ca6a963c %}

##Postando no twitter por meio da API
O Robaudelaire já tinha uma biblioteca cheia e o método para criar seus tweets. O último passo era postá-los no Twitter.
Para isso, era necessário criar uma conta na [plataforma para developers do Twitter](https://developer.twitter.com/en), vinculá-la à conta, registrar o app (i.e., o robô) e adquirir as quatro chaves para autenticação, api key, api secret key, access token, access token secret. (Esta parte é um desafio que vou deixar você enfrentar sozinho, porque a explicação seria longa, fácil e desinteressante).
Com as quatro chaves, é possível autenticar na API e postar o tweet com o método api.update_status(). Utilizei a biblioteca __pandas__ para ler o arquivo .csv e extrair um poema aleatório com o método data.sample().

{% gist https://gist.github.com/escribapetrus/cfad21bca6dee070215e6b58495af0a0 %}

{% gist https://gist.github.com/escribapetrus/7e2605deba3e82cd469f2ad6bd2df3e6 %}

Com o arquivo main.py rodando, o loop infinito while True: posta um tweet a cada intervalo configurado no método time.sleep(). Na hora de configurar o intervalo de tweets, lembre-se que a API impõe um limite de acessos para evitar spam.

##Conclusão
O Robaudelaire foi um programa fácil de se criar, mas um bom exercício para aprender a desenvolver scrapers e crawlers, ler e escrever arquivos, desenhar classes, interagir com APIs.
Por enquanto, ele só posta citações com links para o site que generosamente disponibiliza o acervo de poemas (espero que não se incomodem por eu pegá-lo emprestado). Mas há outras funcionalidades que podem ser desenvolvidas no futuro; por exemplo: retweetar conteúdos relevantes, responder automaticamente a mensagens e citações, entre outras.

Mas essa é outra história. Agradeço ao site poetica.fr por fornecer gratuitamente na internet um acervo de poesia tão impressionante. Se você gostou deste projeto e quer tirar dúvidas ou conversar, me mande um alô pelo [Instagram](https://www.instagram.com/escribapetrus/) ou me adicione no [Github](https://github.com/escribapetrus).