Guia

Web Scraping com BeautifulSoup: Guia

Aprenda web scraping com Python e BeautifulSoup. Extraia dados de sites, parse HTML, trate erros e siga boas práticas de scraping

4 min de leitura

Introdução

Web scraping é a técnica de extrair dados de páginas web automaticamente. Com Python e a biblioteca BeautifulSoup, você pode coletar informações de sites que não oferecem APIs, como preços de produtos, notícias, dados de pesquisa e muito mais.

Neste guia, vamos aprender a usar BeautifulSoup para extrair dados de páginas HTML, tratar erros e seguir boas práticas de scraping responsável.

Instalação

Instale BeautifulSoup e Requests:

pip install beautifulsoup4 requests

O beautifulsoup4 faz o parsing do HTML e o requests faz as requisições HTTP para baixar as páginas.

Primeiro scraping

Vamos extrair dados de uma página HTML simples:

import requests
from bs4 import BeautifulSoup

# Baixar a pagina
url = "https://books.toscrape.com/"
resposta = requests.get(url)
resposta.raise_for_status()  # Levanta erro se a requisicao falhar

# Parsear o HTML
soup = BeautifulSoup(resposta.text, "html.parser")

# Extrair o titulo da pagina
titulo = soup.title.string
print(f"Titulo: {titulo}")

O site books.toscrape.com é um site de prática para web scraping, feito especificamente para aprendizado.

Encontrando elementos

O BeautifulSoup oferece vários métodos para encontrar elementos:

# Encontrar o primeiro elemento por tag
primeiro_h1 = soup.find("h1")

# Encontrar por classe CSS
produtos = soup.find_all("article", class_="product_pod")

# Encontrar por ID
elemento = soup.find(id="meu-id")

# Encontrar por atributo
links = soup.find_all("a", attrs={"data-tipo": "externo"})

Usando seletores CSS

O método select() aceita seletores CSS, que são frequentemente mais práticos:

# Seletor de classe
produtos = soup.select("article.product_pod")

# Seletor de descendente
titulos = soup.select("article.product_pod h3 a")

# Seletor de atributo
links_externos = soup.select("a[href^='http']")

# Primeiro resultado apenas
primeiro = soup.select_one("h1")

Exemplo prático: extraindo livros

Vamos extrair informações de livros do site de prática:

import requests
from bs4 import BeautifulSoup


def extrair_livros(url):
    resposta = requests.get(url)
    resposta.raise_for_status()
    soup = BeautifulSoup(resposta.text, "html.parser")

    livros = []
    for artigo in soup.select("article.product_pod"):
        titulo = artigo.select_one("h3 a")["title"]
        preco = artigo.select_one(".price_color").text
        disponivel = artigo.select_one(".availability").text.strip()
        estrelas = artigo.select_one("p.star-rating")["class"][1]

        livros.append({
            "titulo": titulo,
            "preco": preco,
            "disponivel": disponivel,
            "estrelas": estrelas,
        })

    return livros


livros = extrair_livros("https://books.toscrape.com/")
for livro in livros[:5]:
    print(f"{livro['titulo']} - {livro['preco']}")

Muitos sites dividem conteúdo em múltiplas páginas. Para coletar tudo, siga os links de paginação:

def extrair_todas_paginas(url_base):
    todos_livros = []
    pagina = 1

    while True:
        if pagina == 1:
            url = url_base
        else:
            url = f"{url_base}catalogue/page-{pagina}.html"

        resposta = requests.get(url)
        if resposta.status_code != 200:
            break

        soup = BeautifulSoup(resposta.text, "html.parser")
        livros = soup.select("article.product_pod")

        if not livros:
            break

        for artigo in livros:
            titulo = artigo.select_one("h3 a")["title"]
            todos_livros.append(titulo)

        # Verificar se existe proxima pagina
        proxima = soup.select_one("li.next a")
        if not proxima:
            break

        pagina += 1

    return todos_livros

Extraindo texto e atributos

