Tecnologias e proteções 6 min de leitura

Parser do DOM: análise detalhada

O que é um parser do DOM e como funciona por dentro: construção da árvore do documento, navegação pelos nós e diferenças em relação às abordagens em fluxo e com regex.

EW
Equipe Web-Scraping.biz
Coleta de dados para as demandas do negócio
Publicado: 4 fevereiro 2025

O que é o DOM

O DOM (Document Object Model) é a representação de um documento HTML ou XML em forma de árvore de objetos. Cada tag, atributo e trecho de texto vira um nó (node), e o aninhamento das tags define a hierarquia «pai → filho».

O parser do DOM é o componente que lê a marcação (uma string ou um fluxo de bytes) e constrói a partir dela essa árvore em memória. Uma vez construída a árvore, dá para trabalhar com ela de forma programática: buscar elementos, ler atributos, modificar, remover e adicionar nós, ou serializá-la de volta para texto.

Como funciona

O parsing transcorre em duas etapas principais:

  1. Tokenização — o fluxo de caracteres é dividido em tokens (tag de abertura, tag de fechamento, texto, comentário etc.).
  2. Construção da árvore — os tokens são montados em uma estrutura hierárquica segundo as regras de aninhamento. Para HTML, valem as regras da especificação WHATWG, que, entre outras coisas, «consertam» a marcação incorreta (tags sem fechar, aninhamento errado).

Parsing DOM versus SAX

Existem duas abordagens radicalmente diferentes para analisar um documento.

Abordagem Memória Acesso Quando usar
DOM Documento inteiro em memória Acesso aleatório a qualquer nó Web scraping, processamento de configurações, a maioria das tarefas
SAX / em fluxo Consumo quase nulo Apenas sequencial, por eventos XML muito grandes (de centenas de MB a gigabytes)

DOM carrega o documento completo e oferece um confortável acesso aleatório, mas exige memória proporcional ao tamanho do documento.

SAX não constrói árvore nenhuma: gera eventos («abriu uma tag», «fechou uma tag», «apareceu texto») à medida que lê. Quase não precisa de memória, mas o código é mais difícil de escrever e não há acesso aleatório.

A maioria das bibliotecas populares listadas abaixo são parsers DOM.

Formas de buscar na árvore

Uma vez construída a árvore, os elementos costumam ser buscados de duas maneiras.

Seletores CSS — os mesmos usados na estilização de páginas: div.post > a, #main .title, ul li:first-child. São familiares aos desenvolvedores front-end e legíveis.

XPath — uma linguagem de consultas sobre a árvore, mais poderosa que CSS: //div[@class="post"]/a/@href. Admite condições, o salto para o pai (..) e a busca por texto (//a[contains(text(),"Comprar")]). É mais usado com XML, embora muitas bibliotecas HTML também o suportem.

Regra de escolha: se CSS resolve, use CSS: é mais legível. XPath é necessário quando é preciso navegar para o pai ou um ancestral, selecionar por texto ou expressar condições complexas.


Exemplos em várias linguagens

Todos os exemplos resolvem a mesma tarefa: encontrar os links dentro de div.post e exibir seu texto e seu href.

Python

As bibliotecas mais populares são a BeautifulSoup (simples, tolerante) e a lxml (rápida, baseada na biblioteca C libxml2, com suporte a XPath).

BeautifulSoup

python
from bs4 import BeautifulSoup

html = """
<html><body>
  <div class="post">
    <h2>Título</h2>
    <a href="https://example.com">Link</a>
  </div>
</body></html>
"""

soup = BeautifulSoup(html, "lxml")   # o parser pode ser trocado por "html.parser"

# Busca por tag
print(soup.find("h2").text)

# Seletores CSS
for a in soup.select("div.post a"):
    print(a.text, a.get("href"))

lxml com XPath

python
from lxml import html as lxml_html

tree = lxml_html.fromstring(html)
hrefs = tree.xpath('//div[@class="post"]/a/@href')
print(hrefs)  # ['https://example.com']

Na biblioteca padrão do Python também estão os módulos integrados html.parser e xml.dom.minidom, que não exigem instalação.

JavaScript / Node.js

No navegador, o parser do DOM já vem integrado — DOMParser e querySelector:

javascript
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");

doc.querySelectorAll("div.post a").forEach(a => {
  console.log(a.textContent, a.href);
});

No Node.js, usam-se o Cheerio (leve, com API no estilo jQuery) ou o jsdom (emulação completa do DOM).

javascript
import * as cheerio from "cheerio";

