Le contexte : l'automatisation comme miroir
Chercher des prospects B2B à la main, c'est un rituel absurde : 7 heures pour trouver 100 entreprises, copier-coller leurs infos dans un tableur, vérifier manuellement si les emails sont valides, chercher les profils LinkedIn un par un.
Ce que ça révèle : Cette tâche n'a jamais été du travail intellectuel. C'est de la répétition pure. Une logique figée qu'un humain exécute en mode automatique.
La vraie question : Pourquoi un humain passe-t-il 7 heures à faire ce qu'une machine peut faire en 47 minutes ?
Quand on automatise, on voit enfin ce qui, dans notre travail, n'était plus vraiment du travail. Juste de la répétition. L'IA ne fait qu'amplifier la logique qu'on lui donne : si tu lui donnes du chaos, elle reproduit le chaos. Si tu lui donnes de la clarté, elle libère du temps.
Le problème concret
- 7h de copier-coller pour collecter 100 prospects (nom, entreprise, secteur, contacts)
- 50% d'emails faux ou obsolètes parce qu'on ne vérifie rien
- Mental saturé : impossible de réfléchir stratégiquement après 7h de répétition
- Aucune valeur créée : juste du transport de données
On voulait construire un système qui fait ce travail à notre place, automatiquement, en respectant les limites des outils et la législation (RGPD).
Mais avant de coder quoi que ce soit, on observe. Toujours. Parce qu'automatiser un process qu'on ne comprend pas, c'est juste reproduire le chaos plus vite.
Observer le réel
Pendant 3 jours, on a suivi le process manuel de A à Z. Pas pour juger, mais pour comprendre : où ça bloque, ce qui se répète, comment circule l'information.
Ce qu'on a observé, chrono en main
On a suivi la collecte manuelle de 10 prospects pour comprendre le temps réel passé sur chaque étape :
- 15 min sur LinkedIn : Chercher des entreprises par secteur, vérifier les profils
- 12 min sur Google : Trouver les sites web, vérifier qu'ils sont actifs
- 8 min sur Excel : Copier-coller nom, secteur, ville, URL dans le tableur
- 10 min à deviner les emails : Tester prénom.nom@, contact@, info@... sans certitude
- 5 min de vérification : Retours sur LinkedIn pour compléter les infos manquantes
Total : 50 minutes pour 10 prospects. Extrapolé sur 100 prospects, ça fait 8h+ de travail répétitif.
Les patterns identifiés
Le constat : 90% de ce process est de la logique pure. Pas de créativité, pas de décision stratégique. Juste des règles répétées en boucle :
- Chercher "agence de communication Paris" → Toujours la même requête, juste les mots qui changent
- Copier nom entreprise → Coller dans Excel colonne B → Répété 100 fois
- Deviner format email → Tester 3-4 variantes → Sans jamais être sûr
- Vérifier site web → Cliquer → Regarder 10 secondes → Revenir
Si un humain peut décrire une tâche en 3 étapes claires et répétables, une machine peut la faire. C'est exactement ce qu'on a vu ici.
On n'automatise jamais un process qu'on ne comprend pas. Observer le réel, c'est identifier ce qui se répète, ce qui bloque, ce qui pourrait tourner tout seul. Clarifier avant d'optimiser.
Cette observation nous a révélé 4 étapes clés à automatiser : recherche d'entreprises, enrichissement social, vérification des contacts, export structuré.
Cartographier le système : du chaos à la clarté
Maintenant qu'on a observé le réel, on peut cartographier. Rendre visible ce qui était invisible. Structurer ce qui était chaotique.
Le parcours des données visualisé :
Une fois la carte établie, on peut expliquer les outils qu'on utilise.
La stack technique : lisible, utile, élégante
On ne code pas pour impressionner, on code pour simplifier. Voici les outils qu'on a utilisés et pourquoi on les a choisis eux plutôt que d'autres.
Une API, c'est comme une prise électrique : tu te branches dessus pour accéder aux données d'un service sans tout recoder toi-même. Pappers expose une API qui te donne les infos des entreprises françaises directement. Tu envoies une requête, tu reçois du JSON.
Chaque outil a été choisi pour une raison précise. Pas parce que c'est trendy, mais parce que ça résout un problème réel. On n'a pas choisi au hasard. On a testé, comparé, mesuré. Et on continue d'ajuster selon ce qu'on apprend.
Maintenant qu'on connaît les outils, voyons comment on les connecte ensemble.
Construire le système : 3 piliers
Une fois l'architecture clarifiée, on construit. Trois piliers : le pipeline (orchestration), la résilience (gestion des crashs), l'enrichissement intelligent (respect des APIs).
1. Le pipeline : chef d'orchestre invisible
Le pipeline, c'est le chef d'orchestre. Il coordonne tous les outils, gère le flux de données, assure que chaque étape se fait dans le bon ordre. Voici le code qui orchestre tout ça :
Un pipeline, c'est une suite d'étapes automatiques. Comme une chaîne de production : chaque étape transforme les données et les passe à l'étape suivante. Ici, ça part d'une liste d'entreprises et ça finit par un fichier de prospects qualifiés, sans intervention humaine.
Voir le code complet — pipeline.py
def run_pipeline(output_csv: str = "prospects_unifies.csv") -> pd.DataFrame:
"""Fonction principale qui orchestre la collecte multi-segments"""
logger.info(f"=== LANCEMENT PIPELINE (Target: {TARGET_TOTAL}) ===")
try:
unified_results: List[UnifiedProspect] = []
completed_segments = []
# Reprise automatique si crash détecté
if _checkpoint_manager.has_checkpoint():
logger.warning("⚠️ Checkpoint détecté - reprise en cours...")
checkpoint_data = _checkpoint_manager.load_last_checkpoint()
if checkpoint_data:
unified_results = checkpoint_data.get("prospects", [])
completed_segments = checkpoint_data.get("completed_segments", [])
# Collecte par segment (PME, Studios, Locales)
for segment_name, collector in SEGMENT_COLLECTORS:
if segment_name in completed_segments:
logger.info(f"⏭️ Segment {segment_name} déjà terminé")
continue
target = TARGETS_CONFIG.get(segment_name, 0)
if target > 0:
segment_prospects = collector(target)
unified_results.extend(segment_prospects)
completed_segments.append(segment_name)
# Checkpoint après chaque segment (sécurité)
_checkpoint_manager.save_checkpoint(
prospects=unified_results,
segment_name=segment_name
)
# Déduplication intelligente
df = unify_dicts(unified_results)
df = df.drop_duplicates(subset=["email", "linkedin", "company_name"])
# Export CSV final
df.to_csv(output_csv, index=False, encoding="utf-8")
logger.info(f"💾 CSV généré → {output_csv} ({len(df)} lignes)")
_checkpoint_manager.clear_checkpoints()
return df
except Exception as e:
logger.error(f"❌ Erreur pipeline: {e}")
logger.error("💡 Les checkpoints conservés - relancez pour reprendre")
raise
Ce que ce code fait en langage clair :
- Il lance la collecte sur chaque segment (PME, Studios, Locales) l'un après l'autre
- Après chaque segment collecté, il sauvegarde l'état (checkpoint) au cas où ça plante
- Il vérifie s'il y a un checkpoint existant au démarrage : si oui, il reprend là où ça s'était arrêté
- À la fin, il déduplique les prospects (pas de doublons) et exporte tout dans un CSV propre
En image : ce qui se passe quand tu lances le script à 9h00
Tu tapes python pipeline.py dans ton terminal avec les critères "Agences de communication, Paris, 10-50 employés". Voici ce qui se passe en temps réel :
Voir l'exécution complète — Terminal
# 9h00:00 - Démarrage
=== LANCEMENT PIPELINE (Target total: 200) ===
Répartition: {'pme': 200, 'studios': 0, 'locales': 0}
# 9h00:15 - Pappers trouve les entreprises
🏢 [PME] Collecte de 200 prospects via Pappers + Dropcontact
📊 Pappers: 47 entreprises trouvées pour "agence de communication"
📊 Pappers: 38 entreprises pour "marketing digital"
📊 Pappers: 52 entreprises pour "conseil en communication"
✅ Total Pappers: 137 entreprises collectées
# 9h12:30 - Apify scrape Instagram
📸 [Instagram] Scraping de 137 profils...
✅ 89 profils Instagram actifs récupérés
# 9h25:45 - Dropcontact enrichit les emails
🔍 [DROPCONTACT] Batch 1/14 envoyé (10 prospects)
⏳ Tentative 1/10 - Attente 10s (total: 0s)
⏳ Tentative 2/10 - Attente 15s (total: 10s)
✅ [DROPCONTACT] Prêt en 25s!
📧 Emails vérifiés: 8/10 valides (80%)
# 9h42:20 - Qualification IA
🤖 [IA] Claude analyse et score les prospects...
✅ 200 prospects qualifiés et scorés
# 9h47:00 - Export final
🔄 Déduplication: 12 doublons supprimés
💾 CSV généré → prospects_unifies.csv (188 lignes)
✅ Pipeline terminé avec succès
Résultat : En 47 minutes, tu as un fichier CSV avec 188 prospects qualifiés, emails vérifiés, profils Instagram, scoring IA. Pendant ce temps, tu as pu faire autre chose : réfléchir à ta stratégie, préparer tes approches, boire un café.
⏱️ Timeline d'exécution : 47 minutes
De la requête initiale au CSV final
2. La résilience : construire pour la durée
Le système peut crasher. C'est normal. Une API qui plante, un quota épuisé, une coupure réseau : ça arrive. L'important, c'est de savoir reprendre.
Le système de checkpoint : Après chaque segment collecté (PME, Studios, Locales), on sauvegarde l'état complet. Si ça plante au milieu du segment 2, on ne recommence pas tout : on reprend là où on s'est arrêté.
Voir le code — checkpoint_manager.py
def save_checkpoint(self, prospects: List, segment_name: str) -> None:
"""Sauvegarde l'état complet après chaque segment"""
checkpoint_file = os.path.join(
self.checkpoint_dir,
f"checkpoint_{segment_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
)
checkpoint_data = {
"timestamp": datetime.now().isoformat(),
"segment": segment_name,
"prospects_count": len(prospects),
"prospects": prospects,
"progress_info": {
"completed_segments": completed_segments
}
}
with open(checkpoint_file, 'w', encoding='utf-8') as f:
json.dump(checkpoint_data, f, ensure_ascii=False, indent=2)
self.logger.info(f"💾 Checkpoint sauvegardé: {len(prospects)} prospects")
Ce que ce code fait en langage clair :
- À chaque fin de segment (PME, Studios, Locales), il sauvegarde tout dans un fichier JSON avec timestamp
- Ce fichier contient : tous les prospects collectés jusqu'ici + la liste des segments terminés
- Si le système crash, au redémarrage il charge ce fichier et reprend au segment suivant
- Résultat : on ne perd jamais plus d'un segment de travail, même en cas de crash
🛡️ Système de reprise automatique
En cas de crash, on reprend là où on s'est arrêté
Philosophie : L'IA amplifie ce qu'on lui donne. Si on lui donne de la fragilité, elle plante. Si on lui donne de la résilience, elle tient.
3. L'enrichissement intelligent : respecter les outils
Dropcontact vérifie et enrichit les emails. Mais leur API utilise un système de polling : tu envoies une requête, tu attends qu'elle soit traitée, tu récupères le résultat.
Le polling, c'est comme attendre un colis : tu envoies ta commande, puis tu reviens régulièrement voir si c'est prêt. L'API Dropcontact ne te répond pas immédiatement : elle te donne un numéro de suivi, et tu dois revenir vérifier si le traitement est terminé.
Le problème : Si tu demandes trop vite ("C'est prêt ? C'est prêt ? C'est prêt ?"), tu surcharges l'API et elle te bloque. Si tu attends trop longtemps, tu perds du temps.
La solution : Exponential backoff. On commence par attendre 10 secondes, puis 15s, 20s, 30s... jusqu'à 60s max. On s'adapte progressivement à la vitesse de traitement de l'API.
Voir le code — dropcontact_enrich.py
def dropcontact_enrich_batch_polling(batch_prospects: List[Dict]) -> List[Dict]:
"""Enrichissement avec polling et exponential backoff"""
# Préparer le batch pour Dropcontact
dropcontact_batch = [
{
"first_name": p.get("prenom", ""),
"last_name": p.get("nom", ""),
"company": p.get("entreprise_nom", ""),
"num_siren": p.get("siren", ""),
}
for p in batch_prospects
]
# Envoyer la requête initiale
resp = requests.post(
"https://api.dropcontact.com/v1/enrich/all",
headers=dropcontact_headers,
json={"data": dropcontact_batch, "siren": True, "language": "fr"}
)
request_id = resp.json().get("request_id")
# Exponential backoff: 10s, 15s, 20s, 30s, 45s, 60s...
delays = [10, 15, 20, 30, 30, 45, 45, 60, 60, 60]
total_time = 0
for attempt, delay in enumerate(delays, 1):
logger.info(f"⏳ Tentative {attempt} - Attente {delay}s (total: {total_time}s)")
time.sleep(delay)
total_time += delay
# Vérifier si le résultat est prêt
result_url = f"https://api.dropcontact.com/v1/enrich/all/{request_id}"
result_resp = requests.get(result_url, headers=dropcontact_headers)
if result_resp.status_code == 200:
result_data = result_resp.json()
if result_data.get("success"):
logger.info(f"✅ Enrichissement terminé en {total_time}s")
return result_data.get("data", [])
return []
Ce que ce code fait en langage clair :
- On envoie une requête avec 10 prospects à Dropcontact → on reçoit un numéro de suivi (request_id)
- On attend 10 secondes, puis on vérifie si c'est prêt. Pas prêt ? On attend 15s. Toujours pas ? 20s. Puis 30s...
- On augmente progressivement l'attente jusqu'à 60s max, pour ne pas spammer l'API
- Dès que l'API répond "success: true", on récupère les données enrichies et on passe au batch suivant
⏳ Exponential Backoff : attente progressive
On augmente progressivement le temps d'attente pour ne pas surcharger l'API
T = tentative (ex. T3 = 3ᵉ relance)
On ne force pas les APIs. On s'adapte. Le rate limiting, l'exponential backoff, les retries : c'est du respect pour les outils qu'on utilise. Une bonne automation, c'est celle qui dure, pas celle qui impressionne pendant 5 minutes avant de planter.
Et après ? Le document final
Une fois toutes ces étapes terminées (collecte, enrichissement, qualification), on se retrouve avec un document structuré. Voici à quoi ressemble le fichier CSV final :
| # | Entreprise | Secteur | Score IA | |||
|---|---|---|---|---|---|---|
| 1 | Agence Horizon | Communication | contact@agence-horizon.fr | /company/agence-horizon | 92 | @agencehorizon |
| 2 | Studio Pixel | Design Graphique | hello@studiopixel.com | /company/studio-pixel | 88 | @studiopixel |
| 3 | Digital Makers | Marketing Digital | contact@digitalmakers.fr | /company/digital-makers | 76 | @digitalmakers |
| 4 | Creative Lab Paris | Agence Créative | info@creativelab-paris.fr | /company/creativelab-paris | 94 | @creativelabparis |
| 5 | BrandForge | Branding | team@brandforge.io | /company/brandforge | 71 | @brandforge_io |
| 6 | Lumière Studio | Production Vidéo | production@lumiere-studio.fr | /company/lumiere-studio | 85 | @lumierestudio |
| ... 182 lignes suivantes | ||||||
188 lignes de prospects qualifiés, prêts à être contactés. Chaque ligne contient tout ce dont tu as besoin pour démarrer une vraie conversation : email vérifié, profil LinkedIn, score de qualification. Pas de doublons, pas d'emails pourris, pas de vérifications manuelles à faire.
Ce qui change vraiment : l'humain libéré
On n'automatise pas pour remplacer les gens. On automatise pour leur rendre leur capacité à penser, créer, décider. Une bonne automation se fait oublier : elle tourne en silence pendant que tu te concentres sur ce qui compte vraiment.
Ce qu'on a appris : transparence totale
1. L'architecture modulaire est critique
Chaque segment (PME, Studios, Locales) est indépendant. Si un segment plante, les autres continuent. On isole les erreurs, on ne les laisse pas contaminer tout le système.
2. Le cache = respect des ressources
On a implémenté un cache persistant de 90 jours pour les données Apify. Pourquoi repayer pour scraper les mêmes profils Instagram ? On garde en mémoire, on économise les crédits API, on respecte les outils qu'on utilise.
3. La gestion d'erreurs > le code parfait
Le système peut crasher. C'est normal. L'important, c'est qu'il sache reprendre là où il s'est arrêté. Les checkpoints, les logs détaillés, les retry logic : c'est du respect pour l'utilisateur.
Voir le code — error_handling.py
try:
# Tentative d'enrichissement Dropcontact
enriched_data = dropcontact_enrich_batch(prospects)
except Exception as e:
# Logger l'erreur mais continuer le process
logger.error(f"❌ Erreur Dropcontact: {e}")
# Sauvegarder les prospects non enrichis quand même
save_prospects_csv_common(prospects, "prospects_partiels.csv")
# Le pipeline continue avec les autres segments
pass
4. Observer avant de coder, toujours
On a passé plusieurs jours à regarder le process manuel avant d'écrire une ligne de code. Cette phase d'observation nous a évité de construire un système qui ne correspond pas au réel. Terrain > théorie.
On te montre les erreurs, les choix, le réel. Pas de bullshit marketing. Le code n'est pas parfait, le système peut crasher. Mais il est résilient, et il libère du temps humain. C'est ça qui compte.
Human Intelligence
On automatise les tâches répétitives pour libérer ce qui fait de toi un humain : penser, créer, décider.