À proposExpertiseProjetsParcoursBlogContact
Discuter
À proposExpertiseProjetsParcoursBlogContactDiscuter

Yao David Logan

Software Engineer fullstack spécialisé en SaaS, automatisation métier et plateformes web/mobile scalables.

NavigationExpertiseProjetsParcoursBlogContact
LiensGitHubLinkedInEmail
© 2026 Yao David Logan. Tous droits réservés.
Scraping légal à grande échelle — REX Prospect-Pro AI
Retour au blog

Retour d'experience

Xin@

Scraping légal à grande échelle — REX Prospect-Pro AI

YDLYao David Logan16 min de lecture18 septembre 2026

CatégorieRetour d'experience

Lecture16 min de lecture

Publié18 septembre 2026

Vues0

Partages0

Commentaires0

Sommaire
  1. 011. Le contexte
  2. 022. La règle de base — ce qui est légal
  3. 033. Architecture du pipeline de scraping
  4. 044. Rate-limiting respectueux
  5. 055. Identification claire — user-agent honnête
  6. 066. Le respect du robots.txt
  7. 077. Déduplication et qualité des données
  8. 088. Stockage et historisation
  9. 099. Conformité RGPD pour les données dans le périmètre
  10. 1010. Observabilité
  11. 1111. Pièges à éviter
  12. 1212. Conclusion

Token bucket par source, user-agent identifié, respect du robots.txt, dédoublonnage métier, snapshots horodatés et droit à l'oubli : 14 mois de scraping continu sans incident.

Scraper Pages Jaunes, Pappers, GoAfrica et Google Maps en respectant la loi et les serveurs, à raison de milliers de requêtes par jour. Retour d'expérience sur Prospect-Pro AI.

1. Le contexte

Prospect-Pro AI, plateforme de prospection B2B, doit constituer en continu une base de contacts qualifiés depuis quatre sources publiques : Pages Jaunes (annuaire pro français), Pappers (open data société française), GoAfrica (annuaire business panafricain), Google Maps (fiches d'établissements). L'enjeu : alimenter le pipeline IA avec des données fraîches sans tomber dans l'illégal ni saturer les sources.

Cet article n'est pas un tutoriel "comment contourner un anti-bot". C'est un retour d'expérience sur comment scraper proprement : conformité légale, respect technique, robustesse en production.

2. La règle de base — ce qui est légal

Premier réflexe : connaître le terrain juridique. Trois critères clarifient 95 % des cas :

  1. Donnée publique — accessible sans authentification, non protégée par un paywall ou des CGU explicites de non-scraping
  2. Donnée non-personnelle ou pseudonymisée — la donnée d'entreprise (raison sociale, SIREN, téléphone professionnel) n'est pas couverte par le RGPD si elle reste dans la sphère B2B
  3. Respect des CGU — si le site interdit explicitement le scraping dans ses conditions, on s'abstient

Cas concrets :

  • Pages Jaunes : données publiques pro, scraping toléré pour usage B2B raisonnable
  • Pappers : API publique fournie par l'État (open data SIREN), scraping inutile si on utilise leur API
  • GoAfrica : annuaire panafricain, données publiques pro
  • Google Maps : fiches publiques, mais Google a une API officielle (Places API) — préférable

Toujours préférer l'API officielle quand elle existe. Le scraping est un dernier recours, pas un raccourci.

3. Architecture du pipeline de scraping

Sur Prospect-Pro, chaque source est encapsulée dans un connecteur typé. Le squelette :

TypeScript
1// src/scraping/Connector.ts
2export interface ScrapingConnector {
3 readonly source: SourceName;
4 readonly rateLimit: { rpm: number; concurrent: number };
5 readonly robotsTxtPolicy: "respect" | "ignore"; // toujours "respect" en prod
6 search(query: SearchQuery): AsyncIterable<RawRecord>;
7 hydrate(record: RawRecord): Promise<EnrichedRecord>;
8}

Chaque connecteur déclare :

  • Sa source d'origine
  • Son rate-limit propre (négocié avec la source si possible)
  • Sa politique vis-à-vis du robots.txt
  • Une méthode de recherche qui yield des records bruts (async iterable pour streaming)
  • Une méthode d'hydratation pour enrichir un record (détails, adresse, etc.)

4. Rate-limiting respectueux

Saturer un serveur est un comportement de pirate, pas de pro. Trois techniques combinées :

4.1 Token bucket par source

Chaque source a son propre bucket, dimensionné conservativement (typiquement 60 requêtes / minute).

TypeScript
1// src/scraping/RateLimiter.ts
2export class TokenBucket {
3 private tokens: number;
4 private readonly capacity: number;
5 private readonly refillRate: number; // tokens / sec
6 private lastRefill: number;
7 
8 constructor(capacity: number, refillRate: number) {
9 this.capacity = capacity;
10 this.tokens = capacity;
11 this.refillRate = refillRate;
12 this.lastRefill = Date.now();
13 }
14 
15 async acquire(): Promise<void> {
16 this.refill();
17 if (this.tokens >= 1) {
18 this.tokens -= 1;
19 return;
20 }
21 const waitMs = ((1 - this.tokens) / this.refillRate) * 1000;
22 await sleep(waitMs);
23 return this.acquire();
24 }
25 
26 private refill(): void {
27 const now = Date.now();
28 const elapsed = (now - this.lastRefill) / 1000;
29 this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
30 this.lastRefill = now;
31 }
32}

Chaque connecteur appelle await bucket.acquire() avant chaque requête HTTP.

4.2 Concurrence bornée

Pas plus de N requêtes simultanées par source. Le plus simple : utiliser une queue avec p-limit :

TypeScript
1import pLimit from "p-limit";
2 
3const limit = pLimit(3); // max 3 concurrent sur cette source
4 
5const results = await Promise.all(
6 queries.map((q) => limit(() => connector.search(q)))
7);

4.3 Backoff exponentiel sur erreur

429 Too Many Requests ou 503 Service Unavailable : on attend, on retente avec un délai croissant.

TypeScript
1async function withRetry<T>(fn: () => Promise<T>, maxAttempts = 5): Promise<T> {
2 let lastError: unknown;
3 for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
4 try {
5 return await fn();
6 } catch (error) {
7 lastError = error;
8 const delay = Math.min(30_000, 1000 * 2 ** attempt) + Math.random() * 1000;
9 await sleep(delay);
10 }
11 }
12 throw lastError;
13}

