Guia completo: Como automatizar a coleta de dados na web com Python e Selenium
Muita gente na internet tem dúvida de como fazer robôs que buscam coisas em sites, baixam conteúdo ou simplesmente executam ações para testar alguma funcionalidade do site, sistema ou algo relacionado. Sendo assim resolvi fazer esse Post onde eu faço o acesso a um site, analiso os elementos da página HTML e faço a automação da navegação via Selenium. Uma vez que a automação da navegação é concluída, pego os dados de uma determinada página, faço uma conversão deles pra um formato CSV e o download desses dados para uma análise posterior, essa técnica é chamada Web Scraping.
O Framework mais famoso em Python para fazer Web Scraping é o Scrapy, porém nele temos alguns problemas para renderizar páginas que usam javascript, como por exemplo SPAs ( Single Page Applications ), essas páginas são todas renderizadas via Javascript que é carregado dinamicamente. Isso faz a navegação do site ficar mais amigável, sem a necessidade de ficar recarregando toda a página cada vez que é clicado em um link do site, como acontecia antigamente. Para contornar esse problema do Scrapy é necessário utilizar um outro módulo chamado Splash. Quem quiser saber mais sobre eles pode acessar esse link: https://github.com/scrapy-plugins/scrapy-splash .
Bom, o site que eu quero pegar os dados segue abaixo:
http://leideacesso.etransparencia.com.br/itaquaquecetuba.prefeitura.sp/Portal/desktop.html?410
Interações automatizadas com Selenium
Esse site tem os dados referente as receitas e as despesas da prefeitura da Itaquaquecetuba, ele é um site que usa javascript pra tudo, então tem uma série de páginas pra fazer o carregamento o que acaba deixando um pouco mais difícil o uso de frameworks como o Scrapy, sendo assim utilizei o Selenium para fazer a automação da navegação.
O Selenium sempre vai utilizar um navegador para acessar o site que você quer pegar os dados, para isso é necessário instalar um driver que pode ser encontrado no site oficial:
http://www.seleniumhq.org/download/
Fiz a instalação do GeckoDriver para o Windows, esse driver irá fazer toda a navegação no navegador Firefox.
Bom, agora que a instalação foi feita, vamos acessar o site e ver qual o caminho que será necessário fazer para chegar nos dados que eu quero coletar.
Acessando a página inicial:
Acima temos a página inicial e os dados que eu quero extrair estão dentro de receitas. Após clicar nesse valor será aberta uma segunda página.
No Selenium para fazer a navegação é necessário saber o nome dos elementos DOM ( Document Object Model ) que eu quero interagir, então vamos fazer uma análise.
Na imagem acima em amarelo está o botão em que eu quero automatizar uma ação de clique e precisaremo inspecionar o elemento através do navegador:
Qualquer atributo do elemento pode ser usado para interagir com ele. Eu escolhi a classe que tem o nome val01. O ideal é sempre tentar achar alguma tributo que seja único, como por exemplo o ID do elemento, mas durante o post vou mostrar como se seleciona o elemento pelo ID também.
Uma vez clicado nesse botão será carregada essa segunda página:
Nessa segunda página, temos mais elementos para interagir, como o Select, onde posso selecionar o ano de onde quero pegar os dados, assim que selecionado o ano é necessário clicar no botão filtrar.
Agora vamos pegar algum atributo para identificar esses elementos também.
Primeiro vou inspecionar o elemento Select que usaremos para selecionar o ano antes de clicar no botão.
Neste elemento, vou pegar o atributo name que possui o valor exe. Esse valor será utilizado para fazer a identificação dele via Selenium.
Agora vou inspecionar o botão filtrar.
Esse eu vou interagir com ele através do campo ID, que possui o valor imgFiltrar.
Agora que já sei como interagir, vou precisar pegar os dados da tabela abaixo:
Agora vou inspecionar a tabela:
A imagem acima mostra que ela está dentro de uma DIV com o ID bd01. Esse id que vou usar para interagir.
Um pouco de código
Até agora fizemos somente a análise do que queremos pegar, então vamos entrar em código.
Primeiro passo é instalar o módulo do Selenium para Python e o BeautifulSoup.
python3 -m pip install selenium bs4
Agora que já temos o módulo instalado vou colocar embaixo a parte do código que se refere ao selenium e as explicações estão nos comentários:
# O modulo time aqui foi utilizado para esperar o carregamento das paginas atraves do firefox import time # o modulo webdriver e necessario para definir qual navegador sera utilizado para fazer a automacao from selenium import webdriver # o modulo Select sera utilizado para interagir com a caixa de selecao onde sera definido o ano em que quero buscar os dados from selenium.webdriver.support.ui import Select # esse modulo sera utilizado para from bs4 import BeautifulSoup # a linha abaixo define qual e o navegador que queremos utilizar, lembrando que eu instalei somente o driver para conexao com o firefox, mas existem tambem para Chrome e InternetExplorer driver = webdriver.Firefox() # abaixo foi definido qual e o site que quero acessar driver.get("http://leideacesso.etransparencia.com.br/itaquaquecetuba.prefeitura.sp/Portal/desktop.html?410") # o Sleep abaixo e para aguardar o carregamento da pagina time.sleep(15) # Aqui estou buscando o elemento que possui na classe o valor valo01, que e respectivo ao valor da receita onde quero clicar para ir na proxima pagina receita = driver.find_element_by_class_name("val01") # aqui e feito um clique no elemento que foi encontrado acima receita.click() # aguardando o carregamento da pagina time.sleep(10) # agora quero as receitas desde 2013 ate 2017 anos = ["2013","2014","2015","2016","2017"] for a in anos: # aqui e utilizado o modulo Select do selenium para interagir com o ComboBox select = Select(driver.find_element_by_name("exe")) # aqui e alterado o valor do ComboBox select.select_by_value(a) # agora e buscado o elemento cujo o ID e igual a imgFiltrar filtrar = driver.find_element_by_id('imgFiltrar') # retornado o elemento da busca e clicado no botao filtrar.click() # aguardando o carregamento da tabela time.sleep(3) # armazenando a div que possui a tabela dentro da variavel dados dados = driver.find_element_by_id("bd10")
Se tudo estiver instalado corretamente e você executar esse script, verá que o navegador Firefox irá abrir sozinho e os botões começarão a ser clicados automaticamente através do selenium.
Com a parte da automação já feita, agora vamos fazer a raspagem dos dados.
O código abaixo está dentro do for no pedaço de código acima, no final vou postar o código completo.
# aqui e pegado o codigo HTML que esta dentro da div bd01 no codigo que foi mostrado acima html = dados.get_attribute("innerHTML") # com o codigo HTML dentro da variavel, vamos usar o BeautifulSoup para fazer o parser desse HTML soup = BeautifulSoup(html, "html.parser") # dentro da variavel soup temos o codigo html retornado pelo Selenium ja convertido para o BS # vou utilizar o metodo select_one para buscar o elemento table dentro desse codigo table = soup.select_one("table") # no conteudo dessa tabela temos varias virgulas e espacos, como vou converter esses dados pra csv, vou definir o delimitador com o caracter | # na linha abaixo estou buscando todos os elementos tr, que possui a classe Grid_title e os elementos filhos cujo a tag e td # e feito um list comprehesion para pegar somente os elementos e eles estao sendo separados pelo caracter | headers = [header.text+"|" for header in table.select("tr.Grid_title td")] # abaixo estou buscando os elementos tr que possuem a classe Grid_line no css # e um novo list comprehension para criar uma lista somente com o s elementos que eu quero line = [] data = [d for d in table.select("tr.Grid_line")] for d in data: linha = "" for t in d.select("td"): linha += t.text+"|" line.append(linha) # aqui e a mesma coisa que acima porem com a classe Grid_line_even line_even = [] data = [ d for d in table.select("tr.Grid_line_even")] for d in data: linha = "" for t in d.select("td"): linha += t.text+"|" line_even.append(linha) # agora que os dados ja foram parseados, vou fazer a escrita do arquivo CSV with open("%s.csv"%a,"w") as f: s = "".join(headers) f.write(s+"\n") for l in line: f.write(l+"\n") for l in line_even: f.write(l+"\n") # fim time.sleep(10) driver.close()
Sendo assim, o código completo ficou da seguinte maneira:
# O modulo time aqui foi utilizado para esperar o carregamento das paginas atraves do firefox import time # o modulo webdriver e necessario para definir qual navegador sera utilizado para fazer a automacao from selenium import webdriver # o modulo Select sera utilizado para interagir com a caixa de selecao onde sera definido o ano em que quero buscar os dados from selenium.webdriver.support.ui import Select # esse modulo sera utilizado para from bs4 import BeautifulSoup # a linha abaixo define qual e o navegador que queremos utilizar, lembrando que eu instalei somente o driver para conexao com o firefox, mas existem tambem para Chrome e InternetExplorer driver = webdriver.Firefox() # abaixo foi definido qual e o site que quero acessar driver.get("http://leideacesso.etransparencia.com.br/itaquaquecetuba.prefeitura.sp/Portal/desktop.html?410") # o Sleep abaixo e para aguardar o carregamento da pagina time.sleep(15) # Aqui estou buscando o elemento que possui na classe o valor valo01, que e respectivo ao valor da receita onde quero clicar para ir na proxima pagina receita = driver.find_element_by_class_name("val01") # aqui e feito um clique no elemento que foi encontrado acima receita.click() # aguardando o carregamento da pagina time.sleep(10) # agora quero as receitas desde 2013 ate 2017 anos = ["2013","2014","2015","2016","2017"] for a in anos: # aqui e utilizado o modulo Select do selenium para interagir com o ComboBox select = Select(driver.find_element_by_name("exe")) # aqui e alterado o valor do ComboBox select.select_by_value(a) # agora e buscado o elemento cujo o ID e igual a imgFiltrar filtrar = driver.find_element_by_id('imgFiltrar') # retornado o elemento da busca e clicado no botao filtrar.click() # aguardando o carregamento da tabela time.sleep(3) # armazenando a div que possui a tabela dentro da variavel dados dados = driver.find_element_by_id("bd10") # aqui e pegado o codigo HTML que esta dentro da div bd01 no codigo que foi mostrado acima html = dados.get_attribute("innerHTML") # com o codigo HTML dentro da variavel, vamos usar o BeautifulSoup para fazer o parser desse HTML soup = BeautifulSoup(html, "html.parser") # dentro da variavel soup temos o codigo html retornado pelo Selenium ja convertido para o BS # vou utilizar o metodo select_one para buscar o elemento table dentro desse codigo table = soup.select_one("table") # no conteudo dessa tabela temos varias virgulas e espacos, como vou converter esses dados pra csv, vou definir o delimitador com o caracter | # na linha abaixo estou buscando todos os elementos tr, que possui a classe Grid_title e os elementos filhos cujo a tag e td # e feito um list comprehesion para pegar somente os elementos e eles estao sendo separados pelo caracter | headers = [header.text+"|" for header in table.select("tr.Grid_title td")] # abaixo estou buscando os elementos tr que possuem a classe Grid_line no css # e um novo list comprehension para criar uma lista somente com o s elementos que eu quero line = [] data = [d for d in table.select("tr.Grid_line")] for d in data: linha = "" for t in d.select("td"): linha += t.text+"|" line.append(linha) # aqui e a mesma coisa que acima porem com a classe Grid_line_even line_even = [] data = [ d for d in table.select("tr.Grid_line_even")] for d in data: linha = "" for t in d.select("td"): linha += t.text+"|" line_even.append(linha) # agora que os dados ja foram parseados, vou fazer a escrita do arquivo CSV with open("%s.csv"%a,"w") as f: s = "".join(headers) f.write(s+"\n") for l in line: f.write(l+"\n") for l in line_even: f.write(l+"\n") # fim time.sleep(10) driver.close()
About author
Você pode gostar também
Como Implementar a Análise de Qualidade de Código com DevOps e SonarQube
Dentro da ótica do DevOps e de como implementar agilidade com qualidade, temos os testes automatizados como um dos principais pilares para manter a essência do CI (Continuous Integration), porém
4Linux se torna A.T.O do EXIN e lança curso DEVOPS Master
A 4linux acaba de se tornar um A.T.O. (Authorized Training Organization) do EXIN e juntamente teve o conteúdo de seu treinamento ‘DEVOPS Master’ aprovado, isso significa que o material didático criado
Guia prático: Como configurar e usar o Minio Server em sua infraestrutura
Minio é um storage de objetos, de alta performance e distribuído. A grande vantagem de usá-lo, reside em sua total compatibilidade com o Amazon S3. Neste artigo explico como subir