BeautifulSoup: O que É e Como Funciona | Python Brasil
Aprenda web scraping com BeautifulSoup em Python: parsing de HTML, busca de elementos, extracao de dados e boas praticas para coletar informacoes da web.
O que e BeautifulSoup?
BeautifulSoup e uma biblioteca Python para parsing (analise sintatica) de documentos HTML e XML. Ela cria uma arvore de navegacao que permite buscar, navegar e extrair dados de paginas web de forma simples e intuitiva. BeautifulSoup e a ferramenta mais popular para web scraping em Python — a pratica de extrair dados de sites automaticamente.
A biblioteca nao faz requisicoes HTTP por conta propria. Ela trabalha em conjunto com bibliotecas como requests para obter o HTML e entao analisa-lo e extrai as informacoes desejadas.
Instalacao e Primeiro Uso
# Instalacao
# pip install beautifulsoup4 requests lxml
from bs4 import BeautifulSoup
import requests
# Obter HTML de uma pagina
response = requests.get('https://exemplo.com')
soup = BeautifulSoup(response.text, 'lxml')
# Parser a partir de string HTML
html = """
<html>
<head><title>Minha Pagina</title></head>
<body>
<h1>Bem-vindo</h1>
<p class="destaque">Paragrafo importante</p>
<p>Outro paragrafo</p>
<ul>
<li><a href="/link1">Link 1</a></li>
<li><a href="/link2">Link 2</a></li>
</ul>
</body>
</html>
"""
soup = BeautifulSoup(html, 'lxml')
print(soup.title.string) # 'Minha Pagina'
Buscando Elementos
from bs4 import BeautifulSoup
html = """
<div class="container">
<h2 id="titulo">Produtos</h2>
<div class="produto" data-id="1">
<span class="nome">Notebook</span>
<span class="preco">R$ 3.500,00</span>
</div>
<div class="produto" data-id="2">
<span class="nome">Mouse</span>
<span class="preco">R$ 89,90</span>
</div>
<div class="produto destaque" data-id="3">
<span class="nome">Teclado</span>
<span class="preco">R$ 199,00</span>
</div>
</div>
"""
soup = BeautifulSoup(html, 'lxml')
# find — retorna o primeiro elemento encontrado
primeiro_produto = soup.find('div', class_='produto')
print(primeiro_produto.find('span', class_='nome').string) # 'Notebook'
# find_all — retorna todos os elementos
produtos = soup.find_all('div', class_='produto')
print(f'Total de produtos: {len(produtos)}') # 3
for produto in produtos:
nome = produto.find('span', class_='nome').string
preco = produto.find('span', class_='preco').string
print(f'{nome}: {preco}')
# Busca por ID
titulo = soup.find(id='titulo')
print(titulo.string) # 'Produtos'
# Busca por atributo customizado
produto_2 = soup.find('div', attrs={'data-id': '2'})
print(produto_2.find('span', class_='nome').string) # 'Mouse'
Seletores CSS
from bs4 import BeautifulSoup
# select — usa seletores CSS (retorna lista)
produtos = soup.select('div.produto')
nomes = soup.select('div.produto span.nome')
destaque = soup.select('div.produto.destaque')
# select_one — retorna o primeiro match
titulo = soup.select_one('#titulo')
primeiro_preco = soup.select_one('.produto .preco')
# Seletores avancados
links = soup.select('a[href^="/"]') # links que comecam com /
itens = soup.select('ul > li') # filhos diretos
terceiro = soup.select('li:nth-of-type(3)') # terceiro li
Navegacao pela Arvore
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
produto = soup.find('div', class_='produto')
# Filhos
for filho in produto.children:
if filho.name: # ignora espacos em branco (NavigableString)
print(filho.name, filho.string)
# Pai
pai = produto.parent
print(pai.name) # 'div' (container)
# Irmaos
proximo = produto.find_next_sibling('div')
anterior = produto.find_previous_sibling('div')
# Descendentes
for desc in produto.descendants:
if desc.string and desc.string.strip():
print(desc.string.strip())
Extraindo Dados
from bs4 import BeautifulSoup
# Obter texto
elemento = soup.find('div', class_='container')
texto_completo = elemento.get_text(separator=' ', strip=True)
print(texto_completo)
# Obter atributos
link = soup.find('a')
href = link.get('href') # ou link['href']
classes = link.get('class', []) # retorna lista
# Extrair todos os links
links = []
for a in soup.find_all('a', href=True):
links.append({
'texto': a.get_text(strip=True),
'url': a['href'],
})
# Extrair tabela para lista de dicionarios
def extrair_tabela(soup, seletor='table'):
tabela = soup.select_one(seletor)
if not tabela:
return []
cabecalhos = [th.get_text(strip=True) for th in tabela.select('thead th')]
linhas = []
for tr in tabela.select('tbody tr'):
valores = [td.get_text(strip=True) for td in tr.select('td')]
linhas.append(dict(zip(cabecalhos, valores)))
return linhas
Exemplo Completo: Scraping de Noticias
import requests
from bs4 import BeautifulSoup
import csv
import time
def coletar_noticias(url: str) -> list[dict]:
"""Coleta noticias de uma pagina."""
headers = {'User-Agent': 'Mozilla/5.0 (Python Scraper)'}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml')
noticias = []
for artigo in soup.select('article.noticia'):
titulo = artigo.select_one('h2')
resumo = artigo.select_one('p.resumo')
link = artigo.select_one('a')
if titulo and link:
noticias.append({
'titulo': titulo.get_text(strip=True),
'resumo': resumo.get_text(strip=True) if resumo else '',
'url': link.get('href', ''),
})
return noticias
def salvar_csv(noticias: list[dict], arquivo: str):
"""Salva noticias em CSV."""
if not noticias:
return
with open(arquivo, 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=noticias[0].keys())
writer.writeheader()
writer.writerows(noticias)
Erros Comuns
O erro mais frequente e nao especificar um parser, gerando um aviso e comportamento inconsistente entre ambientes. Sempre passe 'lxml' ou 'html.parser' explicitamente. Outro erro e acessar .string em elementos que possuem tags filhas — nesse caso, .string retorna None e voce deve usar .get_text(). Tambem e comum nao tratar o caso em que find() retorna None, causando AttributeError ao encadear chamadas. Esquecer de respeitar o robots.txt e os termos de servico dos sites e uma questao etica e legal importante.
Boas Praticas
Sempre use lxml como parser — e mais rapido e tolerante a HTML mal-formado. Trate sempre o caso de None retornado por find(). Adicione delays entre requisicoes para nao sobrecarregar o servidor. Defina um User-Agent adequado. Verifique o robots.txt e os termos de uso do site. Para sites com JavaScript dinamico, considere Selenium ou Playwright.
Quando Usar
BeautifulSoup e ideal para extrair dados de paginas HTML estaticas, parsing de feeds RSS/XML, limpeza de HTML e qualquer tarefa de web scraping em paginas que nao dependem de JavaScript. Para sites dinamicos que carregam conteudo via JavaScript, use Selenium ou Playwright.