Web Scraping com BeautifulSoup e Requests

Atualizado em: 21/08/2026
Tempo de leitura: 5 minutos
logo do BeautifulSoup em um fundo branco

Web scraping com BeautifulSoup e Requests permite coletar informações de páginas HTML e transformá-las em dados estruturados. Neste guia, você vai aprender a fazer requisições com segurança, localizar elementos, extrair textos e links, percorrer páginas e salvar o resultado em CSV.

O que são Requests e BeautifulSoup?

BibliotecaResponsabilidade
RequestsFaz a requisição HTTP e recebe a resposta do servidor
BeautifulSoupInterpreta o HTML e ajuda a localizar elementos e atributos

Em outras palavras, o Requests baixa o conteúdo e o BeautifulSoup organiza esse conteúdo para consulta. Para conhecer mais opções de requisição, consulte o guia da biblioteca Requests em Python.

Como instalar BeautifulSoup e Requests

Crie um ambiente virtual para o projeto e instale os pacotes:

python -m venv .venv

# Windows
.venv\Scripts\activate

# Linux ou macOS
source .venv/bin/activate

pip install requests beautifulsoup4

O pacote instalado se chama beautifulsoup4, mas a importação é feita pelo módulo bs4.

Primeiro exemplo de web scraping

O exemplo abaixo acessa uma página simples, verifica se a resposta foi bem-sucedida e extrai o título:

import requests
from bs4 import BeautifulSoup

url = "https://example.com/"

resposta = requests.get(url, timeout=10)
resposta.raise_for_status()

soup = BeautifulSoup(resposta.text, "html.parser")
titulo = soup.find("h1")

if titulo:
    print(titulo.get_text(strip=True))

O parâmetro timeout impede que o programa espere indefinidamente. Já raise_for_status() gera uma exceção quando o servidor responde com um erro HTTP.

Fazendo requisições de forma mais robusta

Uma função reutilizável ajuda a padronizar timeout, identificação do projeto e tratamento de erros:

import requests

CABECALHOS = {
    "User-Agent": (
        "ColetorEducacional/1.0 "
        "(contato: seu-email@example.com)"
    )
}


def baixar_html(url):
    try:
        resposta = requests.get(
            url,
            headers=CABECALHOS,
            timeout=15,
        )
        resposta.raise_for_status()
        return resposta.text
    except requests.Timeout:
        print(f"Tempo esgotado ao acessar {url}")
    except requests.HTTPError as erro:
        print(f"Erro HTTP em {url}: {erro}")
    except requests.RequestException as erro:
        print(f"Falha na requisição para {url}: {erro}")

    return None

Identifique o seu projeto de forma honesta quando isso fizer sentido. Não tente disfarçar o coletor como outro software nem contornar mecanismos de proteção do site.

Como localizar elementos no HTML

Considere este HTML de exemplo:

<section id="catalogo">
    <article class="produto" data-id="101">
        <h2 class="nome">Teclado</h2>
        <span class="preco">R$ 120,00</span>
        <a href="/produtos/101">Detalhes</a>
    </article>

    <article class="produto" data-id="102">
        <h2 class="nome">Mouse</h2>
        <span class="preco">R$ 80,00</span>
        <a href="/produtos/102">Detalhes</a>
    </article>
</section>

find()

Retorna o primeiro elemento correspondente:

primeiro_produto = soup.find("article", class_="produto")

find_all()

Retorna todos os elementos correspondentes:

produtos = soup.find_all("article", class_="produto")

for produto in produtos:
    print(produto.get_text(" ", strip=True))

select() e select_one()

Esses métodos aceitam seletores CSS, o que facilita consultas por classe, ID e hierarquia:

catalogo = soup.select_one("#catalogo")
produtos = soup.select("#catalogo article.produto")
nomes = soup.select(".produto .nome")
from urllib.parse import urljoin

url_base = "https://site-permitido.example/"

