O Querido Diário é um projeto de código aberto da Open Knowledge Brasil que utiliza Python e outras tecnologias para libertar informações do Diário Oficial (DO) das administrações públicas no Brasil. A iniciativa mapeia, baixa e converte todas as páginas das publicações para um formato mais acessível, a fim de facilitar a análise de dados.
Neste tutorial, mostraremos algumas orientações gerais para construir um raspador e contribuir com o projeto Querido Diário.
Este é repositório ainda está em fase de elaboração. Abaixo, estão algumas tarefas ainda pendentes. Você pode ajudar melhorando a documentação por meio de pull requests neste repositório. Confira a lista de tarefas pendentes no final do documento.
Se você prefere uma apresentação sobre o projeto em vídeo, confira o workshop Querido Diário: hoje eu tornei um Diário Oficial acessível da Ana Paula Gomes no Coda.Br 2020.
Existem formas de colaborar com o Querido Diário sem precisar programar. Você pode participar de nosso Censo, por exemplo, e ajudar a mapear os Diários Oficiais de todos os municípios brasileiros.
Se você quiser botar a mão na massa e construir seu raspador, pode começar “adotando” uma cidade. Primeiro, encontre uma cidade que ainda não esteja listado no arquivo CITIES.md do repositório.
O endereço do repositório do projeto é: https://github.com/okfn-brasil/querido-diario/
Para acompanhar o tutorial e construir um raspador, é necessário algum conhecimento sobre:
- Uso do terminal
- Python e o pacote Scrapy
- Git e Github
- HTML,CSS, XPath
Se você não se sente confortável com estas tecnologias, sugerimos a leitura dos seguintes tutoriais primeiro.
Faça um fork do repositório oficial do Querido Diário na sua conta no Github.
Em seguida, clone este novo repositório para seu computador.
Se você usa Windows, baixe as Ferramentas de Build do Visual Studio e execute o instalador. Durante a instalação, selecione a opção “Desenvolvimento para desktop com C++” e finalize o processo.
Se você usa Linux ou Mac Os, pode simplesmente executar os seguintes comandos. Eles também estão descritos no README do projeto, na parte de configuração de ambiente.
python3 -m venv .venv
source .venv/bin/activate
pip install -r data_collection/requirements.txt
pre-commit install
Usuários de Windows devem executar os mesmo comandos, apenas trocando o segundo deles por: .venv\Scripts\activate.bat
Todos os raspadores do projeto ficam na pasta data_collection/gazette/spiders/. Navegue por diferentes arquivos e repare no que há de comum e diferente no código de cada um.
Os nomes de todos os arquivos seguem o padrão: uf_nomedacidade.py.
Ou seja, primeiro, temos a sigla da UF, seguido de underline e nome da cidade. Tudo em minúsculas, sem espaços, acentos ou caracteres especiais.
Veja alguns exemplos paradigmáticos de Diários Oficiais:
-
Paginação: um bom exemplo de raspador de DO onde as publicações estão separadas em várias páginas é o script da cidade de Manaus.
-
Busca de datas: outra situação comum é quando você precisa preencher um formulário e fazer uma busca de datas para acessar as publicações. É caso por exemplo do script ba_salvador.py, que raspa as informações da capital baiana.
-
Consulta via APIs: pode ser também que ao analisar as requisições do site, você descubra uma API escondida, com dados dos documentos já organizadas em um arquivo JSON, por exemplo. É o caso do raspador de Natal.
Se você navegou pelos raspadores, talvez tenha reparado que alguns raspadores praticamente não possuem código e quase se repetem entre si. Neste caso, tratam-se de municípios que compartilham o mesmo sistema de publicação. Então, tratamos eles conjuntamente, como associações de municípios, ao invés de repetir o mesmo raspador em cada arquivo.
Mas não se preocupe com isso, por ora. Vamos voltar ao nosso exemplo e ver como construir um raspador completo para apenas uma cidade.
Por padrão, todos os raspadores começam importando alguns pacotes. Vejamos quais são.
import datetime
: pacote para lidar com datas.
from gazette.items import Gazette
: item que será salvo com campos que devem/podem ser preenchidos com os metadados dos diários.
from gazette.spiders.base import BaseGazetteSpider
: é o raspador (spider) base do projeto, que já traz várias funcionalidades úteis.
Cada raspador traz uma classe em Python, que executa determinadas rotinas para cada URL de Diários Oficiais. Todas as classes possuem pelo menos as informações básicas abaixo.
Vejamos um exemplo a partir da cidade Paulínia em São Paulo.
name
= Nome do raspador no mesmo padrão do nome do arquivo, sem a extensão. Exemplo: sp_paulinia
.
TERRITORY_ID
= código da cidade no IBGE. Confira a o arquivo territories.csv
do projeto para descobrir o código da sua cidade. Exemplo: 2905206
.
allowed_domains
= Domínios nos quais o raspador irá atuar. Exemplo: ["paulinia.sp.gov.br"]
start_urls
= URL de início da navegação do raspador. A resposta dessa requisição inicial é encaminhada para a variável response, do método padrão do Scrapy chamado parse
. Veremos mais sobre isso em breve. Exemplo:["http://www.paulinia.sp.gov.br/semanarios/"]
start_date
= Representação de data no formato ano, mês e dia (YYYY, M, D), usando o pacote datetime
. É a data inicial da publicação do Diário Oficial no sistema questão, ou seja, a data da primeira publicação disponível online. Encontre esta data pesquisando e inserindo essa data manualmente nesta variável. Exemplo: datetime.date(2017, 4, 3)
.
Além disso, cada raspador também precisa retornar algumas informações por padrão. Isso acontece usando a expressão yield
.
date
= A data da publicação em questão. Em nosso código de exemplo, definimos este parâmetro como o dia de hoje, apenas para ter uma versão básica operacional do código. Porém, ao construir um raspador real, neste parâmetro você deverá indicar as datas corretas das publicações.
file_urls
= Retorna a URL da publicação do DO (um documento pode ter mais de uma URL, mas é raro).
power
= Aceita os parâmetros executive
ou executive_legislative
. Aqui, definimos se o DO tem informações apenas do poder executivo ou também do legislativo. Para definir isso, é preciso olhar manualmente nas publicações se há informações da Câmara Municipal agregadas no mesmo documento, por exemplo.
is_extra_edition
= Sinalizamos aqui se é uma edição extra do Diário Oficial ou não.
edition_number
= Número da edição do DO em questão.
Vejamos novamente nosso código de exemplo.
O Scrapy começa fazendo uma requisição para a URL definida no parâmetro start_urls
. A resposta dessa requisição vai para o método padrão parse
, que irá armazenar a resposta na variável response
.
A variável response
tem vários atributos, como o text
, que traz o HTML da página em questão como uma string.
Então, você pode uma forma de fazer um famoso "Hello, world!" no projeto Querido Diário seria com um código mais ou menos como este abaixo. Você encontra o código visto acima no arquivo sp_paulinia.py, presente neste repositório. Este código não baixa nenhum DO de fato, mas dá as bases para você entender como os raspadores operam e por onde começar a desenvolver o seu próprio.
Para testar um raspador e começar a desenvolver o seu, siga as seguintes etapas:
- Importe o arquivo para a pasta
data_collection/gazette/spiders/
no repositório criado no seu computador a partir do seu fork do Querido Diário. - Abra o terminal nesta pasta.
- Ative o ambiente virtual, caso não tenha feito antes. Rode
source .venv/bin/activate
ou o comando adequado na pasta onde o ambiente foi criado. - No terminal, rode o raspador com o comando
scrapy crawl nomedoraspador
. Ou seja, no exemplo rodamos:scrapy crawl sp_paulinia
.
Se tudo deu certo, deve aparecer um arquivo de log enorme terminal.
Ele começa com [scrapy.utils.log] INFO: Scrapy 2.4.1 started (bot: gazette)
e traz uma série de informações sobre o ambiente inicialmente. Mas a parte que mais nos interessa começa apenas após a linha [scrapy.core.engine] INFO: Spider opened
e termina na linha [scrapy.core.engine] INFO: Closing spider (finished)
. Vejamos abaixo.
A linha DEBUG: Scraped from <200 http://www.paulinia.sp.gov.br/semanarios/>
nos indica conseguimos acessar o endereço especificado (código 200).
Ao desenvolvedor um raspador, busque principalmente por avisos de WARNING e ERROR. São eles que trarão as informações mais importantes para você entender os problemas que ocorrem.
Depois de encerrado o raspador, temos a linha a seção do MONITORS, que trará um relatório de execução. É normal que apareçam erros, como este abaixo.
======================================================================
FAIL: Comparison Between Executions/Days without gazettes
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/abitporu/querido-diario/data_collection/gazette/monitors.py", line 66, in test_days_without_gazettes
self.assertNotEqual(
**AssertionError: 0 == 0 : No gazettes scraped in the last 5 days.**
2021-08-19 18:44:04 [sp_paulinia] INFO: [Spidermon] 5 monitors in 0.022s
2021-08-19 18:44:04 [sp_paulinia] INFO: [Spidermon] FAILED (failures=1)
2021-08-19 18:44:04 [sp_paulinia] INFO: [Spidermon] -------------------------- FINISHED ACTIONS --------------------------
2021-08-19 18:44:04 [spidermon.contrib.actions.telegram] INFO: *sp_paulinia* finished
- Finish time: *2021-08-19 21:44:04.450166*
- Gazettes scraped: *1*
- 🔥 1 failures 🔥
Basicamente, estamos sendo avisados que nada foi raspado nos últimos dias. Tudo bem, este é apenas um teste inicial para irmos nos familiarizando com o projeto.
Aqui, tudo vai depender da forma como cada site é construído. Mas separamos algumas dicas gerais que podem te ajudar.
Primeiro, identifique um seletor que retorne todas as publicações separadamente. Se as publicações estão separadas em várias abas ou várias páginas, primeiro certifique-se de que todas elas seguem o mesmo padrão. Sendo o caso, então, você pode começar fazendo o raspador para a página mais recente e depois repetir as etapas para as demais, por meio de um loop, por exemplo.
Para testar os seletores e construir o raspador, você pode utilizar algumas destas alternativas:
-
Inspetor Web: disponível nos navegadores, permite a busca por seletores XPath.
-
Scrapy shell: você também pode testar seus seletores usando o Scrapy Shell. Experimente rodar por exemplo
scrapy shell "http://www.paulinia.sp.gov.br/semanarios"
. Neste terminal, você pode rodar códigos comoresponse.xpath("//div[@class='container body-content']//div[@class='row']//a[contains(@href, 'AbreSemanario')]")
e ver os resultados. -
Python debuger: insira a linha
import pdb; pdb.set_trace()
em qualquer parte do código que será executado para inspecionar seu código (contexto, variáveis, etc.) durante a execução.
Se tiver dúvidas sobre algo, abra uma issue neste repositório.
- Gerar uma nova imagem de "print" do código inicial de Paulínia (o código foi alterado)
- Completar a lista de tutoriais introdutórios com materiais relevantes
- Testar e reportar eventuais problemas com a configuração de ambiente no Windows
- Testar e reportar eventuais problemas com a configuração de ambiente no Linux
- Testar e reportar eventuais problemas com a configuração de ambiente no Mac OS
- Documentar o processo de fazer um commit no repositório e problemas comuns
- Fazer uma seção mostrando como enviar o seu raspador depois de feito
- Melhorar a seção "Construindo um raspador de verdade".
- Melhorar dicas para debugar o código.
- Revisar e incorporar conteúdos faltantes (e ainda atuais) citados no artigo do Vanz.
- Revisar e incorporar conteúdos faltantes (e ainda atuais) citados no post e na apresentação no Coda.Br 2020 feito por Ana Paula Gomes.