Todo projeto que deseja raspar dados da web se resume ao seguinte loop:
Fazer uma requisição para uma URL;
Processar a resposta (HTML, XML ou JSON);
Extrair os dados;
Deduzir as próximas URLs a visitar;
Repetir o loop.
A parte mais difícil aqui é processar o HTML. É um processo todo delicado, o
HTML é cheio de detalhes para tolerar tags que não fecham, símbolos largados
nos lugares errados e por aí vai. Por isso é difícil ter um parser
(processador) de HTML nas bibliotecas-padrão de qualquer linguagem, seja
Python, Ruby, Javascript ou PHP. Já XML e JSON são formatos muito mais estritos
e tem parsers nativos em qualquer linguagem.
Vamos ver como pegar os gastos da Copa do Mundo 2014, expostos em
XML.
Alguns browsers vão exibir o XML como uma árvore, facilitando a visualização da
estrutura — meu browser favorito, o
Firefox, faz isso.
Nesse XML você pode ver um elemento maior, o collection, com muitos elementos
copa:empreendimento dentro. Esse prefixo copa: corresponde a um
namespace, um recurso do XML para poder misturar elementos de vocabulários
distintos. É importante prestar atenção nisso para podermos informar o nosso
parser de XML.
Para começar o loop, vamos carregar a URL e popular uma árvore de elementos —
uma abstração do Python para podermos manipular mais facilmente esses dados:
from urllib.request import urlopendata_url = “http://www.portaltransparencia.gov.br/copa2014/api/rest/empreendimento”
with urlopen(data_url) as datafile:
data = ElementTree.parse(datafile)
Repare como carregar uma URL no Python 3 tem uma sintaxe confortável, idêntica
à sintaxe de abrir arquivos. Agora pra seguir o loop, vamos extrair o que nos
interessa: o gasto (valorTotalPrevisto) de cada empreendimento iniciado ou
concluído (cujo andamento não esteja no estado 1, Não iniciado).
for element in data.iterfind(‘.//copa:empreendimento’,
namespaces={‘copa’: data_url[:46]})
if element.find(‘./andamento/id’).text != ‘1’]
Pegar elementos de um ElementTree é fácil usando o método iterfind (retorna
um iterável, pra usar com for) ou findall (retorna uma lista propriamente
dita). Já pegar o conteúdo de um elemento exige apenas chamar o atributo
.text. Fácil, não?
Isso daria certo se os dados fossem consistentes, mas… outro porém! O XML é
estrito — as tags fecham, os símbolos estão no lugar certo, está tudo certo,
mas os dados em si não são consistentes. Nem todo elemento
copa:empreendimento tem um elemento valorTotalPrevisto dentro. E agora?
É simples: vamos encapsular o processamento desse valor em um método simples,
que retorna zero quando não existe valor total previsto (pra facilitar a soma,
depois).
cost = element.find(‘./valorTotalPrevisto’)
return 0 if (cost is None) else float(cost.text)
Agora basta chamar o get_cost na nossa compreensão de lista:
for element in data.iterfind(‘.//copa:empreendimento’,
namespaces={‘copa’: data_url[:46]})
if element.find(‘./andamento/id’).text != ‘1’]
E aí podemos finalmente somar todos os valores encontrados e imprimir usando o
poderoso método format do Python
(estude!).
total=sum(spending)))
Bônus: Uma versão mais idiomática (PYTHONICA) do código está disponível no meu
Gist. Fique à vontade
para contribuir, comentar, melhorar, etc 🙂
Agradecimentos ao Fernando Masanori que começou a brincadeira com esses
dados!
Este artigo foi importado automaticamente por fazer parte do Planetário Dev. Quer fazer parte deste HUB de conteúdos? Faça parte do Planetário e veja as vantagens.
Não tem site ou blog? Seja um autor do site e ainda pode ser remunerado.
Leia também: