Voltar ao blogue
Guias
Sorin-Gabriel Marica13 de julho de 20219 min de leitura

Extrair e analisar dados da Web com Python e BeautifulSoup

Extrair e analisar dados da Web com Python e BeautifulSoup

Uma visão geral do BeautifulSoup

O BeautifulSoup, tal como indicado na sua documentação, é uma biblioteca Python destinada a extrair dados de ficheiros HTML e XML. Assim, pode utilizar o Python para extrair o conteúdo HTML de um site e, em seguida, utilizar o BeautifulSoup para analisar esse HTML e obter apenas as informações relevantes.

A principal vantagem de utilizar o BeautifulSoup é a sintaxe simples que oferece. Com esta biblioteca, é possível navegar pela árvore DOM, procurar elementos específicos ou modificar o conteúdo HTML. Todas estas vantagens tornaram-na a biblioteca Python mais popular para analisar documentos HTML e XML.

Instalação

Para instalar o BeautifulSoup, deve consultar o guia disponível aqui, uma vez que a instalação varia consoante o sistema operativo que utiliza. Neste artigo, estou a utilizar um sistema Linux e basta executar o seguinte comando:

pip install beautifulsoup4

Se utilizar o Python 3, poderá ser necessário instalar a biblioteca utilizando, em vez disso, o seguinte comando:

pip3 install beautifulsoup4

Tenha em conta que o meu computador já tem o Python 3 instalado. Se é novo no Python, pode encontrar aqui um guia sobre como instalá-lo. Além disso, recomendo que consulte o nosso guia definitivo sobre como criar um web scraper com Python para obter ainda mais informações sobre o assunto.

Criar um scraper com o BeautifulSoup

Agora, se tudo correu bem, estamos prontos para começar a criar o nosso próprio scraper. Para este artigo, optei por recuperar os 100 melhores filmes de todos os tempos do Rotten Tomatoes e guardar tudo nos formatos JSON e CSV.

Recuperar o código-fonte da página

Para nos familiarizarmos com o BeautifulSoup, vamos primeiro recuperar o código HTML completo da página e guardá-lo num novo ficheiro chamado «page.txt».

Se quiser ver o código-fonte HTML de qualquer página, pode fazê-lo no Google Chrome premindo CTRL+U. Isto irá abrir um novo separador, e verá algo semelhante a isto:

Página com o código-fonte, mostrando as tags HTML e de script de uma página de listagem de filmes

Para obter o mesmo código-fonte com o BeautifulSoup e o Python, podemos usar o seguinte código:

import requests
from bs4 import BeautifulSoup
 
scraped_url = 'https://www.rottentomatoes.com/top/bestofrt/'
page = requests.get(scraped_url)
 
soup = BeautifulSoup(page.content, 'html.parser')
 
file = open('page.txt', mode='w', encoding='utf-8')
file.write(soup.prettify())

Neste código, fazemos uma solicitação à página do RottenTomatoes e, em seguida, adicionamos todo o conteúdo da página a um objeto BeautifulSoup. A única utilização do BeautifulSoup neste exemplo é a função final chamada«prettify()», que formata o código HTML para facilitar a leitura.

To understand the function better, for this HTML code “<div><span>Test&lt;/span></div>”, prettify, will add the tabulations and transform it in this formatted code:

<div>

   <span>

       Teste

   </span>

</div>

O resultado final do código é a criação de um ficheiro chamado page.txt que contém todo o código-fonte da nossa página:

Bloco de notas a mostrar o código-fonte da página com itens da lista JSON-LD para uma tabela dos filmes mais vistos

Deve ter em conta que este é o código-fonte da página antes da execução de qualquer código JavaScript. Por vezes, os sites podem optar por alterar o conteúdo da página de forma dinâmica. Nesses casos, o código-fonte da página terá um aspeto diferente do conteúdo efetivamente apresentado ao utilizador. Se precisar que o seu scraper execute JavaScript, pode consultar o nosso guia sobre como criar um scraper web com o Selenium, ou pode utilizar o WebScrapingAPI, o nosso produto que resolve esta questão por si.

Obter os dados da Web

