Automatiser sa veille concurrentielle avec n8n, sans bruit
Construis une veille concurrentielle automatisée qui nettoie le HTML, détecte les vrais changements et alerte seulement sur les mouvements stratégiques.

À 6 h, une alerte annonce que le tarif d’un concurrent a changé. À 12 h, la même alerte revient. À 18 h, encore une fois. Tu ouvres la page et le prix n’a pas bougé. Seul l’identifiant d’une session, la date d’un bandeau ou l’ordre de deux témoignages a changé dans le HTML. Après trois jours, tu coupes les notifications. Le jour où le prix change vraiment, ton système se tait dans une boîte mail que tu ne lis plus.
C’est le problème central d’une veille concurrentielle automatisée : récupérer une page est simple, détecter un changement utile ne l’est pas. Je vais donc construire un système qui surveille des entreprises nommées, nettoie le contenu avant toute comparaison et réserve les interruptions aux mouvements tarifaires et aux lancements de produit. Les autres signaux alimentent une fiche concurrent consultable sans transformer ta journée en centre de contrôle.
Le nettoyage avant comparaison, le choix des seuils et la fiche concurrent sont détaillés dans la page, avec les sélecteurs et les règles qui évitent les fausses alertes.
Une archive contient le montage complet : le workflow n8n importable et le modèle de fiche concurrent persistante. Elle est dans l’espace ressources de la communauté, l’accès est gratuit : récupérer l’archive de la veille concurrentielle.
Le workflow est livré inactif, avec des domaines .example et sans identifiant. C’est volontaire : tu dois choisir tes cibles, vérifier les règles applicables et calibrer les filtres avant la première collecte.
Veille informationnelle et veille concurrentielle ne cherchent pas la même chose
Une veille informationnelle automatisée suit des sujets : n8n, agents IA, automatisation commerciale, évolution d’une réglementation. Elle agrège des flux, des articles et des annonces, puis détecte les publications pertinentes. Sa question est : « Qu’est-ce qui vient de paraître sur mon secteur ? »
La veille concurrentielle part d’une liste fermée d’entreprises. Sa question est : « Qu’est-ce qui a changé chez ces entreprises depuis mon dernier passage ? » Elle regarde moins de sources, mais elle conserve un état précédent pour chaque cible. Une page de tarifs silencieusement modifiée vaut davantage que vingt articles d’opinion. Une nouvelle offre d’emploi peut renseigner sur une direction produit avant toute annonce officielle.
Confondre les deux crée du bruit. Si tu recherches le nom de quinze concurrents dans tous les flux du Web, tu récoltes des interviews, des reprises de communiqués et des comparatifs superficiels. Si tu appliques une simple détection de publication à une page de tarifs, tu ignores les modifications internes de cette page. Je garde donc deux pipelines séparés, reliés seulement au moment où une information concurrentielle peut enrichir la veille générale.
Choisir cinq à quinze concurrents et les pages qui révèlent une décision
Une liste exhaustive n’est pas une bonne liste. Plus tu ajoutes d’entreprises, plus tu multiplies les sélecteurs à maintenir, les faux positifs et les vérifications juridiques. Je commencerais avec cinq concurrents directs et, si les résultats restent lisibles après deux semaines, j’étendrais progressivement jusqu’à quinze.
Pour chaque entreprise, choisis des pages qui traduisent une décision :
| Page | Signal recherché | Fréquence de départ |
|---|---|---|
| Tarifs | hausse, baisse, nouveau plan, limite déplacée | toutes les 6 heures |
| Fonctionnalités | capacité ajoutée, retirée ou repositionnée | une fois par jour |
| Changelog | lancement, correction importante, dépréciation | deux fois par jour |
| Carrières | équipe créée, expertise recrutée, zone géographique | une fois par jour |
| Conditions | clause, engagement ou périmètre de service modifié | une fois par semaine |
Ces fréquences sont des points de départ, pas des droits de passage. Un flux officiel, une API ou une newsletter doit toujours passer avant la récupération répétée d’une page. Une page juridique ne justifie pas une visite toutes les heures. À l’inverse, une page de tarifs au milieu d’un lancement mérite temporairement un intervalle plus court si le site l’autorise.
La page d’accueil est rarement une bonne cible. Elle contient des blocs marketing, des témoignages, des compteurs et des expérimentations A/B qui changent souvent sans révéler une décision. Si une information possède sa propre page, surveille cette page et isole son contenu principal.
Concevoir la fiche concurrent avant le workflow
Je ne commence pas par le nœud HTTP Request. Je commence par la sortie. Si tu ne sais pas ce que la fiche doit conserver, tu finiras par archiver des pages entières « au cas où », sans pouvoir répondre à une question simple six mois plus tard.
Le livrable utilise trois Data Tables :
veille_pagescontient les cibles, leur sélecteur CSS, leur fréquence et trois drapeaux d’autorisation ;veille_snapshotsconserve la dernière empreinte, le texte utile, la qualification et le résumé ;veille_keywordsenregistre les nouveaux mots-clés détectés pour un domaine.
La clé page_key reste stable, même si l’URL change. Par exemple, concurrent-a-tarifs désigne la fonction surveillée, pas le chemin actuel. Cette clé permet de comparer les versions et de déplacer une page sans perdre son historique logique.
Le CSV fourni contient cinq lignes désactivées. Les colonnes collection_allowed et robots_allowed sont distinctes, car un robots.txt favorable ne répond pas à toutes les questions juridiques. La colonne ignore_patterns reçoit un tableau JSON d’expressions régulières propres à la page. La colonne notes documente la décision, la source officielle préférée ou une limite à ne pas franchir.
Le cadre légal et éthique avant toute collecte
Je ne peux pas transformer cette section en avis juridique universel. Les règles dépendent du site, des données, du pays, du volume et de l’usage. En revanche, un workflow sérieux doit matérialiser les vérifications au lieu de les laisser dans la tête de la personne qui l’a créé.
Le fichier robots.txt donne les règles demandées aux robots pour un service. Le protocole Robots Exclusion Protocol précise aussi que ces règles ne constituent pas une autorisation d’accès. Un chemin non interdit n’est donc pas automatiquement libre de toute autre contrainte. Consulte également les conditions générales du site, privilégie les sources officielles prévues pour la redistribution et identifie clairement ton agent utilisateur avec une adresse de contact.
En France et dans l’Union européenne, la réutilisation d’informations publiquement accessibles peut relever du RGPD dès qu’une personne physique est identifiable. Les recommandations de la CNIL sur la réutilisation de données publiées sur Internet rappellent notamment qu’une donnée accessible n’échappe pas aux principes de finalité, de minimisation, d’information et de durée de conservation. Une offre d’emploi est un signal sur l’entreprise. Le nom du recruteur, son adresse ou la liste des candidats ne sont pas nécessaires à ce signal. Ne les collecte pas.
Le droit des bases de données compte aussi. L’article L342-1 du Code de la propriété intellectuelle permet au producteur d’interdire certaines extractions ou réutilisations substantielles. Des extractions répétées et systématiques peuvent également poser problème. Surveiller cinq pages publiques à faible fréquence n’est pas équivalent à recopier un catalogue entier, mais la différence de volume ne dispense pas d’examiner les droits et les conditions.
J’utilise donc une règle opérationnelle simple :
- privilégier API, flux, changelog ou newsletter officiels ;
- ne collecter que la zone nécessaire ;
- espacer les passages et ralentir après une erreur ;
- ne pas contourner une restriction technique ;
- exclure les données personnelles inutiles ;
- documenter la finalité et la durée de conservation ;
- laisser les drapeaux d’activation à
falsetant que le contrôle n’est pas terminé.
Collecter à faible fréquence avec n8n
Le déclencheur principal s’exécute toutes les six heures, mais il ne récupère pas toutes les pages à chaque passage. Il lit veille_pages, élimine les lignes inactives et compare next_check_at à l’heure actuelle. Chaque cible conserve ainsi sa fréquence propre.
Le nœud HTTP Request utilise un agent utilisateur transparent et limite l’attente à trente secondes. Il doit recevoir le corps en texte et, idéalement, le statut HTTP complet. Un statut 429 signifie que le serveur demande de ralentir. Un statut 403 ou une page de défi Cloudflare ne doit pas déclencher une course au contournement. Le workflow consigne l’erreur et double temporairement l’intervalle, avec un plafond d’une semaine.
Les requêtes conditionnelles permettent parfois d’éviter de retélécharger un contenu inchangé. Si le serveur renvoie un ETag ou une date de modification, conserve la valeur et utilise If-None-Match ou If-Modified-Since au passage suivant. Un statut 304 confirme alors que la représentation n’a pas changé. Le fonctionnement des validateurs et de la réponse 304 est défini dans la spécification HTTP. Le workflow téléchargeable reste compatible avec les pages qui n’en fournissent pas, mais tu peux ajouter ce raccourci pour chaque site qui le supporte correctement.
Nettoyer le HTML avant de calculer la différence
Comparer le HTML brut est presque toujours une erreur. Une page contient des scripts, des identifiants, des paramètres de suivi, des jetons et des composants réordonnés. Le nœud HTML de n8n extrait d’abord le texte du sélecteur défini dans la fiche. Un sélecteur comme main .pricing-grid vaut mieux que body.
Ensuite, le nœud Code normalise Unicode et les espaces, puis applique seulement les motifs explicitement prévus pour cette page :
const source = $("Filtrer les pages dues").item.json;
const warnings = [];
let text = String($json.content ?? "")
.normalize("NFKC")
.replace(/\u00a0/g, " ")
.replace(/[\t\r ]+/g, " ")
.replace(/\n{3,}/g, "\n\n")
.trim();
let patterns = [];
try {
patterns = JSON.parse(source.ignore_patterns || "[]");
if (!Array.isArray(patterns)) throw new Error("not an array");
} catch {
warnings.push("INVALID_IGNORE_PATTERN");
}
for (const pattern of patterns) {
try {
text = text.replace(new RegExp(String(pattern), "gimu"), " ");
} catch {
warnings.push("INVALID_IGNORE_PATTERN");
}
}
text = text.replace(/\s+/g, " ").trim();
if (text.length < 80) {
warnings.push("SELECTOR_EMPTY_OR_CONTENT_TOO_SHORT");
}
return [{
json: {
...source,
clean_content: text.slice(0, 14000),
content_ok: text.length >= 80,
normalization_warnings: [...new Set(warnings)],
},
}];
Le point délicat se trouve dans ignore_patterns. Ne crée pas une règle globale qui efface toutes les dates, tous les nombres ou toutes les devises. Elle supprimerait précisément un passage de 29 à 39 euros, une nouvelle limite à 10 000 opérations ou une date d’entrée en vigueur. Observe d’abord les faux changements, puis écris une règle étroite pour un élément identifié, par exemple le libellé « mis à jour le » lorsqu’il reflète seulement l’heure de génération.
Le seuil de quatre-vingts caractères protège contre un sélecteur cassé qui renverrait un bouton ou un titre isolé. La limite de 14 000 caractères garde une qualification raisonnable, mais elle suppose que le sélecteur soit précis. Tronquer une page entière peut masquer le changement situé à la fin.
Détecter le changement sans demander l’avis d’un modèle
Une fois le texte stabilisé, le nœud Crypto calcule son empreinte SHA-256. La première exécution enregistre une référence et ne produit aucune alerte. Aux passages suivants, une empreinte identique termine le traitement. Une empreinte différente ouvre la phase de qualification.
Le hash décide s’il existe une différence, pas si cette différence est importante. Ce découpage est essentiel. Demander à un modèle de relire la page toutes les six heures coûterait plus cher et introduirait une réponse variable là où une comparaison déterministe suffit.
Conserve aussi l’ancien texte. Une empreinte prouve que deux états diffèrent, mais elle ne permet pas d’expliquer comment. Le modèle a besoin des deux versions pour citer des éléments observables et le lecteur de la fiche doit pouvoir vérifier le résumé.
Suivre les contenus, les mots-clés et les recrutements
La surveillance de pages couvre le changelog, les tarifs, les fonctionnalités et les offres d’emploi. Elle détecte aussi un nouvel article si tu extrais la liste des publications, mais un flux RSS officiel reste préférable : il donne le titre, l’URL et la date sans comparer toute la page.
Pour les mots-clés, le workflow possède un second déclencheur hebdomadaire. Il déduit les domaines actifs, appelle l’endpoint ranked_keywords/live de DataForSEO pour la France et le français, puis conserve seulement les lignes dont rank_changes.is_new vaut true. La documentation officielle de DataForSEO décrit ce champ et les paramètres de la requête.
Cette branche requiert un compte payant et une authentification HTTP Basic que tu sélectionnes dans n8n. Aucun identifiant n’est inclus dans le fichier. Tu peux supprimer toute la branche si les positions organiques ne servent pas ta décision. Un mot-clé nouveau n’est pas nécessairement une stratégie nouvelle : une page peut apparaître brièvement sur une requête secondaire. Cherche un ensemble cohérent, une nouvelle page et une progression dans le temps.
Les recrutements demandent la même prudence. Une offre pour trois ingénieurs spécialisés peut indiquer un investissement, pas une roadmap certaine. Stocke le rôle, l’équipe, le lieu et la date d’apparition si ces éléments sont nécessaires. Évite les noms et les coordonnées. Puis relie le signal à d’autres observations : page produit modifiée, documentation créée, discours commercial déplacé.
Faire qualifier le mouvement par un agent
L’agent reçoit uniquement les changements confirmés. Son travail n’est pas de prédire la stratégie secrète d’une entreprise. Il classe l’écart observé parmi sept catégories et fournit un résumé bref avec des preuves :
Tu qualifies un changement observé sur une page publique d'un concurrent.
Entreprise : {{ $json.competitor }}
Type de page : {{ $json.page_type }}
URL : {{ $json.url }}
ANCIEN CONTENU
{{ $json.previous_content }}
NOUVEAU CONTENU
{{ $json.clean_content }}
Réponds uniquement avec un objet JSON valide :
{
"classification": "cosmetique|positionnement|tarif|lancement_produit|recrutement|juridique|inconnu",
"confidence": 0.0,
"urgent": false,
"summary": "deux phrases factuelles maximum",
"evidence": ["extrait court avant", "extrait court après"]
}
Une urgence vaut true uniquement pour un mouvement tarifaire ou un
lancement produit explicite. N'invente rien. Si le changement est ambigu,
choisis inconnu et baisse confidence.
Le nœud suivant parse la réponse et refuse les catégories inconnues. Il borne la confiance entre 0 et 1. Surtout, il recalcule urgent : même si le modèle renvoie true, l’alerte reste impossible pour une catégorie autre que tarif ou lancement_produit. Un second seuil exige une confiance d’au moins 0,65.
Ce garde-fou ne rend pas le modèle infaillible. Il réduit son pouvoir. Le résumé reste une interprétation à vérifier à partir de l’URL et des extraits. Une décision commerciale importante ne doit jamais reposer sur cette seule sortie.
Réserver les alertes aux mouvements qui exigent une réaction
Une bonne fiche peut recevoir plusieurs mises à jour par semaine. Une bonne alerte doit rester rare. Je route donc les résultats ainsi :
Le message d’alerte contient le concurrent, la catégorie, la confiance, le résumé et l’URL. Il ne dit pas « le concurrent va faire ceci ». Il dit « cette page publique est passée de ceci à cela ». Cette formulation sépare le fait de l’hypothèse.
Pour les autres catégories, consulte une vue hebdomadaire de veille_snapshots. Tu peux ensuite envoyer la fiche vers un CRM qui ne possède pas d’intégration native, mais garde une copie indépendante : une observation concurrentielle ne doit pas devenir par erreur une activité attachée à un prospect.
Ce qui va te bloquer en production
Cloudflare et les protections anti-robot
Une page de défi n’est pas le contenu du concurrent. Les défis Cloudflare peuvent exécuter du JavaScript et poser un cookie avant de donner accès à la ressource. Si ton workflow reçoit cette page, son hash changera ou restera bloqué sur un écran inutile.
La bonne réaction est de chercher un flux officiel, une API, une page publique alternative ou une fréquence acceptée. Faire tourner des adresses IP pour imiter des visiteurs est une fausse bonne idée : tu augmentes le coût, la fragilité et le risque de dépasser une restriction explicite, tout en masquant l’identité de ton collecteur. Ce guide ne fournit aucun mécanisme de contournement.
Les pages rendues en JavaScript
Le nœud HTTP Request récupère la réponse du serveur, pas nécessairement le texte construit dans le navigateur. Si le HTML contient seulement un conteneur vide et des scripts, utilise d’abord une API publique appelée par la page, si elle existe et si son usage est permis. Sinon, un navigateur automatisé peut rendre la page.
Playwright permet d’évaluer du JavaScript dans le contexte de la page, donc d’extraire le texte après rendu. Cette solution alourdit cependant l’infrastructure, la maintenance et la surface de panne. Si tu l’emploies, installe un petit service interne, appelle-le depuis n8n, limite les domaines autorisés et garde les mêmes fréquences prudentes. Le navigateur ne change rien aux obligations juridiques.
Les sélecteurs qui cassent
Une refonte peut remplacer .pricing-grid par .plans. Sans contrôle de longueur, ton extracteur renverra une chaîne vide et le système interprétera la disparition comme un changement majeur. Le workflow refuse donc tout contenu inférieur à quatre-vingts caractères et journalise SELECTOR_EMPTY_OR_CONTENT_TOO_SHORT.
Ajoute une revue hebdomadaire des erreurs et un test de sélecteur avant chaque activation. Un sélecteur sémantique comme main, [data-section="pricing"] ou une hiérarchie courte résiste mieux qu’une suite de classes générées.
Les exécutions concurrentes et l’ordre des écritures
Deux exécutions lancées sur la même page peuvent lire le même ancien état, envoyer deux alertes puis écraser la fiche. Définis une concurrence faible pour ce workflow, évite de déclencher manuellement une page pendant le passage planifié et utilise page_key comme clé d’upsert. Pour une charge plus grande, place les cibles dans une file et verrouille la clé pendant le traitement.
Mettre le workflow en production sans l’oublier
Le fichier importé contient des nœuds marqués A CONFIGURER. Lance d’abord la branche manuelle qui crée les trois tables. Sélectionne ensuite leur identifiant réel dans chaque nœud Data Table. Importe le CSV dans veille_pages, remplace le concurrent d’exemple et renseigne un sélecteur par URL.
Configure ensuite :
- une référence Anthropic dans le nœud de modèle, ou remplace-le par ton fournisseur ;
- une référence SMTP et les deux adresses de l’alerte ;
- une référence HTTP Basic DataForSEO si tu gardes la branche SEO ;
- le fuseau horaire du workflow ;
- une politique de conservation pour les anciens textes et les erreurs.
Le workflow a été préparé contre n8n 2.30.5. Une version ultérieure peut modifier les paramètres d’un nœud. Lis les notes de version et teste l’import dans un environnement non critique. Si tu gères toi-même l’instance, le guide pour auto-héberger une stack d’automatisation couvre la base de déploiement, les sauvegardes et l’exposition réseau.
Une sauvegarde de n8n ne suffit pas si les Data Tables ne sont pas incluses dans ta stratégie de restauration. Exporte périodiquement la configuration des pages et la dernière fiche. Ne conserve pas toutes les versions complètes indéfiniment : garde la dernière référence et les changements qualifiés nécessaires à ton analyse.
Les limites du système
Ce système observe des traces publiques. Il ne connaît ni les intentions internes, ni les chiffres de vente, ni la date réelle d’un projet. Une série d’offres d’emploi peut annoncer une nouvelle équipe, remplacer des départs ou constituer un vivier. Un nouveau plan tarifaire peut être un test limité. Un mot-clé peut apparaître par accident.
Il existe aussi un biais de visibilité : les concurrents qui publient beaucoup produisent davantage de signaux que ceux qui changent en silence. Le volume de la fiche ne mesure donc pas l’intensité stratégique. Compare des événements de même nature et recoupe-les.
Enfin, le système demande de la maintenance. Les pages changent de structure, les règles d’accès évoluent et un motif d’exclusion pertinent aujourd’hui peut masquer un signal demain. Automatise la détection, pas la responsabilité. Une personne doit revoir les erreurs, les règles et les décisions d’alerte.
Commencer par une semaine silencieuse
Importe le workflow et le CSV, puis choisis un seul concurrent et deux pages : tarifs et changelog. Laisse les alertes SMTP désactivées pendant sept jours. Chaque changement détecté doit être relu et classé dans une petite matrice :
| Observation | Le hash devait-il changer ? | La qualification est-elle juste ? | Action |
|---|---|---|---|
| horodatage dynamique | non | sans objet | ajouter un motif étroit |
| prix modifié | oui | tarif | conserver et tester l’alerte |
| paragraphe déplacé | idéalement non | cosmétique | améliorer le sélecteur |
| sélecteur vide | non | erreur technique | corriger le sélecteur |
| nouvelle fonctionnalité | oui | lancement ou positionnement | vérifier les preuves |
Cette semaine révèle le vrai niveau de bruit. Ajuste les sélecteurs avant les expressions régulières, car extraire moins de contenu est plus sûr qu’effacer largement après coup. Active ensuite les alertes pour le tarif et le lancement, ajoute les pages carrières et conditions, puis seulement les autres concurrents.
La réussite ne se mesure pas au nombre de pages surveillées. Elle se mesure à trois chiffres : le taux de faux changements, le nombre d’alertes réellement actionnables et le temps nécessaire pour comprendre pourquoi une alerte existe. Si tu peux ouvrir la fiche, voir l’ancien texte, le nouveau, la catégorie et la preuve en moins d’une minute, ta veille te rend du temps. Si elle te demande de relire quinze pages chaque matin, tu as simplement automatisé la création d’une nouvelle corvée.