# Texto de um elemento
paragrafo = soup.find("p")
texto = paragrafo.text          # Texto sem tags HTML
texto = paragrafo.get_text()    # Equivalente
texto = paragrafo.get_text(strip=True)  # Remove espacos extras

# Atributos de um elemento
link = soup.find("a")
href = link["href"]             # Atributo href
href = link.get("href", "")    # Com valor padrao se nao existir

# Todos os atributos
atributos = link.attrs  # Dicionario com todos os atributos

Tratamento de erros

Scraping envolve muitas situações imprevisíveis. Trate erros adequadamente:

import requests
from bs4 import BeautifulSoup
import time


def scraping_seguro(url, tentativas=3):
    headers = {
        "User-Agent": "Mozilla/5.0 (compatible; MeuBot/1.0)"
    }

    for tentativa in range(tentativas):
        try:
            resposta = requests.get(url, headers=headers, timeout=10)
            resposta.raise_for_status()
            return BeautifulSoup(resposta.text, "html.parser")
        except requests.exceptions.HTTPError as e:
            print(f"Erro HTTP: {e}")
            if resposta.status_code == 429:  # Too Many Requests
                print("Rate limit atingido, aguardando...")
                time.sleep(60)
            else:
                break
        except requests.exceptions.ConnectionError:
            print(f"Erro de conexao. Tentativa {tentativa + 1}/{tentativas}")
            time.sleep(5)
        except requests.exceptions.Timeout:
            print("Timeout. Tentando novamente...")
            time.sleep(5)

    return None

Salvando dados extraídos

Salvando em CSV

import csv


def salvar_csv(livros, arquivo):
    with open(arquivo, "w", newline="", encoding="utf-8") as f:
        escritor = csv.DictWriter(f, fieldnames=livros[0].keys())
        escritor.writeheader()
        escritor.writerows(livros)

salvar_csv(livros, "livros.csv")

Salvando em JSON

import json


def salvar_json(dados, arquivo):
    with open(arquivo, "w", encoding="utf-8") as f:
        json.dump(dados, f, ensure_ascii=False, indent=2)

salvar_json(livros, "livros.json")

Salvando com Pandas

import pandas as pd

df = pd.DataFrame(livros)
df.to_csv("livros.csv", index=False)
df.to_excel("livros.xlsx", index=False)

Lidando com HTML dinâmico

BeautifulSoup funciona apenas com HTML estático. Se o site carrega conteúdo via JavaScript, você precisará de ferramentas adicionais:

pip install requests-html
from requests_html import HTMLSession

session = HTMLSession()
resposta = session.get("https://exemplo.com")
resposta.html.render()  # Renderiza JavaScript

soup = BeautifulSoup(resposta.html.html, "html.parser")

Para sites mais complexos, considere usar Selenium ou Playwright.

Boas práticas e ética

Web scraping deve ser feito de forma responsável:

  • Respeite o robots.txt: verifique https://site.com/robots.txt antes de fazer scraping
  • Limite a frequência: adicione delays entre requisições com time.sleep()
  • Identifique-se: use um User-Agent que identifique seu bot
  • Não sobrecarregue o servidor: faça requisições em intervalos razoáveis
  • Verifique os termos de uso: alguns sites proíbem scraping explicitamente
  • Prefira APIs: se o site oferece uma API, use-a em vez de scraping
  • Cache: salve páginas baixadas para evitar requisições repetidas durante o desenvolvimento
import time

for url in urls:
    dados = scraping_seguro(url)
    processar(dados)
    time.sleep(2)  # Espere 2 segundos entre requisicoes

Conclusão

Web scraping com BeautifulSoup e Python é uma habilidade valiosa para coletar dados que não estão disponíveis via APIs. A combinação de Requests para baixar páginas e BeautifulSoup para parsear HTML é simples e poderosa para a maioria dos casos. Lembre-se sempre de praticar scraping responsável, respeitando os limites dos servidores e os termos de uso dos sites que você acessa.