Se consultar o código-fonte da página anterior, verá que é possível encontrar os nomes dos filmes e a respetiva classificação. Felizmente para nós, o RottenTomatoes não carrega a lista de filmes dinamicamente, pelo que podemos avançar e extrair as informações necessárias.

Primeiro, inspecionamos a página e verificamos como o HTML está estruturado. Para isso, pode clicar com o botão direito do rato no título de um filme e selecionar a opção «Inspecionar elemento». Deverá aparecer a seguinte janela:

Ferramentas de desenvolvimento do navegador a destacar uma linha da tabela e um link para um filme numa página do Rotten Tomatoes

I used the red line to highlight the useful information from this image. You can see that the page displays the top movies in a table and that there are four cells on each table row (<tr> element).

A primeira célula contém a posição do filme, a segunda contém informações sobre as classificações (elemento com a classe tMeterScore ), a terceira inclui o título do filme e a última célula indica o número de críticas.

Conhecendo esta estrutura, podemos agora começar a extrair as informações de que precisamos.

import requests
from bs4 import BeautifulSoup
 
links_base = 'https://www.rottentomatoes.com'
scraped_url = 'https://www.rottentomatoes.com/top/bestofrt/'
page = requests.get(scraped_url)
 
soup = BeautifulSoup(page.content, 'html.parser')
 
table = soup.find("table", class_="table") # We extract just the table code from the entire page
rows = table.findAll("tr") # This will extract each table row, in an array
 
movies = []
 
for index, row in enumerate(rows):
    if index > 0: # We skip the first row since this row only contains the column names
        link = row.find("a") # We get the link from the table row
        rating = row.find(class_="tMeterScore") # We get the element with the class tMeterScore from the table row
        movies.append({
            "link": links_base + link.get('href'), # The href attribute of the link
            "title": link.string.strip(), # The strip function removes blank spaces at the beginning and the end of a string
            "rating": rating.string.strip().replace("&nbsp;", ""), # We remove &nbsp; from the string and the blank spaces
        })
        
print(movies)

Ao executar este código, deverá obter um resultado semelhante a este:

Saída do terminal que mostra títulos de filmes, links e classificações extraídos, formatados em JSON

Neste exemplo, estamos a extrair o conteúdo da tabela e a percorrer as linhas da tabela. Uma vez que a primeira linha contém apenas os nomes das colunas, vamos ignorá-la.

On the rest of the rows, we continue the process by extracting the anchor (<a>) element and the span element with the class “tMeterScore”. Having them, we can now retrieve the information needed.

O título do filme encontra-se dentro do elemento âncora, o link é o atributo «href» da âncora e a classificação encontra-se dentro do elemento span com a classe «tMeterScore». Basta criarmos um novo dicionário para cada linha e adicioná-lo à nossa lista de filmes.

Guardar os dados da Web

Até agora, o scraper recuperou e formatou os dados, mas apenas os apresentámos no terminal. Em alternativa, podemos guardar a informação no nosso computador como um ficheiro JSON ou CSV. O código completo do scraper (incluindo a criação de um ficheiro local) é:

import requests
from bs4 import BeautifulSoup
import csv
import json
 
links_base = 'https://www.rottentomatoes.com'
scraped_url = 'https://www.rottentomatoes.com/top/bestofrt/'
page = requests.get(scraped_url)
 
soup = BeautifulSoup(page.content, 'html.parser')
 
table = soup.find("table", class_="table") # We extract just the table code from the entire page
rows = table.findAll("tr") # This will extract each table row from the table, in an array
 
movies = []
 
for index, row in enumerate(rows):
    if index > 0: # We skip the first row since this row only contains the column names
        link = row.find("a") # We get the link from the table row
        rating = row.find(class_="tMeterScore") # We get the element with the class tMeterScore from the table row
        movies.append({
            "link": links_base + link.get('href'), # The href attribute of the link
            "title": link.string.strip(), # The strip function removes blank spaces at the beginning and the end of a string
            "rating": rating.string.strip().replace("&nbsp;", ""), # We remove &nbsp; from the string and the blank spaces
        })
        
file = open('movies.json', mode='w', encoding='utf-8')
file.write(json.dumps(movies))
 
writer = csv.writer(open("movies.csv", 'w'))
for movie in movies:
    writer.writerow(movie.values())

