html.parser no Python: analise HTML

Publicado em: 20/08/2026
Tempo de leitura: 6 minutos
Código HTML em uma tela representando análise com html.parser no Python

O módulo html.parser oferece um parser de HTML e XHTML baseado em eventos. Você cria uma subclasse de HTMLParser e implementa métodos chamados quando aparecem tags de abertura, tags de fechamento, texto, comentários, entidades e declarações.

Ele é leve, faz parte da biblioteca padrão e tolera muitos documentos malformados encontrados na web. É útil para extrair links, títulos, texto e metadados de páginas simples, criar validadores específicos e processar HTML em streaming. Porém, não constrói uma árvore DOM completa, não executa JavaScript e não sanitiza conteúdo.

Como HTMLParser funciona

O parser recebe texto por feed(). Quando reconhece elementos completos, chama handlers sobrescritos pela subclasse. Dados incompletos ficam em buffer até a próxima chamada ou até close().

from html.parser import HTMLParser

class DebugParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("abertura", tag, attrs)

    def handle_endtag(self, tag):
        print("fechamento", tag)

    def handle_data(self, data):
        print("texto", repr(data))

parser = DebugParser()
parser.feed("<h1>Olá &amp; Python</h1>")
parser.close()

Por padrão, convert_charrefs=True transforma entidades como &amp; no caractere correspondente, exceto em contextos especiais como script e style.

from html.parser import HTMLParser
from urllib.parse import urljoin

class LinkParser(HTMLParser):
    def __init__(self, base_url: str):
        super().__init__()
        self.base_url = base_url
        self.links: list[str] = []

    def handle_starttag(self, tag, attrs):
        if tag != "a":
            return

        attributes = dict(attrs)
        href = attributes.get("href")
        if href:
            self.links.append(urljoin(self.base_url, href))

parser = LinkParser("https://example.com/docs/")
parser.feed('<a href="python.html">Python</a>')
parser.close()
print(parser.links)

Os nomes de tags e atributos são convertidos para minúsculas. As aspas dos valores são removidas e atributos sem valor recebem None. Use urllib.parse no Python para resolver e validar URLs.

Valide URLs extraídas

Um link pode usar javascript:, data:, file: ou apontar para uma origem inesperada. O parser apenas devolve a string; ele não decide se é segura.

from urllib.parse import urlsplit

parts = urlsplit(candidate)
if parts.scheme not in {"http", "https"}:
    return
if parts.hostname not in {"example.com", "www.example.com"}:
    return

Ao baixar os destinos, aplique proteção contra SSRF, redirects, limites e timeouts conforme o guia de urllib.request no Python.

Extraindo texto visível

handle_data() recebe texto comum, mas também o conteúdo de script e style. Para criar texto de leitura, acompanhe o contexto atual.

class TextParser(HTMLParser):
    ignored = {"script", "style", "noscript"}

    def __init__(self):
        super().__init__()
        self.skip_depth = 0
        self.parts: list[str] = []

    def handle_starttag(self, tag, attrs):
        if tag in self.ignored:
            self.skip_depth += 1

    def handle_endtag(self, tag):
        if tag in self.ignored and self.skip_depth:
            self.skip_depth -= 1

    def handle_data(self, data):
        if self.skip_depth == 0:
            cleaned = " ".join(data.split())
            if cleaned:
                self.parts.append(cleaned)

    def text(self) -> str:
        return " ".join(self.parts)

HTML malformado pode fechar tags em ordem inesperada. Um contador simples funciona para casos controlados, mas não substitui um parser com árvore e regras completas do HTML5.

Extraindo título e meta description

class MetadataParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.in_title = False
        self.title_parts = []
        self.description = None

    def handle_starttag(self, tag, attrs):
        attributes = dict(attrs)
        if tag == "title":
            self.in_title = True
        elif tag == "meta" and attributes.get("name", "").lower() == "description":
            self.description = attributes.get("content")

    def handle_endtag(self, tag):
        if tag == "title":
            self.in_title = False

    def handle_data(self, data):
        if self.in_title:
            self.title_parts.append(data)

    @property
    def title(self):
        return " ".join("".join(self.title_parts).split())

Páginas podem conter múltiplas tags, valores vazios ou metadados gerados por JavaScript. Defina uma política de seleção e limite o tamanho acumulado.

Processamento incremental

feed() aceita fragmentos. O parser mantém tags incompletas no buffer.

parser = LinkParser("https://example.com/")

for chunk in ["<a hr", 'ef="/docs">Doc', "umentação</a>"]:
    parser.feed(chunk)

parser.close()

Isso combina com leitura em blocos e evita guardar a página inteira. Porém, o argumento de feed() precisa ser str. Use um decoder incremental para transformar bytes sem cortar uma sequência UTF-8. O guia de codecs no Python explica esse fluxo.

Decoder incremental com limite

import codecs

parser = TextParser()
decoder = codecs.getincrementaldecoder("utf-8")(errors="replace")
total = 0

while chunk := response.read(64 * 1024):
    total += len(chunk)
    if total > 5 * 1024 * 1024:
        raise ValueError("HTML muito grande")
    parser.feed(decoder.decode(chunk))

parser.feed(decoder.decode(b"", final=True))
parser.close()

Determine o encoding a partir de um contexto confiável. Declarações HTML, headers e BOM podem discordar. Para scraping complexo, uma biblioteca que implemente as regras de encoding do HTML pode ser mais adequada.

Comentários e declarações

handle_comment() recebe comentários sem os delimitadores. handle_decl() trata declarações como DOCTYPE.