const $ = cheerio.load(html);
$("div.post a").each((i, el) => {
  console.log($(el).text(), $(el).attr("href"));
});

jsdom

javascript
import { JSDOM } from "jsdom";

const dom = new JSDOM(html);
dom.window.document.querySelectorAll("div.post a")
  .forEach(a => console.log(a.textContent, a.href));

Java

O padrão de facto é o jsoup, muito prático para HTML, com seletores CSS.

java
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

Document doc = Jsoup.parse(html);
for (Element link : doc.select("div.post a")) {
    System.out.println(link.text() + " " + link.attr("href"));
}

Para o XML estrito, o JDK inclui o JAXP (javax.xml.parsers.DocumentBuilder), um parser DOM nativo, além de SAX/StAX para a análise em fluxo.

PHP

A classe integrada DOMDocument, com suporte a XPath

php
$dom = new DOMDocument();
@$dom->loadHTML($html);          // @ silencia os avisos do HTML «sujo»
$xpath = new DOMXPath($dom);

foreach ($xpath->query('//div[@class="post"]/a') as $node) {
    echo $node->textContent . ' ' . $node->getAttribute('href') . PHP_EOL;
}

A alternativa com seletores CSS é o Symfony DomCrawler

C# / .NET

HtmlAgilityPack — o clássico, trabalha com XPath

c#
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);

foreach (var link in doc.DocumentNode.SelectNodes("//div[@class='post']/a"))
{
    Console.WriteLine(link.InnerText + " " + link.GetAttributeValue("href", ""));
}

AngleSharp — mais moderno, com um parsing fiel à especificação e seletores CSS

c#
var context = AngleSharp.BrowsingContext.New(AngleSharp.Configuration.Default);
var doc = await context.OpenAsync(req => req.Content(html));

foreach (var a in doc.QuerySelectorAll("div.post a"))
    Console.WriteLine(a.TextContent + " " + a.GetAttribute("href"));

Go

O goquery oferece uma API no estilo jQuery

go
import (
    "fmt"
    "strings"
    "github.com/PuerkitoBio/goquery"
)

doc, _ := goquery.NewDocumentFromReader(strings.NewReader(html))
doc.Find("div.post a").Each(func(i int, s *goquery.Selection) {
    href, _ := s.Attr("href")
    fmt.Println(s.Text(), href)
})

Por baixo, o goquery utiliza o pacote oficial de baixo nível golang.org/x/net/html, que também pode ser usado diretamente.

Ruby

Nokogiri — suporta tanto CSS quanto XPath

ruby
require "nokogiri"

doc = Nokogiri::HTML(html)
doc.css("div.post a").each do |link|
  puts "#{link.text} #{link['href']}"
end

Tabela comparativa de bibliotecas

Linguagem Biblioteca Seletores Particularidades
Python BeautifulSoup CSS Simples, tolera erros de marcação
Python lxml CSS + XPath Rápida (backend C libxml2)
JS (navegador) DOMParser CSS Integrada ao navegador
Node.js Cheerio CSS Leve, no estilo jQuery
Node.js jsdom CSS Emulação completa do DOM
Java jsoup CSS Padrão de facto para HTML
PHP DOMDocument XPath Integrada ao PHP
C# HtmlAgilityPack XPath O clássico do ecossistema .NET
C# AngleSharp CSS Moderno, parsing correto
Go goquery CSS No estilo jQuery
Ruby Nokogiri CSS + XPath Rápida (backend em C)

Observações práticas

O HTML é «sujo» por natureza. A marcação real costuma conter tags sem fechar e erros de aninhamento. Os parsers voltados para HTML (BeautifulSoup, jsoup, AngleSharp, Nokogiri em modo HTML) os toleram e corrigem. Os parsers XML estritos, diante de uma entrada dessas, falham: para XML, é preciso uma marcação válida.

Parser ≠ navegador. As bibliotecas mencionadas analisam apenas o HTML original que chegou do servidor. Se o conteúdo é carregado por JavaScript já no navegador, um parser do DOM comum não vai vê-lo. Para esses sites, são necessários navegadores headless:

Primeiro eles executam os scripts e depois entregam o DOM já pronto.

Desempenho. Os parsers em C (lxml, o backend libxml2 do Nokogiri) são uma ordem de grandeza mais rápidos que as implementações puras em Python ou Ruby. Para XML enormes, escolha SAX/StAX em fluxo em vez de DOM, para não esbarrar na memória.

Escolha de seletores. Se CSS resolve, use CSS: é mais legível. XPath é necessário para navegar até o pai ou um ancestral, selecionar por texto ou expressar condições complexas.