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ído | Exemplo | Correção típica |
|---|---|---|
| Scripts e dados incorporados | Payloads de analytics, estado JSON, tokens CSRF | Remover blocos de script e style antes de comparar |
| Texto baseado em tempo | ”Atualizado há 3 minutos”, horários, datas | Remover com padrões, ou comparar campos que os excluam |
| Módulos rotativos | Anúncios, “em alta agora”, recomendações | Comparar apenas a região ou os campos que importam |
| Personalização e experimentos | Variantes de teste A/B, blocos específicos por localização | Coletar de um ponto de vista fixo com configurações consistentes |
| Ordenação | Listas que embaralham a cada carregamento | Ordenar 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:
| Tipo | Significado | Para onde vai |
|---|---|---|
field | Um valor que você acompanha mudou | Direto para o conjunto de dados e, se importar, um alerta |
content | A substância da página mudou além do ruído | Uma fila de revisão humana, com um diff de texto anexado |
noise | A página se moveu, mas dentro da variação normal | Registrado para calibração, nunca alertado |
none | Idêntico byte a byte | Nada |
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
noneounoiseé evidência para esse modelo. - Use sinais baratos primeiro. Onde um site envia cabeçalhos confiáveis de
ETagouLast-Modified, uma requisição condicional que retorna304 Not Modifiedresponde à 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
- Manku, Jain e Das Sarma, Detecting Near-Duplicates for Web Crawling, WWW 2007.
- Moses Charikar, Similarity Estimation Techniques from Rounding Algorithms, STOC 2002. A origem do simhash.
- Shifter, documentação de geo-targeting de Proxies Residenciais.