class AuditParser(HTMLParser):
    def handle_comment(self, data):
        if "TODO" in data:
            print("comentário de revisão encontrado")

    def handle_decl(self, decl):
        print("declaração", decl)

Não confie em comentários para esconder segredos; eles são enviados ao cliente e facilmente lidos.

Entidades de caracteres

Com convert_charrefs=True, entidades nomeadas e numéricas são convertidas automaticamente. Quando você precisa observar a forma original, crie o parser com convert_charrefs=False e implemente handle_entityref() e handle_charref().

A conversão de entidade não sanitiza HTML. O texto &lt;script&gt; pode tornar-se uma string contendo sinais de menor e maior; se depois você o inserir como HTML sem escape, pode criar uma vulnerabilidade.

O parâmetro scripting

Desde Python 3.14.1, HTMLParser aceita scripting. Quando verdadeiro, o conteúdo de noscript é entregue como texto bruto em vez de ser processado como markup. Isso aproxima o comportamento do caso em que scripting está habilitado, mas não executa JavaScript.

parser = TextParser(scripting=True)

Se sua subclasse define __init__, aceite e encaminhe os argumentos necessários para super().__init__().

HTML inválido e limites do parser

O parser tolera markup inválido, mas não verifica se tags de fechamento correspondem às de abertura e não gera automaticamente eventos de fechamento implícito. Uma página com <p><b>texto</p> pode produzir eventos que não representam a árvore construída por um navegador.

Para seletores CSS, manipulação estrutural e fidelidade ao HTML5, use Beautiful Soup, lxml ou html5lib conforme o projeto. O artigo de web scraping com Beautiful Soup mostra uma abordagem baseada em árvore.

Parsing não é sanitização

HTMLParser não remove scripts, URLs perigosas, atributos de evento ou CSS malicioso. Não use a classe sozinha para permitir HTML de usuários em uma página.

Sanitização exige uma allowlist de tags, atributos e protocolos, regras de contexto e uma biblioteca mantida para segurança. Mesmo depois de sanitizar, aplique escape no contexto correto ao renderizar.

Não execute conteúdo extraído

Nunca passe texto de script a eval(), shell ou interpretador. Não abra automaticamente links encontrados e não use valores de atributos como caminhos locais sem validação. Trate todo HTML externo como não confiável.

Proteção de recursos

Limite bytes baixados, quantidade de links, tamanho de cada atributo e texto acumulado. Um documento pode conter milhões de tags ou um atributo enorme.

if len(self.links) > 10_000:
    raise ValueError("Links demais")
if href and len(href) > 4_096:
    return

O parser não oferece uma política global de recursos; sua subclasse precisa aplicá-la.

Reutilização e reset

Crie uma instância por documento ou chame reset() e limpe também todo estado definido pela subclasse. Esquecer listas e flags mistura resultados de páginas diferentes.

Testando o parser

Inclua casos com atributos sem aspas, entidades, comentários, tags divididas entre chunks, tags mal fechadas, scripts, URLs relativas e valores vazios. Teste limites e certifique-se de que close() seja chamado.

Erros comuns

Os erros frequentes são alimentar bytes em vez de texto, esquecer close(), assumir uma árvore HTML5 correta, extrair texto de script, seguir qualquer URL, não limitar tamanho, reutilizar estado, confundir entidades convertidas com sanitização e renderizar texto extraído como HTML.

Boas práticas

Use uma instância por documento, decoder incremental, limites de bytes e eventos, validação de URLs e tratamento explícito de scripts. Escolha uma biblioteca de árvore quando precisar de estrutura completa. Para conteúdo de usuário, use sanitizador dedicado e escape contextual.

Conclusão

html.parser é uma ferramenta leve e útil para extração orientada a eventos. Ele processa HTML em blocos, tolera markup imperfeito e permite coletar texto, links e metadados sem dependências. Seus limites precisam ser claros: não é navegador, não é parser HTML5 completo e não é sanitizador de segurança.

Consulte a documentação oficial de html.parser e a especificação viva do HTML.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Pessoa usando laptop em uma sessão web representando cookies com http.cookiejar no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    http.cookiejar no Python: gerencie cookies

    Aprenda http.cookiejar no Python para manter sessões, aplicar políticas, persistir cookies com segurança e integrar com urllib.request.

    Ler mais

    Tempo de leitura: 6 minutos
    20/08/2026
    Rack de servidores representando conexões HTTP de baixo nível com http.client no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    http.client no Python: HTTP de baixo nível

    Aprenda http.client no Python para controlar conexões HTTP e HTTPS, streaming, headers, TLS, reutilização, limites e erros de protocolo.

    Ler mais

    Tempo de leitura: 5 minutos
    20/08/2026
    Código HTML em uma tela representando crawling responsável com robotparser no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    robotparser no Python: leia robots.txt

    Aprenda urllib.robotparser no Python para respeitar robots.txt, crawl-delay, request-rate, sitemaps, cache e limites de crawling.

    Ler mais

    Tempo de leitura: 5 minutos
    20/08/2026
    Teclas formando HTTP representando requisições com urllib.request no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    urllib.request no Python: HTTP nativo

    Aprenda urllib.request no Python para fazer GET, POST e downloads com timeout, TLS, redirects, proxies, limites e tratamento de erros.

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Cabos Ethernet conectados representando servidores de rede com socketserver no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    socketserver no Python: crie servidores

    Aprenda socketserver no Python para criar servidores TCP e UDP, aplicar concorrência, limites, timeouts e encerramento seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Sala de servidores iluminada representando conexões TLS seguras com ssl no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ssl no Python: conexões TLS seguras

    Aprenda ssl no Python para criar clientes e servidores TLS, validar certificados, configurar versões mínimas, CA e autenticação mútua.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026