Extração de dados

Detecção de Mudanças em Escala: Comparando Páginas Web Sem Se Afogar em Ruído

Páginas mudam a cada carregamento: anúncios, timestamps, tokens. Como distinguir mudanças reais de ruído com normalização, diffs em nível de campo e simhash, com código testado.

Chris Collins

Chris Collins

27 de setembro de 2026 · 10 min de leitura

Todo trabalho de monitoramento eventualmente faz a mesma pergunta: essa página mudou? Parece uma comparação de hash. Não é. Páginas modernas mudam a cada carregamento, com anúncios rotativos, timestamps, blocos de recomendação, tokens de sessão e contadores ao vivo, então uma comparação ingênua reporta uma mudança toda vez e o canal de alertas se enche de ruído até que todo mundo o silencie. A falha oposta é mais silenciosa e pior: um pipeline tão ajustado para ignorar ruído que perde o corte de preço ou o certificado removido que ele foi construído para detectar.

Este guia cobre como detectar mudanças reais em escala: comparar campos em vez de páginas, normalizar o que você não pode evitar comparar, medir o quanto uma página mudou em vez de se ela mudou, e encaminhar cada tipo de mudança para o lugar certo.

Principais conclusões

  • Comparação bruta de bytes é inútil para a maioria dos sites. Buscamos a página inicial de um site de notícias duas vezes, com 45 segundos de intervalo: os bytes diferiram, e até o texto limpo diferiu.
  • Compare campos, não páginas, sempre que possível. Um preço, um título ou um status de estoque ou mudou ou não mudou.
  • Onde você precisa comparar páginas inteiras, normalize primeiro, depois meça a similaridade. Simhash, a técnica de fingerprinting que o Google descreveu para detecção de quase-duplicatas em 8 bilhões de páginas, transforma “mudou?” em “quanto mudou?”
  • Classifique toda mudança: mudança de campo, mudança de conteúdo ou ruído. Cada uma merece uma resposta diferente.
  • Ajuste por site e por tipo de página. Um limiar que é correto para uma página de produto está errado para uma página inicial de notícias.

Por que a comparação ingênua falha

Para ver o problema de forma concreta, buscamos a página inicial internacional de um grande site de notícias duas vezes, com 45 segundos de intervalo. O HTML bruto diferiu, como esperado. Mais interessante: depois de remover scripts, estilos e marcação, e remover timestamps e tokens, o texto visível ainda diferiu. Páginas ao vivo se atualizam continuamente. Um monitor que alerta em qualquer diferença teria alertado sobre essa página em toda execução.

As fontes de ruído são previsíveis:

RuídoExemploCorreção típica
Scripts e dados incorporadosPayloads de analytics, estado JSON, tokens CSRFRemover blocos de script e style antes de comparar
Texto baseado em tempo”Atualizado há 3 minutos”, horários, datasRemover com padrões, ou comparar campos que os excluam
Módulos rotativosAnúncios, “em alta agora”, recomendaçõesComparar apenas a região ou os campos que importam
Personalização e experimentosVariantes de teste A/B, blocos específicos por localizaçãoColetar de um ponto de vista fixo com configurações consistentes
OrdenaçãoListas que embaralham a cada carregamentoOrdenar antes de comparar

Algum ruído é na verdade uma diferença em quem perguntou. Uma página carregada da Alemanha pode diferir da mesma página carregada dos Estados Unidos por razões que nada têm a ver com uma mudança. Mantenha o ponto de vista fixo para cada página monitorada, para que a única coisa que varie entre execuções seja o tempo. Com um gateway residencial, isso significa fixar o país, por exemplo customer-USERNAME-country-de, para toda execução daquele trabalho.

Princípio 1: compare campos, não páginas

O detector de mudanças mais confiável não compara páginas de forma alguma. Ele extrai os campos que importam, como preço, disponibilidade, título, uma lista de certificados ou um endereço, e compara esses valores. Um campo mudou ou não mudou, e o ruído em outras partes da página é irrelevante.