Ir ainda mais longe

Agora que já tem toda a informação, pode optar por aprofundar o seu trabalho de scraping. Lembre-se de que cada filme tem um link. Pode continuar a fazer scraping nas páginas dos filmes e extrair ainda mais informações sobre eles.

Por exemplo, se consultar a página do filme «It Happened One Night» (1934), poderá ver que ainda é possível extrair informações úteis, tais como a pontuação do público, a duração do filme, o género, etc.

No entanto, efetuar todos estes pedidos num curto espaço de tempo parece muito suspeito e pode levar a validações CAPTCHA ou mesmo ao bloqueio de IPs. Para evitar isso, deve utilizar proxies rotativos, para que o tráfego enviado pareça natural e provenha de vários IPs.

Outras funcionalidades do BeautifulSoup

Embora o nosso scraper do RottenTomatoes esteja concluído, o BeautifulSoup ainda tem muito para oferecer. Sempre que estiver a trabalhar num projeto, deve manter a página da documentação aberta para poder procurar rapidamente uma solução quando se deparar com um obstáculo.

Por exemplo, o BeautifulSoup permite navegar pela árvore DOM da página:

from bs4 import BeautifulSoup
 
soup = BeautifulSoup("<head><title>Title</title></head><body><div><p>Some text <span>Span</span></p></div></body>", 'html.parser')
 
print(soup.head.title) # Will print "<title>Title</title>"
print(soup.body.div.p.span) # Will print "<span>Span</span>"

Esta funcionalidade pode ajudá-lo quando precisar de selecionar um elemento que não possa ser identificado pelos seus atributos. Nesse caso, a única forma de o encontrar é através da estrutura do DOM.

Outra vantagem do BeautifulSoup é que permite modificar o código-fonte da página:

from bs4 import BeautifulSoup
 
soup = BeautifulSoup("<head><title>Title</title></head><body><div><p>Some text <span>Span</span></p></div></body>", 'html.parser')
 
soup.head.title.string = "New Title"
print(soup)
# The line above will print "<head><title>New Title</title></head><body><div><p>Some text <span>Span</span></p></div></body>"

Isto pode ser extremamente útil se pretender criar um serviço que permita aos utilizadores otimizar as suas páginas. Por exemplo, pode utilizar o script para extrair o conteúdo de um site, obter o CSS, minimizá-lo e substituí-lo no código-fonte HTML. As possibilidades são infinitas!

Raspe sempre com cuidado

Quero mesmo que te lembres disto: usar Python e BeautifulSoup para extrair dados da Web é uma excelente ideia. Torna o processo muito mais fácil em comparação com outras linguagens de programação.

O scraper que criámos para recuperar os filmes mais bem avaliados de todos os tempos do Rotten Tomatoes pode ser programado em apenas alguns minutos, e pode até utilizá-lo em conjunto com o scraper do IMDB do nosso guia definitivo para scraping com PHP.

No entanto, alguns sites são mais acessíveis aos scrapers do que outros. Embora o projeto apresentado neste artigo seja simples e divertido, há outros que são tudo menos isso. Por vezes, os sites fazem tudo o que está ao seu alcance para impedir que o seu conteúdo seja extraído.

Em certas situações, a única forma de extrair conteúdo é disfarçando a sua atividade através de vários endereços IP e de um navegador real. Para este tipo de situação, criámos a WebScrapingAPI, uma solução poderosa que oferece proxies rotativos e renderização em JavaScript, permitindo-lhe extrair conteúdo de qualquer site que desejar com o mínimo de complicações!

Não acredite apenas na minha palavra, experimente você mesmo! Pode iniciar o seu período de teste gratuito agora mesmo e obter 5000 chamadas de API sem ter de fornecer quaisquer dados confidenciais, como os dados do seu cartão de crédito.

Sobre o autor
Sorin-Gabriel Marica, Desenvolvedor Full-Stack na WebScrapingAPI
Sorin-Gabriel MaricaDesenvolvedor Full-Stack

Sorin Marica é engenheiro Full Stack e DevOps na WebScrapingAPI, onde desenvolve funcionalidades do produto e mantém a infraestrutura que garante o bom funcionamento da plataforma.

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.