Web scraping da Corrida de São Silvestre

Não é novidade que a internet é rica em informação. Contém textos e mais textos sobre tudo. Mas não é só por meio dessa informação, em forma de texto, que podemos aprender. A internet contém também muita informação crua na forma de dados.

Ao contrário dos textos, a informação crua de dados, conjuntos de números, requer coleta, processamento e análise para se transformar em informação verbal. Quando os dados estão reunidos, e.g. dentro de um único arquivo, não existe problema de coleta. Você baixa esse arquivo e começa a processar e analisar de imediato. Por outro lado, muitos dados estão disponíveis de uma maneira fragmentada e a coleta deles pode ser um desafio.

A corrida de São Silvestre têm resultados disponíveis na internet desde 1998. A partir de 2007, os resultados são centenas de páginas html com tabelas de 10 linhas, uma linha por atleta. Com os dados fragmentados desse jeito, qualquer iniciativa do tipo recorta-cola-edita-repete, algo que muitos usuários de planilha eletrônica fazem todos os dias, é abandonada por ser impraticável, inexequível.

Vou partir para uma solução computacional, afinal, o computador tem que trabalhar para mim, e não eu para ele. Vou trabalhar com técnicas de web scraping para extrair esse resultados de todas as páginas.

Programar é algo que eu gosto, pois apredendo e também exercito o que já sei. Na pior das hipóteses, posso passar horas programando essa solução, mas certamente será menos tempo do que fazer a terefa manualmente, algo repetitivo e que não acrescenta conhecimento algum. Na melhor das hipóteses, posso usar minha rotina para outras páginas e compartilhar com outras pessoas (com você que está lendo essa matéria).

As páginas de resultado têm uma estrutura fixa que é mantida em todas. Os resultados para o masculino de 2013 consistem de cabeçalho e rodapé com informações de situação, botões para mudar de página, e a tabela que quero no meio. Essa têm 10 linhas e 8 colunas que incluem informações como nome, idade e tempo para cumprir a prova.

Eu visitei várias páginas para certificar que a estrutura era a mesma. Então fui para a página 4 (não me pergunte porque essa) para usar o inspecionar elemento do Firefox e encontrei a estrutura na qual (figura abaixo) a tabela é um elemento html <table>, que tem 8 atributos, como a largura <witdh='98%'> e a cor de fundo <bgcolor>.

Carreguei os pacotes necessários e li a página para decodificar com htmlTreeParse(). No summary tem-se o número de ocorrências de cada tipo de elemento html na página. O campo que me interessa é o table pois é ele que contém os dados que quero. Mas não existe apenas um table (como eu gostaria) e sendo assim, eu tive que ser específico ao escrever a minha consulta para trazer a tabela alvo ignorando as demais.

Minha primeira tentativa foi tentar a função XML::readHTMLTable() que extraí as tabelas (table) de páginas html. De início eu achei que não tinha dado muito certo porque ela leu todas as ocorrências de table deixando em uma lista (tb). Para estudar o que eu havia conseguido, pedi classe (class) e depois dimensão (dim). Nisso vi que tinha um data.frame no elemento 10 com as dimensões certas, 10 por 8. Era a tabela que eu queria.

O código até aqui já resolveu 1 problema: ler a tabela de uma página. No entanto, não vou omitir minhas outras tentativas. Eu procurei um jeito de especificar apenas a tabela de resultados que eu queria e consegui filtrando para as tabelas que tinham width='98%' e bgcolor. Essas são informações que vieram do inspecionar elemento que fiz (ver imagem). Note como fazer para especificar dois atributos.

Temos duas soluções para o problema 1, mas ainda resta o problema 2: ler todas as páginas. Resolvi correr um loop no intervalo de páginas. Para deixar meu script mais interessante e praticar um pouco mais, o número total de páginas eu fiz questão de extrair. Usei o inspecionar elemento para chegar à especificação abaixo.

Para ler as 1745 páginas, eu precisei preparar os endereços. Felizmente, o endereço de cada página contém o seu número (PaginaAtual=) e eu só precisei alterá-lo. Criei uma função (getTable), que ao receber o link (url), executa os passos: decodifica a página, extraí a tabela e escreve num arquivo texto, acrescentando novas linhas (append=TRUE). Eu incluí uma opção de verbose para ter algum status de evolução do processo, algo que foi útil enquanto eu fazia a função. Também incluí um indicador de progresso modo texto no laço for que faz a extração dos dados.

O sexo também é parte do endereço (sexo=) então resolvi ler os dois. Algo inesperado aconteceu quando eu experimentei a leitura com poucas páginas. O que difere na url para masculino e feminino, nos resultados gerais, não é o sexo=M e sexo=F que eu esperava mas o tipo=3 e tipo=4. Independente do sexo, as urls têm sexo=M. Esquisito, não é mesmo? Mas nos resultados por faixa etária o sexo=F aparece.

O que domina o tempo para concluir a tarefa é a velocidade de conexão com a internet. O processo levou 15 minutos com conexão cabeada.

O objetivo dessa matéria foi mostrar como extrair esses dados de página html. Agora que temos os dados, vamos só fazer um acabamento: ler os dados para certificar a integridade/qualidade e conhecer, mesmo que por cima, um pouco sobre eles.

plot of chunk unnamed-chunk-6

plot of chunk unnamed-chunk-6

plot of chunk unnamed-chunk-6

Você pode adaptar esse script (webScrapSaoSilvestre.R) para os resultados de outros anos da São Silvestre. No entanto, se você visitar os outros links, vai perceber que os resultados em html começaram em 2007 e que mantiveram o padrão estrutural. Antes de 2007, eles eram divulgados em formato texto de comprimento fixo e cada ano tinha uma estrutura diferente. Para ler os últimos, você pode usar a read.fwf().

Share

Leave a Reply

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">