for produto in soup.select("article.produto"):
    nome = produto.select_one(".nome")
    preco = produto.select_one(".preco")
    link = produto.select_one("a[href]")

    dados = {
        "id": produto.get("data-id"),
        "nome": nome.get_text(strip=True) if nome else None,
        "preco": preco.get_text(strip=True) if preco else None,
        "url": urljoin(url_base, link["href"]) if link else None,
    }

    print(dados)

Verifique se o elemento existe antes de chamar get_text() ou acessar um atributo. Essa validação evita erros quando o HTML muda ou algum item não contém todos os campos.

Projeto completo: coletar dados e salvar em CSV

O código abaixo reúne a estrutura principal de um coletor. Substitua a URL e os seletores pelos de uma página que autorize a coleta.

import csv
from urllib.parse import urljoin

import requests
from bs4 import BeautifulSoup

URL_BASE = "https://site-permitido.example/"
URL_CATALOGO = urljoin(URL_BASE, "catalogo")
CABECALHOS = {
    "User-Agent": (
        "ColetorEducacional/1.0 "
        "(contato: seu-email@example.com)"
    )
}


def baixar_html(url):
    resposta = requests.get(
        url,
        headers=CABECALHOS,
        timeout=15,
    )
    resposta.raise_for_status()
    return resposta.text


def extrair_produtos(html):
    soup = BeautifulSoup(html, "html.parser")
    produtos = []

    for card in soup.select("article.produto"):
        nome = card.select_one(".nome")
        preco = card.select_one(".preco")
        link = card.select_one("a[href]")

        if not nome:
            continue

        produtos.append(
            {
                "nome": nome.get_text(strip=True),
                "preco": (
                    preco.get_text(strip=True)
                    if preco
                    else ""
                ),
                "url": (
                    urljoin(URL_BASE, link["href"])
                    if link
                    else ""
                ),
            }
        )

    return produtos


def salvar_csv(produtos, caminho="produtos.csv"):
    campos = ["nome", "preco", "url"]

    with open(caminho, "w", newline="", encoding="utf-8") as arquivo:
        escritor = csv.DictWriter(arquivo, fieldnames=campos)
        escritor.writeheader()
        escritor.writerows(produtos)


def main():
    try:
        html = baixar_html(URL_CATALOGO)
        produtos = extrair_produtos(html)
        salvar_csv(produtos)
        print(f"{len(produtos)} produtos salvos.")
    except requests.RequestException as erro:
        print(f"Não foi possível baixar a página: {erro}")
    except OSError as erro:
        print(f"Não foi possível salvar o CSV: {erro}")


if __name__ == "__main__":
    main()

A separação em funções facilita testar a extração com um arquivo HTML local, sem fazer uma nova requisição a cada execução.

Como trabalhar com paginação

Quando o catálogo está dividido em várias páginas, percorra somente a quantidade necessária, inclua uma pausa razoável e pare quando não houver resultados:

import time


def coletar_paginas(limite=5):
    todos_os_produtos = []

    for pagina in range(1, limite + 1):
        url = f"https://site-permitido.example/catalogo?page={pagina}"
        html = baixar_html(url)
        produtos = extrair_produtos(html)

        if not produtos:
            break

        todos_os_produtos.extend(produtos)
        time.sleep(1)

    return todos_os_produtos

Não faça requisições em alta frequência. Respeite limites informados pelo site e use cache quando precisar repetir a análise do mesmo conteúdo.

Sites que carregam conteúdo com JavaScript

Requests recebe o HTML enviado pelo servidor, mas não executa JavaScript. Se os dados aparecem somente depois que a página carrega, procure primeiro uma API oficial ou um recurso de exportação. Quando a automação de navegador for realmente necessária, use-a apenas com autorização e respeite as regras do serviço.

Boas práticas legais e éticas

  • Leia os termos de uso e as políticas do site antes de coletar dados.
  • Consulte o arquivo robots.txt como indicação das áreas destinadas ou não a robôs, sem tratá-lo como única fonte de permissão.
  • Prefira APIs oficiais quando estiverem disponíveis.
  • Não contorne login, captcha, bloqueios ou controles de acesso.
  • Evite coletar dados pessoais ou conteúdo protegido sem base adequada.
  • Reduza a frequência das requisições e armazene resultados quando possível.
  • Identifique seu projeto e forneça um contato quando apropriado.
  • Interrompa a coleta se o servidor responder com bloqueio ou limitação.