Le + Math.random() (jitter) évite que tous les workers retentent en même temps après une panne.

5. Identification claire — user-agent honnête

Cacher son identité derrière un user-agent de Chrome standard est dangereux : juridiquement, ça suggère une intention de tromper. La bonne pratique :

Plain Text
1User-Agent: ProspectProBot/1.0 (+https://prospect-pro.example.com/bot)

L'URL dans le user-agent pointe vers une page expliquant le projet, à qui s'adresser pour des questions, comment se faire désindexer. Les sources sérieuses respectent cette transparence. Les sources qui blockent un bot identifié signalent qu'elles refusent le scraping — et on s'abstient.

6. Le respect du robots.txt

Avant chaque session, on charge le robots.txt de la source et on respecte ses Disallow.

TypeScript
1import robotsParser from "robots-parser";
2 
3async function loadRobots(host: string): Promise<robotsParser.Robot> {
4 const text = await fetch(`https://${host}/robots.txt`).then((r) => r.text());
5 return robotsParser(`https://${host}/robots.txt`, text);
6}
7 
8async function shouldFetch(url: string, robots: robotsParser.Robot): Promise<boolean> {
9 return robots.isAllowed(url, "ProspectProBot");
10}

Si robots.txt interdit, on n'y va pas. Point. Même si techniquement on pourrait.

7. Déduplication et qualité des données

Une source peut retourner le même contact deux fois sous des libellés légèrement différents. Sans dédoublonnage, votre base se pollue rapidement.

Stratégie : clé de dédoublonnage métier combinant les champs stables.

TypeScript
1function leadKey(lead: RawLead): string {
2 // Combinaison : SIREN si présent, sinon hash(nom_normalisé + ville)
3 if (lead.siren) return `siren:${lead.siren}`;
4 const normalized = lead.name.toLowerCase().replace(/[^a-z0-9]/g, "");
5 return `hash:${sha256(`${normalized}|${lead.city}`)}`;
6}

En base, contrainte unique sur cette clé. INSERT ... ON CONFLICT DO UPDATE met à jour les champs récents sans dupliquer.

8. Stockage et historisation

Les données scrapées vieillissent. Une entreprise déménage, change de numéro, ferme. Stocker une snapshot horodatée plutôt qu'un seul état :

SQL
1CREATE TABLE leads (
2 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
3 key VARCHAR(120) NOT NULL UNIQUE,
4 name TEXT NOT NULL,
5 siren VARCHAR(20) NULL,
6 city TEXT NULL,
7 -- ...
8 first_seen TIMESTAMP NOT NULL DEFAULT NOW(),
9 last_seen TIMESTAMP NOT NULL DEFAULT NOW()
10);
11 
12CREATE TABLE lead_snapshots (
13 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
14 lead_id UUID NOT NULL REFERENCES leads(id),
15 source VARCHAR(40) NOT NULL,
16 payload JSONB NOT NULL,
17 scraped_at TIMESTAMP NOT NULL DEFAULT NOW()
18);

leads contient l'état courant, lead_snapshots l'historique brut. Ça permet de retracer un contact, de comprendre une évolution, et de purger les snapshots anciens sans perdre les leads.

9. Conformité RGPD pour les données dans le périmètre

Si vous scrapez du B2C ou des données qui touchent à du B2B mais avec PII (nom + prénom d'un dirigeant par exemple), le RGPD s'applique. Trois exigences minimales :

  1. Information : page publique expliquant la collecte, la durée, les droits
  2. Droit à l'oubli : un endpoint qui permet à une personne de demander la suppression
  3. Minimisation : ne stocker que ce qui est nécessaire — pas de scraping aveugle "au cas où"
TypeScript
1// src/controllers/rightToErasure.ts
2export async function requestErasure(req: Request, res: Response) {
3 const email = req.body.email; // identifiant fourni par la personne
4 const found = await Lead.findOne({ where: { contactEmail: email } });
5 if (!found) return res.status(404).json({ ok: false });
6 
7 await found.update({
8 redactedAt: new Date(),
9 name: "[REDACTED]",
10 contactEmail: null,
11 contactPhone: null,
12 });
13 await LeadSnapshot.destroy({ where: { leadId: found.id } });
14 
15 return res.status(200).json({ ok: true, redactedAt: found.redactedAt });
16}

10. Observabilité

Les scrapers en prod doivent émettre des métriques sur :

  • Requêtes par seconde (par source)
  • Taux d'erreurs HTTP (par code)
  • Latence moyenne et p95
  • Volume de leads nouveaux vs mis à jour
  • Taux de dédoublonnage

Un dashboard Grafana sur ces signaux détecte tôt :

  • Une source qui change son HTML (drop brutal de leads scrapés)
  • Un anti-bot qui se déclenche (montée des 403)
  • Un goulot d'étranglement (latence p95 qui explose)

11. Pièges à éviter

PiègeSymptômeCorrection
User-agent menteurRisque juridique, ban éthiqueUA identifié + URL projet
Pas de rate-limitSaturation source, ban IPToken bucket par source
Concurrence illimitéeTrop de connexions, OOMp-limit ou pool borné
Ignorer robots.txtIllégal selon CGU + éthiqueParser et respecter
Pas de dédoublonnageBase polluéeClé métier + UPSERT
Snapshot uniquePas d'historiqueTable snapshots horodatée
Pas de droit à l'oubliRisque RGPDEndpoint d'effacement
Pas de monitorSource change → silenceDashboard sur taux et codes

12. Conclusion

Scraper à grande échelle, c'est jouer un rôle d'utilisateur intensif sans pénaliser la source. Trois mots-clés : identification, modération, respect.

L'identification (user-agent honnête, page projet) protège juridiquement. La modération (rate-limit, concurrence, backoff) protège techniquement la source. Le respect (robots.txt, RGPD, droit à l'oubli) protège votre marque et vos utilisateurs.

Sur Prospect-Pro, ce framework a permis de scraper en continu pendant 14 mois sans incident juridique, sans ban, et avec une qualité de données stable. Pas de magie noire, juste de la discipline.

Commentaires

Réactions des lecteurs

Aucun commentaire pour l'instant

Aucun spam — l'email sert uniquement à vérifier votre identité.

·

Soyez le premier à réagir à cet article.

←PrécédentNext.js 14 App Router en production — leçons après 6 mois sur NexusRetour d'experience · 16 min de lectureSuivantN8N en production — 8 patterns pour ne pas s'auto-empoisonnerTutoriel · 16 min de lecture→
Prochain échange

Transformer cette lecture en décision produit.

Si ce sujet ressemble à un problème réel dans votre produit, je peux intervenir sur le diagnostic, l'architecture, le backend, l'interface et les automatisations qui rendent une plateforme exploitable.

Format
CDI, freelance, mission longue
Focus
SaaS, API, back-office, automatisation
Discuter du sujetTélécharger le CV

Newsletter

Recevoir les prochaines notes techniques.

Une sélection courte sur SaaS, architecture backend, automatisation métier et qualité produit. Pas de bruit, seulement des idées applicables.