Dados estruturados tornam isso mais fácil do que parece. Muitas páginas publicam seus campos-chave como JSON-LD, que é muito mais estável do que o layout ao redor; extraí-lo é abordado em pare de analisar HTML. Onde os campos vêm de seletores em vez disso, compare os valores extraídos, nunca o HTML ao redor deles.

A comparação de campos também torna os alertas úteis. “O preço mudou de 89.00 para 79.00” é acionável. “A página mudou” não é.

Princípio 2: normalize o que você precisa comparar

Algum monitoramento realmente é sobre páginas inteiras: termos de serviço, a página “sobre” de um fornecedor, uma declaração de sustentabilidade, uma página de política. Para esses casos, remova o que nunca importa antes de comparar:

  • Remova blocos que não são conteúdo: scripts, estilos, templates, SVG inline.
  • Mantenha apenas o texto visível, com espaços em branco colapsados e maiúsculas/minúsculas normalizadas.
  • Remova fragmentos voláteis: horários, datas ISO, horários relativos, tokens e identificadores hexadecimais longos.
  • Restrinja a uma região quando uma página tem uma área de conteúdo principal estável, como o corpo do artigo ou o elemento principal.

A normalização remove uma grande parte do ruído. Ela não remove tudo, e é por isso que o próximo passo importa.

Princípio 3: meça o quanto, não se

Em vez de perguntar se o texto normalizado é idêntico, pergunte quão similar ele é. Simhash é uma boa ferramenta para isso. Ele reduz um documento a uma impressão digital de 64 bits com uma propriedade útil: documentos similares recebem impressões digitais que diferem em apenas algumas posições de bit. O Google descreveu o uso disso para detecção de quase-duplicatas em “Detecting Near-Duplicates for Web Crawling” (WWW 2007), onde os autores validaram que “for a repository of 8B web-pages, 64-bit simhash fingerprints and k = 3 are reasonable”, ou seja, páginas cujas impressões digitais diferem em no máximo três bits poderiam ser tratadas como quase-duplicatas.

Isso transforma a detecção de mudanças em uma questão de distância. Em nosso teste, as duas buscas da página inicial de notícias, com 45 segundos de intervalo, resultaram em uma diferença de 2 bits: abaixo do limiar, portanto ruído. A mesma página inicial comparada com um artigo não relacionado resultou em uma diferença de 34 bits: claramente conteúdo diferente.

import hashlib
import re

VOLATILE = [
    r"\b\d{1,2}:\d{2}(?::\d{2})?\s?(?:am|pm|AM|PM)?\b",          # clock times
    r"\b\d{4}-\d{2}-\d{2}(?:T[\d:.]+Z?)?\b",                      # ISO dates
    r"\b\d+\s+(?:second|minute|hour|day)s?\s+ago\b",              # relative times
    r"\b(?:csrf|nonce|token|session|sid)[\w-]*[=:]\s*[\w-]+",      # tokens
    r"\b[0-9a-f]{24,}\b",                                         # long hex ids
]


def normalise(html):
    """Visible text only, with volatile fragments removed."""
    html = re.sub(r"(?is)<(script|style|noscript|svg|template)\b.*?</\1>", " ", html)
    text = re.sub(r"(?s)<[^>]+>", " ", html)
    for pattern in VOLATILE:
        text = re.sub(pattern, " ", text, flags=re.I)
    return re.sub(r"\s+", " ", text).strip().lower()


def simhash(text, bits=64):
    """Charikar-style simhash over word 3-grams."""
    words = text.split()
    grams = [" ".join(words[i:i + 3]) for i in range(max(1, len(words) - 2))]
    weights = [0] * bits
    for gram in grams:
        h = int.from_bytes(hashlib.blake2b(gram.encode(), digest_size=8).digest(), "big")
        for i in range(bits):
            weights[i] += 1 if h >> i & 1 else -1
    return sum(1 << i for i in range(bits) if weights[i] > 0)


def distance(a, b):
    return bin(a ^ b).count("1")


