Chaque tâche de surveillance finit par poser la même question : est-ce que cette page a changé ? Cela ressemble à une comparaison de hachage. Ce n’en est pas une. Les pages modernes changent à chaque chargement, avec des publicités tournantes, des horodatages, des blocs de recommandation, des jetons de session et des compteurs en direct, si bien qu’une comparaison naïve signale un changement à chaque fois et le canal d’alerte se remplit de bruit jusqu’à ce que tout le monde le désactive. L’échec inverse est plus silencieux et pire : un pipeline tellement calibré pour ignorer le bruit qu’il rate la baisse de prix ou le certificat supprimé qu’il était censé détecter.
Ce guide explique comment détecter les changements réels à grande échelle : comparer des champs plutôt que des pages, normaliser ce qu’on ne peut pas éviter de comparer, mesurer à quel point une page a changé plutôt que si elle a changé, et acheminer chaque type de changement vers le bon endroit.
Points clés
- La comparaison brute d’octets est inutile pour la plupart des sites. Nous avons récupéré la page d’accueil d’un site d’actualités deux fois, à 45 secondes d’intervalle : les octets différaient, et même le texte nettoyé différait.
- Comparez des champs, pas des pages, chaque fois que possible. Un prix, un titre ou un statut de stock a changé ou n’a pas changé.
- Là où vous devez comparer des pages entières, normalisez d’abord, puis mesurez la similarité. Simhash, la technique d’empreinte digitale décrite par Google pour la détection de quasi-doublons parmi 8 milliards de pages, transforme “est-ce que ça a changé ?” en “à quel point est-ce que ça a changé ?”
- Classez chaque changement : changement de champ, changement de contenu ou bruit. Chacun mérite une réponse différente.
- Ajustez par site et par type de page. Un seuil adapté à une page produit est inadapté à une page d’accueil d’actualités.
Pourquoi la comparaison naïve échoue
Pour voir le problème concrètement, nous avons récupéré la page d’accueil internationale d’un grand site d’actualités deux fois, à 45 secondes d’intervalle. Le HTML brut différait, comme attendu. Plus intéressant : après avoir retiré les scripts, les styles et le balisage, et supprimé les horodatages et les jetons, le texte visible différait encore. Les pages en direct se mettent à jour continuellement. Un moniteur qui alerte sur toute différence aurait alerté sur cette page à chaque exécution.
Les sources de bruit sont prévisibles :
| Bruit | Exemple | Correctif typique |
|---|---|---|
| Scripts et données intégrés | Charges analytiques, état JSON, jetons CSRF | Retirer les blocs script et style avant la comparaison |
| Texte basé sur le temps | ”Mis à jour il y a 3 minutes”, heures d’horloge, dates | Supprimer avec des motifs, ou comparer des champs qui les excluent |
| Modules rotatifs | Publicités, “tendances actuelles”, recommandations | Comparer uniquement la région ou les champs qui vous intéressent |
| Personnalisation et expérimentations | Variantes de test A/B, blocs spécifiques à la localisation | Collecter depuis un point d’observation fixe avec des paramètres cohérents |
| Ordonnancement | Listes qui se mélangent à chaque chargement | Trier avant de comparer |
Une partie du bruit est en réalité une différence liée à qui a fait la demande. Une page chargée depuis l’Allemagne peut différer de la même page chargée depuis les États-Unis pour des raisons qui n’ont rien à voir avec un changement. Gardez le point d’observation fixe pour chaque page surveillée, de sorte que la seule chose qui varie entre les exécutions soit le temps. Avec une passerelle résidentielle, cela signifie fixer le pays, par exemple customer-USERNAME-country-de, pour chaque exécution de cette tâche.
Principe 1 : comparer des champs, pas des pages
Le détecteur de changement le plus fiable ne fait pas de diff sur les pages du tout. Il extrait les champs qui comptent, comme le prix, la disponibilité, le titre, une liste de certificats ou une adresse, et compare ces valeurs. Un champ a changé ou n’a pas changé, et le bruit ailleurs sur la page est sans importance.
Les données structurées facilitent cela plus qu’il n’y paraît. De nombreuses pages publient leurs champs clés sous forme de JSON-LD, ce qui est bien plus stable que la mise en page qui l’entoure ; l’extraction de ces données est couverte dans arrêtez de parser le HTML. Lorsque les champs proviennent plutôt de sélecteurs, comparez les valeurs extraites, jamais le HTML qui les entoure.
La comparaison de champs rend aussi les alertes utiles. “Le prix est passé de 89,00 à 79,00” est actionnable. “La page a changé” ne l’est pas.
Principe 2 : normaliser ce que vous devez comparer
Certaines surveillances portent vraiment sur des pages entières : conditions d’utilisation, page “à propos” d’un fournisseur, déclaration de durabilité, page de politique. Pour celles-ci, retirez ce qui ne compte jamais avant de comparer :
- Retirez les blocs sans contenu : scripts, styles, modèles, SVG en ligne.
- Ne gardez que le texte visible, avec les espaces réduits et la casse uniformisée.
- Retirez les fragments volatils : heures d’horloge, dates ISO, temps relatifs, jetons et identifiants hexadécimaux longs.
- Restreignez à une région lorsqu’une page a une zone de contenu principal stable, comme le corps de l’article ou l’élément principal.
La normalisation retire une grande part du bruit. Elle ne le retire pas entièrement, ce qui explique pourquoi l’étape suivante compte.
Principe 3 : mesurer combien, pas si
Plutôt que de demander si le texte normalisé est identique, demandez à quel point il est similaire. Simhash est un bon outil pour cela. Il réduit un document à une empreinte de 64 bits avec une propriété utile : des documents similaires obtiennent des empreintes qui ne diffèrent que sur quelques positions de bits. Google a décrit son utilisation pour la détection de quasi-doublons dans “Detecting Near-Duplicates for Web Crawling” (WWW 2007), où les auteurs ont validé que “for a repository of 8B web-pages, 64-bit simhash fingerprints and k = 3 are reasonable”, ce qui signifie que des pages dont les empreintes diffèrent d’au plus trois bits pourraient être considérées comme des quasi-doublons.
Cela transforme la détection de changement en une question de distance. Dans notre test, les deux récupérations de la page d’accueil d’actualités, à 45 secondes d’intervalle, se sont révélées éloignées de 2 bits : en dessous du seuil, donc du bruit. La même page d’accueil comparée à un article sans rapport s’est révélée éloignée de 34 bits : un contenu clairement différent.
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}
Traitez le seuil comme un point de départ, pas comme une constante. Le chiffre de WWW 2007 a été choisi pour trouver des doublons parmi des milliards de pages, pas pour surveiller une page dans le temps. Calibrez-le par type de page : récupérez chaque page surveillée plusieurs fois de suite, à un moment où rien de significatif ne devrait changer, et fixez le seuil juste au-dessus des distances observées. Les pages courtes demandent plus de prudence, car quelques mots modifiés déplacent l’empreinte d’un petit document davantage que celle d’un document long.
Principe 4 : classer, puis acheminer
Un détecteur qui renvoie seulement “changé” reporte le vrai travail sur celui qui lit l’alerte. Renvoyez un type, et acheminez en fonction de celui-ci :
| Type | Signification | Où cela va |
|---|---|---|
field | Une valeur que vous suivez a changé | Directement dans le jeu de données et, si c’est important, une alerte |
content | Le fond de la page a changé au-delà du bruit | Une file d’examen humain, avec un diff textuel joint |
noise | La page a bougé, mais dans les limites de la variation normale | Enregistré pour calibration, jamais alerté |
none | Identique octet pour octet | Rien |
Deux raffinements sont rentables. Conservez l’instantané précédent et un diff textuel lisible pour chaque changement de type content, afin qu’un relecteur puisse en juger en quelques secondes. Et enregistrez le taux de chaque type par site : un site dont la distance de bruit augmente progressivement sur plusieurs semaines change ses modèles, et un site qui ne produit soudainement plus aucun changement pourrait vous servir une page obsolète ou bloquée, un mode d’échec couvert dans le taux d’échec silencieux.
Passer à l’échelle
La détection de changement à grande échelle est surtout un problème de stockage et de planification.
- Stockez des empreintes et des champs, pas des pages complètes, pour la plupart des exécutions. Une empreinte de 64 bits et une poignée de champs sont minuscules. Ne gardez des instantanés complets que lorsqu’un changement est détecté, ou selon un calendrier plus lent pour l’audit.
- Revisitez selon le changement attendu. Les pages qui changent rarement n’ont pas besoin de vérifications horaires. La planification de crawl sensible aux coûts explique comment dépenser un budget de récupération là où le changement est probable, et chaque résultat
noneounoiseest une preuve pour ce modèle. - Utilisez d’abord les signaux bon marché. Là où un site envoie des en-têtes
ETagouLast-Modifiedfiables, une requête conditionnelle qui renvoie304 Not Modifiedrépond à la question pour presque aucune bande passante. - Surveillez le surveillant. Des pages canaris avec des motifs de changement connus vous disent si le détecteur lui-même fonctionne encore.
Où cela est utilisé
Le même mécanisme sous-tend des tâches très différentes : la surveillance des prix et des stocks, où les changements de champs sont tout l’enjeu, comme dans construire un flux de prix concurrentiel en temps réel ; la surveillance des fournisseurs et de la conformité, où les changements de contenu de page entière comptent, comme dans surveiller l’empreinte publique de vos fournisseurs et vérifier les allégations ESG ; et la préservation de preuves lorsqu’un changement a une portée juridique.
En résumé
“Est-ce que cette page a changé ?” est la mauvaise question pour le web moderne, car la réponse est presque toujours oui. Les questions utiles sont “est-ce qu’un champ qui m’intéresse a changé ?” et, là où vous devez comparer des pages entières, “est-ce que le fond a changé au-delà de la variation normale de cette page ?”
Comparez des champs chaque fois que possible. Normalisez ce que vous ne pouvez pas éviter de comparer, mesurez la similarité plutôt que l’égalité, calibrez les seuils par type de page, et acheminez chaque type de changement vers l’endroit qui peut agir dessus. Le résultat est un flux de changements auquel les gens font confiance, ce qui est le seul type qui soit lu.
Sources et références
- Manku, Jain et Das Sarma, Detecting Near-Duplicates for Web Crawling, WWW 2007.
- Moses Charikar, Similarity Estimation Techniques from Rounding Algorithms, STOC 2002. L’origine du simhash.
- Shifter, documentation sur le ciblage géographique des Residential Proxies.