Erros comuns em web scraping

ProblemaComo evitar
AttributeError ao usar get_text()Confirme se find() ou select_one() encontrou o elemento
Caracteres incorretosVerifique a codificação da resposta e salve arquivos como UTF-8
Programa travadoDefina um timeout para cada requisição
Links incompletosUse urljoin() para combinar URLs relativas
Seletor parou de funcionarInspecione novamente o HTML e crie testes com amostras locais
Dados duplicadosUse uma chave única, como URL ou ID, antes de salvar

Perguntas frequentes

BeautifulSoup faz a requisição para o site?

Não. Ele interpreta HTML ou XML. A requisição normalmente é feita com Requests, outra biblioteca HTTP ou um conteúdo salvo localmente.

Qual a diferença entre find e select?

find() e find_all() trabalham com nome da tag e atributos. select() e select_one() usam seletores CSS. A melhor opção é a que deixa a busca mais clara e estável.

Como saber se o scraping é permitido?

Analise os termos de uso, as políticas aplicáveis, o tipo de dado e eventuais orientações do responsável pelo site. Em caso de dúvida, solicite autorização ou use uma API oficial.

BeautifulSoup executa JavaScript?

Não. Ele analisa o HTML recebido. Conteúdo gerado no navegador pode exigir uma API, um arquivo incorporado à página ou, quando autorizado, automação de navegador.

Conclusão

Requests e BeautifulSoup formam uma combinação simples e poderosa para estudar HTML e coletar dados de páginas autorizadas. Comece com uma única página, valide cada elemento, trate erros e só depois adicione paginação e exportação. Um coletor confiável precisa ser claro, moderado e respeitar as regras da fonte.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Comparando listas e diferenças entre arrays em Python
    Automação e Scripts
    Foto de perfil de Leandro Hirt da Academify

    Compare listas no Python: diferenças e duplicatas

    Comparar dados é uma das tarefas mais comuns e fundamentais no dia a dia de qualquer programador. Seja para sincronizar

    Ler mais

    Tempo de leitura: 11 minutos
    01/05/2026
    Bot Python para monitorar queda de preço de produtos online
    Automação e Scripts
    Foto de perfil de Leandro Hirt da Academify

    Bot de alerta de preços com Python e scraping

    Você já passou pela frustração de comprar um eletrônico e, no dia seguinte, ver que o preço caiu drasticamente? Monitorar

    Ler mais

    Tempo de leitura: 8 minutos
    23/04/2026
    Como converter código Python para C usando Cython
    Automação e Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cython: compile Python para C e ganhe desempenho

    Muitos desenvolvedores escolhem o Python por sua legibilidade e vasta biblioteca, mas eventualmente se deparam com um obstáculo comum: a

    Ler mais

    Tempo de leitura: 9 minutos
    22/04/2026
    Web scraper de notícias em Python com envio para Telegram
    Automação e Scripts
    Foto de perfil de Leandro Hirt da Academify

    Scraper de notícias com Python e Telegram

    Você já sentiu a necessidade de acompanhar notícias de um site específico sem ter que abrir o navegador a cada

    Ler mais

    Tempo de leitura: 8 minutos
    16/04/2026
    Integração do ChatGPT com código Python em projeto prático
    Automação e Scripts
    Foto de perfil de Leandro Hirt da Academify

    ChatGPT no Python: integre a OpenAI API

    A inteligência artificial transformou a maneira como desenvolvemos software, e aprender como integrar o ChatGPT ao seu código Python é

    Ler mais

    Tempo de leitura: 10 minutos
    15/04/2026
    Automação em Python para organizar arquivos do computador
    Automação e Scripts
    Foto de perfil de Leandro Hirt da Academify

    Organize arquivos do PC com Python

    Você já sentiu aquela frustração de abrir a pasta de downloads e encontrar centenas de arquivos misturados? Documentos PDF perdidos

    Ler mais

    Tempo de leitura: 8 minutos
    14/04/2026