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?
| Biblioteca | Responsabilidade |
|---|---|
| Requests | Faz a requisição HTTP e recebe a resposta do servidor |
| BeautifulSoup | Interpreta 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 beautifulsoup4O 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: [email protected])"
)
}
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 NoneIdentifique 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")Extraindo texto, links e atributos
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: [email protected])"
)
}
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_produtosNã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.txtcomo 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
| Problema | Como evitar |
|---|---|
AttributeError ao usar get_text() | Confirme se find() ou select_one() encontrou o elemento |
| Caracteres incorretos | Verifique a codificação da resposta e salve arquivos como UTF-8 |
| Programa travado | Defina um timeout para cada requisição |
| Links incompletos | Use urljoin() para combinar URLs relativas |
| Seletor parou de funcionar | Inspecione novamente o HTML e crie testes com amostras locais |
| Dados duplicados | Use 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.






