Web Scraping de páginas dinâmicas com Python
Web scraping pode ficar bem complicado quando a página é dinâmica, você não mais precisa somente carregar o código-fonte da página mas também precisa esperar o JavaScript da página construir a página, que é o caso do site do Reclame Aqui.
Nesses casos, precisamos controlar um navegador remotamente por meio de um web
driver, por exemplo, o geckodriver
para o Firefox ou chromedriver
para o
Chrome.
Para isso, usamos uma biblioteca a partir da qual podemos instruir este
navegador a fazer o que queremos, por exemplo o Selenium
.
Pros meus propósitos, usarei o Selenium
para abrir uma URL e extrair alguma
informação na página, tarefa para alguma biblioteca que entenda estrutura HTML e
facilite a extração dos elementos, como a BeautifulSoup
.
Utilizo essas duas bibliotecas no script abaixo. E podemos instalá-las com:
$ pip3 instal selenium bs4
Para fazer o crawleamento, criei uma classe que aceita o nome de uma empresa e o web driver a ser utilizado.
Ela tem o método ReclameAqui.extrair_informacoes(n_paginas)
que vai extrair os
links e títulos das reclamações das primeiras n_paginas
.
Outro método, o ReclameAqui.extrair_descricoes()
, abre cada uma dessas URLs e
extrai as descrições das informações.
Para extrair a informação desejada, nós fazemos o BeautifulSoup
“entender” o
código-fonte primeiro para depois usarmos os métodos para extração de algum
element do HTML.
Por exemplo, para extrair o título e os links das reclamações em cada página,
procuramos por parágrafos que contenham a classe text-detail
:
(<p class='text-detail'></p>
), e depois procuramos elementos a
, que contém o
título dentro dele e o link dentro do atributo href
.
Já para a descrição, procuramos por um elemento div
com a classe
complain-body
, e extraímos o texto dentro dele.
from time import sleepfrom selenium import webdriverfrom bs4 import BeautifulSoup as bs class ReclameAqui: base_url = "https://www.reclameaqui.com.br/empresa/" def __init__(self, driver, empresa): self.driver = driver self.empresa = empresa def extrair_informacoes(self, n_paginas): url = self.base_url + self.empresa + "/lista-reclamacoes/?pagina=" self.reclamacoes, self.titulos, self.links = [], [], [] for i in range(1, n_paginas + 1): self.driver.get(url + str(i)) sleep(3) html = bs(self.driver.page_source, "html.parser") reclamacoes_html = html.find_all("p", {"class": "text-detail"}) reclamacoes_na_pagina = [ reclamacao.text.split("|") for reclamacao in reclamacoes_html ] self.reclamacoes.extend(reclamacoes_na_pagina) titulos_e_links = html.find_all( "a", {"class": "link-complain-id-complains"} ) self.titulos.extend([titulo.text.strip() for titulo in titulos_e_links]) self.links.extend([link.get("href") for link in titulos_e_links]) def extrair_descricoes(self): urls = [self.base_url + link[1:] for link in self.links] self.descricoes = [] for url in urls: self.driver.get(url) sleep(3) html = bs(self.driver.page_source, "html.parser") descricao = html.find("div", {"class": "complain-body"}).text.strip() self.descricoes.append(descricao)
Esse código deve funcionar para qualquer empresa contanto que a estrutura do HTML do ReclameAqui não mude (isto é, o nome das classes e o tipo dos elementos onde as informações se encontram).