Curso de Python / Extrair dados de HTML com Beautiful Soup

Use um analisador de HTML para selecionar elementos e ler seus textos ou atributos. Confira a quantidade esperada de itens e os campos necessários, pois uma mudança na estrutura pode produzir uma extração vazia que parece válida.

Começar com um documento estático controlado

Esta prática interpreta uma string HTML incluída no próprio programa e não envia requisições a sites. Beautiful Soup cria uma árvore de elementos a partir do documento recebido; o código consulta essa estrutura, em vez de tentar interpretar toda a gramática HTML com um padrão textual.

O pacote é externo. Selecione o ambiente virtual do projeto e instale-o:

python -m pip install beautifulsoup4

O nome de distribuição é beautifulsoup4, mas o nome importado é bs4. O exemplo usa html.parser, o analisador que acompanha Python, como backend. Isso é diferente de escolher outros analisadores, que podem exigir dependências e produzir diferenças em documentos malformados. Veja a documentação oficial do Beautiful Soup.

Interpretar um HTML e coletar um site externo são etapas distintas. Para uma coleta, você ainda precisa obter o documento com acesso apropriado e considerar as condições de uso, a frequência das requisições e o tratamento dos dados. O exemplo local permite estudar a extração sem depender de uma página que possa mudar.

Executar a extração completa

Salve o programa abaixo como parse_html.py, em UTF-8, em uma pasta de prática. Execute python parse_html.py no terminal com o ambiente virtual selecionado. No macOS ou Ubuntu, fora de um ambiente virtual, use python3 se esse for o nome do interpretador.

from bs4 import BeautifulSoup

html = """<ul id="expenses">
<li data-amount="3500"><span class="name">coffee</span></li>
<li data-amount="8000"><span class="name">lunch</span></li>
</ul>"""
soup = BeautifulSoup(html, "html.parser")
rows = soup.select("#expenses > li")
if len(rows) != 2:
    raise ValueError("unexpected row count")
total = 0
for row in rows:
    name = row.select_one(".name")
    amount_text = row.get("data-amount")
    if name is None or not amount_text or not amount_text.isascii() or not amount_text.isdecimal():
        raise ValueError("missing or invalid field")
    amount = int(amount_text)
    print(name.get_text(" ", strip=True), amount)
    total += amount
print("total:", total)

Resultado esperado:

coffee 3500
lunch 8000
total: 11500

O programa exige dois itens diretamente dentro da lista de despesas. Para cada item, procura o elemento do nome e o atributo data-amount. A quantia precisa conter somente algarismos decimais ASCII: o teste de isascii() restringe o conjunto de caracteres, enquanto isdecimal() verifica os algarismos. Depois, int converte o texto em inteiro e a soma é acumulada.

Esse contrato aceita zero e zeros à esquerda, mas recusa sinais, espaços ao redor do número e frações. Ele não implementa um teto geral de quantia ou tamanho de entrada. Defina esses limites separadamente se ampliar a ferramenta para dados externos.

Ler seletores e texto sem perder a estrutura

#expenses > li seleciona elementos li que são filhos diretos do elemento de ID expenses. Um seletor com espaço, como #expenses li, também alcançaria descendentes mais profundos; a diferença importa quando existem listas aninhadas. .name procura um elemento com essa classe dentro do item atual.

select_one devolve None quando não encontra um elemento. Confira essa possibilidade antes de chamar um método de texto. row.get("data-amount") lê o atributo e permite detectar sua ausência, em vez de tratar o valor como se fosse sempre uma string válida.

get_text(" ", strip=True) junta os trechos textuais com espaços e remove espaços externos de cada trecho. Se o nome contiver elementos internos, como uma palavra em negrito, essa opção ajuda a evitar palavras coladas. A presença de um elemento .name não garante que seu conteúdo seja não vazio: se isso fizer parte do contrato real, valide também o texto extraído.

Beautiful Soup enxerga somente o HTML fornecido. Se JavaScript criar outros elementos no navegador depois do carregamento, eles não passam a existir automaticamente na string analisada. Compare a resposta original com o que o navegador mostra e identifique a origem dos dados antes de escolher a forma de coleta. A aula sobre expressões regulares ajuda a distinguir padrões de texto de uma estrutura de documento.

Detectar alterações e definir a falha esperada

Encontrar zero itens pode indicar uma mudança de seletor, não uma coleção realmente vazia. A exigência de exatamente dois registros é uma regra desta amostra controlada; um coletor real precisa de limites e critérios adequados à origem, sem presumir que toda página sempre tenha dois itens.

  1. Troque a classe name do primeiro nome por outro valor. O programa deve gerar ValueError por campo ausente ou inválido.
  2. Restaure a classe e experimente -1 e abc no primeiro atributo data-amount. Ambos devem ser rejeitados; restaure 3500.
  3. Troque o texto coffee por <strong>coffee</strong> shop dentro do elemento do nome. A saída deve apresentar coffee shop 3500, preservando o segundo valor e o total. Restaure a marcação original.
  4. Altere o ID da lista e confira a falha de quantidade inesperada. Restaure-o e confirme as três linhas originais.

A validação de cada item ocorre durante o laço. Se o segundo item estiver inválido, a primeira linha pode já ter sido impressa, mas a soma final não será emitida e o processo falhará. Não use essa saída parcial como relatório completo. Se precisar publicar todos os dados de uma vez, valide e armazene os resultados antes da etapa de saída.