Voltar ao Glossario
Glossario Python

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
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.