def compare(old_html, new_html, old_fields=None, new_fields=None, threshold=3):
    """Classify a change as none, noise, content or field-level."""
    old_fields, new_fields = old_fields or {}, new_fields or {}
    field_changes = {k: (old_fields.get(k), new_fields.get(k))
                     for k in set(old_fields) | set(new_fields)
                     if old_fields.get(k) != new_fields.get(k)}
    if field_changes:
        return {"kind": "field", "changes": field_changes}
    if old_html == new_html:
        return {"kind": "none"}
    d = distance(simhash(normalise(old_html)), simhash(normalise(new_html)))
    return {"kind": "content" if d > threshold else "noise", "distance": d}

Trate o limiar como um ponto de partida, não como uma constante. O número do WWW 2007 foi escolhido para encontrar duplicatas entre bilhões de páginas, não para monitorar uma página ao longo do tempo. Calibre-o por tipo de página: busque cada página monitorada várias vezes em rápida sucessão, onde nada significativo deveria mudar, e defina o limiar logo acima das distâncias observadas. Páginas curtas exigem mais cuidado, porque algumas palavras alteradas movem a impressão digital de um documento pequeno mais do que a de um longo.

Princípio 4: classifique, depois encaminhe

Um detector que retorna apenas “mudou” empurra o trabalho real para quem lê o alerta. Retorne um tipo, e encaminhe conforme ele:

TipoSignificadoPara onde vai
fieldUm valor que você acompanha mudouDireto para o conjunto de dados e, se importar, um alerta
contentA substância da página mudou além do ruídoUma fila de revisão humana, com um diff de texto anexado
noiseA página se moveu, mas dentro da variação normalRegistrado para calibração, nunca alertado
noneIdêntico byte a byteNada

Dois refinamentos se pagam sozinhos. Mantenha o snapshot anterior e um diff de texto legível junto a toda mudança content, para que um revisor possa julgá-la em segundos. E registre a taxa de cada tipo por site: um site cuja distância de ruído aumenta gradualmente ao longo de semanas está mudando seus templates, e um site que de repente não produz nenhuma mudança pode estar servindo uma página desatualizada ou bloqueada, um modo de falha abordado em a taxa de falha silenciosa.

Escalando isso

Detecção de mudanças em escala é principalmente um problema de armazenamento e agendamento.

  • Armazene impressões digitais e campos, não páginas completas, para a maioria das execuções. Uma impressão digital de 64 bits e alguns campos são minúsculos. Mantenha snapshots completos apenas quando uma mudança é detectada, ou em um cronograma mais lento para auditoria.
  • Revisite conforme a mudança esperada. Páginas que raramente mudam não precisam de verificações a cada hora. Agendamento de crawl consciente de custo estabelece como gastar um orçamento de busca onde a mudança é provável, e todo resultado none ou noise é evidência para esse modelo.
  • Use sinais baratos primeiro. Onde um site envia cabeçalhos confiáveis de ETag ou Last-Modified, uma requisição condicional que retorna 304 Not Modified responde à pergunta com quase nenhuma banda.
  • Observe o observador. Páginas canário com padrões de mudança conhecidos dizem se o próprio detector ainda funciona.

Onde isso é usado

O mesmo mecanismo sustenta trabalhos muito diferentes: monitoramento de preço e estoque, onde mudanças de campo são todo o objetivo, como em construindo um feed de preços competitivos em tempo real; monitoramento de fornecedores e conformidade, onde mudanças de conteúdo de página inteira importam, como em monitorando a pegada pública dos seus fornecedores e verificando alegações ESG; e preservando evidências quando uma mudança é juridicamente significativa.

Conclusão

“Essa página mudou?” é a pergunta errada para a web moderna, porque a resposta é quase sempre sim. As perguntas úteis são “um campo que me importa mudou?” e, onde você precisa comparar páginas inteiras, “a substância mudou além da variação normal desta página?”

Compare campos sempre que possível. Normalize o que você não pode evitar comparar, meça similaridade em vez de igualdade, calibre limiares por tipo de página, e encaminhe cada tipo de mudança para o lugar que pode agir sobre ela. O resultado é um feed de mudanças em que as pessoas confiam, que é o único tipo que é realmente lido.

Fontes e referências

Pronto para começar?

Experimente os proxies residenciais da Shifter, mais de 205M IPs, mais de 195 países, a partir de $ 0,75/GB.

Começar