Que fait Rechercheur de Relations Presse Concurrentielles ?
Fournissez l'URL de votre produit. Il trouve vos concurrents, recherche tous les canaux RP qu'ils ont utilisés (actualités, podcasts, communautés), met en évidence les canaux qui apparaissent chez plusieurs concurrents (vos cibles éprouvées), trouve le journaliste ou l'hôte pour chacun, et rédige un pitch froid personnalisé pour votre produit sur chaque canal de niveau 1.
Politique zéro hallucination : Chaque canal, nom de journaliste, angle d'article et détail de pitch dans le résultat doit provenir d'un résultat de recherche Tavily spécifique ou de la page produit récupérée. Ceci s'applique à :
- Noms des concurrents : doivent apparaître dans les résultats de recherche Tavily, pas dans les connaissances d'entraînement de l'IA
- Noms des canaux : doivent avoir une URL dans les résultats de recherche
- Noms des journalistes/hôtes : doivent apparaître textuellement dans un extrait Tavily
- Angles d'articles : extraits uniquement des titres d'articles/épisodes dans les résultats de recherche
- Brouillons de pitchs : référencent des preuves spécifiques issues des données de recherche + de l'analyse du produit
Erreurs Courantes
| L'agent voudra... | Pourquoi c'est faux |
|---|---|
| Nommer un journaliste à partir de ses connaissances d'entraînement | Chaque nom de journaliste doit provenir d'un extrait de résultat de recherche. Écrire "Sarah Perez couvre les startups chez TechCrunch" de mémoire est une hallucination. |
| Lister des canaux sans URLs de preuve | Chaque canal dans le résultat doit avoir au moins une URL provenant des résultats de recherche RP prouvant qu'un concurrent y a été présenté. |
| Sauter l'étape de confirmation du concurrent | Montrez toujours les concurrents découverts et attendez la confirmation de l'utilisateur. De mauvais concurrents signifient des recherches gaspillées et un résultat inutile. |
| Générer des pitchs génériques ("Nous aimerions être présentés") | Chaque pitch doit référencer un angle spécifique issu des preuves ET un différenciateur spécifique issu de l'analyse du produit. |
| Marquer un canal comme Niveau 1 avec seulement 1 occurrence de concurrent | Niveau 1 = 3+ concurrents. Niveau 2 = exactement 2. Niveau 3 = 1. Ne promouvez pas des canaux qui n'ont pas fait leurs preuves. |
| Utiliser des tirets cadratins dans le résultat | Remplacez tous les tirets cadratins (—) par des traits d'union (-). |
Lire les Fichiers de Référence Avant Chaque Exécution
cat references/pr-channel-types.md
cat references/pitch-guide.md
cat references/tier-scoring.md
Étape 1 : Vérification de Configuration
echo "TAVILY_API_KEY: ${TAVILY_API_KEY:+défini}${TAVILY_API_KEY:-NON DÉFINI -- requis}"
echo "FIRECRAWL_API_KEY: ${FIRECRAWL_API_KEY:+défini}${FIRECRAWL_API_KEY:-non défini, l'extraction Tavily sera utilisée comme solution de repli}"
Si TAVILY_API_KEY est manquant : Arrêtez immédiatement. Dites à l'utilisateur : "TAVILY_API_KEY est requis pour rechercher les concurrents et trouver la couverture RP. Il n'y a pas de solution de repli. Obtenez-la sur app.tavily.com -- niveau gratuit : 1000 crédits/mois (environ 43 exécutions complètes à ~23 recherches/exécution). Ajoutez-la à votre fichier .env."
Si seulement FIRECRAWL_API_KEY est manquant : Continuez. L'extraction Tavily sera utilisée pour la récupération d'URL.
Étape 2 : Analyse de l'Entrée
Collectez à partir de la conversation :
product_url: l'URL à récupérer (obligatoire, sauf si l'utilisateur colle une description directement)product_name: facultatif, dérivé de la page si non fournigeography: facultatif -- États-Unis / Europe / mondial. Par défaut : États-Unis
Si l'utilisateur fournit uniquement une description collée (pas d'URL) : Sautez les Étapes 3 et 4. Allez directement à l'Étape 4 (analyse du produit) en utilisant le texte collé comme product_content. Définissez page_source sur user_description et notez-le dans data_quality_flags.
Si ni URL ni description : Demandez : "Quelle est l'URL de votre produit ou startup ? Ou collez une courte description : ce qu'il fait, pour qui, et ce qui le rend différent des concurrents."
Dérivez l'identifiant du produit :
PRODUCT_SLUG=$(python3 -c "
from urllib.parse import urlparse
import sys
url = 'URL_HERE'
if url.startswith('http'):
host = urlparse(url).netloc.replace('www.', '')
print(host.split('.')[0])
else:
import re
print(re.sub(r'[^a-z0-9]', '-', url[:30].lower()).strip('-'))
")
echo "Identifiant du produit : $PRODUCT_SLUG"
Étape 3 : Récupération de la Page Produit
Principal : Firecrawl (si FIRECRAWL_API_KEY est défini)
curl -s -X POST https://api.firecrawl.dev/v1/scrape \
-H "Authorization: Bearer $FIRECRAWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "URL_HERE", "formats": ["markdown"], "onlyMainContent": true}' \
| python3 -c "
import sys, json
d = json.load(sys.stdin)
content = d.get('data', {}).get('markdown', '') or d.get('markdown', '')
print(f'Récupéré via Firecrawl : {len(content)} caractères')
open('/tmp/cprf-product-raw.md', 'w').write(content)
"
Solution de repli : Extraction Tavily (si FIRECRAWL_API_KEY n'est pas défini)
curl -s -X POST https://api.tavily.com/extract \
-H "Content-Type: application/json" \
-d "{\"api_key\": \"$TAVILY_API_KEY\", \"urls\": [\"URL_HERE\"]}" \
| python3 -c "
import sys, json
d = json.load(sys.stdin)
content = d.get('results', [{}])[0].get('raw_content', '')
print(f'Récupéré via extraction Tavily : {len(content)} caractères')
open('/tmp/cprf-product-raw.md', 'w').write(content)
"
Point de contrôle :
python3 -c "
content = open('/tmp/cprf-product-raw.md').read()
if len(content) < 200:
print('ERREUR : moins de 200 caractères récupérés')
else:
print(f'Contenu OK : {len(content)} caractères')
"
Si le contenu < 200 caractères : Arrêtez la récupération. Dites à l'utilisateur : "La page produit n'a retourné aucun contenu lisible -- le site utilise probablement du JavaScript et a bloqué la récupération. Veuillez coller une courte description directement : ce qu'il fait, pour qui, et ce qui le rend différent."
Étape 4 : Analyse du Produit (IA)
Affichez le contenu de la page :
python3 -c "
content = open('/tmp/cprf-product-raw.md').read()[:5000]
print('=== PAGE PRODUIT (5000 premiers caractères) ===')
print(content)
"
Instructions IA : Analysez la page produit ci-dessus et extrayez :
product_name: le nom du produit ou de l'entrepriseone_line_description: ce qu'il fait, pour qui, proposition de valeur principale. Moins de 20 mots. Pas de langage marketing. Exemple : "Automatisation CI/CD pour les équipes de développeurs qui auto-hébergent leurs pipelines."industry_taxonomy:l1(niveau supérieur : ex. outils développeur / fintech / healthtech / grand public),l2(secteur : ex. devops / paiements / télémédecine),l3(niche spécifique : ex. automatisation CI/CD / paiements intégrés / consultation vidéo asynchrone). Les étiquettes vagues comme "technologie" seules ne sont pas acceptables.differentiators: exactement 2-3 éléments spécifiques qui distinguent ce produit des concurrents génériques. Ceux-ci alimentent directement les brouillons de pitchs -- soyez précis. Exemple : ["Exécuteur de pipeline auto-hébergé -- aucune donnée ne quitte votre infrastructure", "Prise en charge native des monorepos avec génération dynamique d'étapes"]icp:buyer_persona(intitulé du poste),company_type,company_sizegeography_bias: États-Unis / Europe / mondial / peu clairpage_source: "live_page" ou "user_description"
Écrivez dans /tmp/cprf-product-analysis.json :
python3 << 'PYEOF'
import json
analysis = {
# À REMPLIR à partir de votre analyse ci-dessus
"product_name": "",
"one_line_description": "",
"industry_taxonomy": {"l1": "", "l2": "", "l3": ""},
"differentiators": [],
"icp": {"buyer_persona": "", "company_type": "", "company_size": ""},
"geography_bias": "États-Unis",
"page_source": "live_page"
}
json.dump(analysis, open('/tmp/cprf-product-analysis.json', 'w'), indent=2)
print('Analyse du produit écrite.')
PYEOF
Vérifiez :
python3 -c "
import json
a = json.load(open('/tmp/cprf-product-analysis.json'))
print('Produit :', a['product_name'])
print('Industrie :', a['industry_taxonomy']['l1'], '>', a['industry_taxonomy']['l2'], '>', a['industry_taxonomy']['l3'])
print('Différenciateurs :')
for d in a['differentiators']:
print(f' - {d}')
"
Étape 4b : Phase 1 -- Découverte des Concurrents
ls scripts/research.py 2>/dev/null && echo "script trouvé" || echo "ERREUR : scripts/research.py introuvable -- impossible de continuer"
python3 scripts/research.py \
--phase discover \
--product-analysis /tmp/cprf-product-analysis.json \
--tavily-key "$TAVILY_API_KEY" \
--output /tmp/cprf-competitors-raw.json
Affichez les résultats pour examen par l'IA :
python3 -c "
import json
data = json.load(open('/tmp/cprf-competitors-raw.json'))
print(f'Recherches exécutées : {len(data[\"competitor_searches\"])}')
for s in data['competitor_searches']:
print(f'\nRequête : {s[\"query\"]}')
print(f'Réponse : {s.get(\"answer\",\"\")[:400]}')
for r in s.get('results', [])[:5]:
print(f' - {r[\"title\"]} | {r[\"url\"]}')
print(f' {r.get(\"content\",\"\")[:200]}')
"
Instructions IA : Lisez les résultats de recherche ci-dessus. Choisissez exactement 5 sociétés concurrentes qui :
- Sont nommées dans les titres, réponses ou extraits des résultats de recherche
- Sont dans la même niche L3 que le produit analysé
- Sont des produits concurrents réels (pas des agences, cabinets de conseil ou articles de liste)
- Sont distincts les uns des autres (pas la même société sous des noms différents)
Pour chaque concurrent, écrivez : name, url (depuis le résultat de recherche où il est apparu), description (une phrase tirée de l'extrait), source_url (l'URL du résultat de recherche où il a été trouvé).
Étape 5 : Confirmation des Concurrents
Montrez les concurrents découverts à l'utilisateur :
python3 << 'PYEOF'
import json
analysis = json.load(open('/tmp/cprf-product-analysis.json'))
# À REMPLIR : 5 concurrents à partir des résultats de recherche ci-dessus
candidates = [
# {"name": str, "url": str, "description": str, "source_url": str}
]
print(f"\nTrouvé 5 concurrents pour {analysis['product_name']} dans {analysis['industry_taxonomy']['l3']} :\n")
for i, c in enumerate(candidates, 1):
print(f" {i}. {c['name']} -- {c['description']}")
print(f" {c['url']}")
print("\n")
data = json.load(open('/tmp/cprf-competitors-raw.json'))
data['competitor_candidates'] = candidates
json.dump(data, open('/tmp/cprf-competitors-raw.json', 'w'), indent=2)
PYEOF
Dites à l'utilisateur : "Voici les 5 concurrents que je vais rechercher pour la couverture RP. Ajoutez-en, retirez-en, ou échangez-en -- ou dites 'parfait' pour continuer."
Attendez la confirmation. Si l'utilisateur modifie la liste (ajoute/retire/échange), mettez à jour les candidats en conséquence. Ensuite, écrivez la liste confirmée :
python3 << 'PYEOF'
import json
# À REMPLIR : liste des concurrents confirmée (après examen par l'utilisateur)
confirmed = [
# {"name": str, "url": str}
]
json.dump({"confirmed_competitors": confirmed}, open('/tmp/cprf-competitors-confirmed.json', 'w'), indent=2)
print(f"Confirmé {len(confirmed)} concurrent(s) pour la recherche RP.")
for c in confirmed:
print(f" - {c['name']} ({c['url']})")
PYEOF
Étape 6 : Recherche RP sur Trois Canaux (Phase 2)
python3 scripts/research.py \
--phase pr-research \
--competitors /tmp/cprf-competitors-confirmed.json \
--product-analysis /tmp/cprf-product-analysis.json \
--tavily-key "$TAVILY_API_KEY" \
--output /tmp/cprf-pr-raw.json
Ceci exécute 3 recherches par concurrent (15 au total) :
- Track A (Éditorial) :
"[concurrent]" featured press coverage TechCrunch Forbes Wired article interview - Track B (Podcasts) :
"[concurrent]" founder CEO podcast interview appeared on episode - Track C (Communautés) :
"[concurrent]" site:reddit.com OR site:news.ycombinator.com OR site:producthunt.com
Affichez le résumé de la couverture :
python3 -c "
import json
data = json.load(open('/tmp/cprf-pr-raw.json'))
print(f'Concurrents recherchés : {data[\"competitors_researched\"]}')
print()
for r in data['results']:
print(f'{r[\"competitor\"]} :')
for track, tdata in r['tracks'].items():
n = len(tdata.get('results', []))
print(f' {track:12} : {n} résultats')
"
Si les 3 tracks pour un concurrent retournent 0 résultat : Ce concurrent a une très faible couverture presse. Notez-le dans data_quality_flags et continuez -- le modèle multi-concurrents fonctionnera toujours avec les 4 restants.
Étape 7 : Analyse des Modèles (IA)
Affichez tous les résultats RP bruts :
python3 -c "
import json
data = json.load(open('/tmp/cprf-pr-raw.json'))
for r in data['results']:
print(f'\n=== {r[\"competitor\"]} ===')
for track, tdata in r['tracks'].items():
print(f'\n--- Track {track.upper()} ---')
print(f'Requête : {tdata[\"query\"]}')
print(f'Réponse : {tdata.get(\"answer\",\"\")[:400]}')
for item in tdata.get('results', [])[:5]:
print(f' Titre : {item[\"title\"]}')
print(f' URL : {item[\"url\"]}')
print(f' Extrait : {item.get(\"content\",\"\")[:200]}')
"
Instructions IA : Lisez TOUS les résultats de recherche ci-dessus. Construisez un plan de fréquence des canaux.
Étape 1 -- Normalisez les URLs en domaine racine : https://techcrunch.com/2023/06/article-title → techcrunch.com. https://open.spotify.com/episode/... → identifier comme podcast (épisode spotify). https://www.reddit.com/r/devops/ → reddit.com/r/devops.
Étape 2 -- Comptez les occurrences : Combien de concurrents différents sont apparus dans les résultats de chaque canal racine ? Un canal qui apparaît dans le Track A du Concurrent A ET le Track A du Concurrent B a une fréquence de 2.
Étape 3 -- Hiérarchisez les canaux (suivez references/tier-scoring.md) :
- Niveau 1 : apparu chez 3+ concurrents
- Niveau 2 : apparu chez exactement 2 concurrents
- Niveau 3 : apparu chez 1 concurrent
Étape 4 -- Extrayez les angles d'articles à partir des titres d'articles/épisodes dans les résultats. Classez chacun comme : annonce-de-financement / lancement-de-produit / histoire-du-fondateur / article-de-tendance / création-de-catégorie / tutoriel / comparaison / prix. N'inférez pas -- ne classez que les angles visibles dans les titres.
Étape 5 -- Classez le type de canal pour chacun : éditorial / podcast / communauté / newsletter.
Écrivez dans /tmp/cprf-pr-patterns.json :
python3 << 'PYEOF'
import json
# À REMPLIR -- canaux apparaissant chez 3+ concurrents
# Chaque : {"channel_name": str, "channel_url": str, "channel_type": str,
# "frequency": int, "found_in_competitors": [str],
# "evidence_urls": [str], "story_angles_used": [str],
# "journalist_name": "", "journalist_beat": ""}
tier_1_channels = []
# À REMPLIR -- canaux apparaissant chez exactement 2 concurrents
# Chaque : {"channel_name": str, "channel_url": str, "channel_type": str,
# "frequency": int, "found_in_competitors": [str],
# "evidence_urls": [str], "story_angles_used": [str],
# "journalist_name": "", "journalist_beat": ""}
tier_2_channels = []
# À REMPLIR -- canaux apparaissant chez 1 concurrent
# Chaque : {"channel_name": str, "channel_url": str, "channel_type": str,
# "frequency": int, "found_in_competitors": [str],
# "evidence_urls": [str], "story_angles_used": [str],
# "journalist_name": "", "journalist_beat": ""}
tier_3_channels = []
patterns = {
"tier_1_channels": tier_1_channels,
"tier_2_channels": tier_2_channels,
"tier_3_channels": tier_3_channels,
"cross_competitor_highlights": {
"most_common_channel_type": "",
"top_story_angle": "",
"total_unique_channels": 0
},
"data_quality_flags": []
}
json.dump(patterns, open('/tmp/cprf-pr-patterns.json', 'w'), indent=2)
print('Modèles RP écrits.')
PYEOF
Étape 8 : Identification des Journalistes
Pour chaque canal de Niveau 1 et Niveau 2 :
python3 scripts/research.py \
--phase journalist-lookup \
--patterns /tmp/cprf-pr-patterns.json \
--product-analysis /tmp/cprf-product-analysis.json \
--tavily-key "$TAVILY_API_KEY" \
--output /tmp/cprf-journalists-raw.json
Ceci exécute :
- Pour chaque canal éditorial :
"publication_name" technology editor writer journalist - Pour chaque podcast :
"podcast_name" host - Pour chaque communauté : identifiez le modérateur ou le propriétaire de la communauté
Affichez les résultats :
python3 -c "
import json
data = json.load(open('/tmp/cprf-journalists-raw.json'))
for r in data['results']:
print(f'\n=== {r[\"channel_name\"]} ===')
for s in r['searches']:
print(f'Requête : {s[\"query\"]}')
print(f'Réponse : {s.get(\"answer\",\"\")[:400]}')
for item in s.get('results', [])[:3]:
print(f' Titre : {item[\"title\"]} | {item[\"url\"]}')
print(f' Extrait : {item.get(\"content\",\"\")[:200]}')
"
Instructions IA : Pour chaque canal de Niveau 1 et Niveau 2, lisez les résultats de recherche ci-dessus et trouvez le journaliste ou l'hôte exact. Le nom doit apparaître textuellement dans un extrait de résultat de recherche. Si vous ne trouvez pas de nom exact, marquez "journalist_status" comme "could_not_confirm" au lieu d'en inventer un.
Étape 9 : Agrégation Assistée par IA du Résultat Final
python3 << 'PYEOF'
import json
# Charger TOUS les fichiers de données
product = json.load(open('/tmp/cprf-product-analysis.json'))
competitors_data = json.load(open('/tmp/cprf-competitors-raw.json'))
confirmed = json.load(open('/tmp/cprf-competitors-confirmed.json'))
patterns = json.load(open('/tmp/cprf-pr-patterns.json'))
journalist_data = json.load(open('/tmp/cprf-journalists-raw.json'))
# --- Construire la liste des concurrents ---
competitors_list = []
for c in confirmed['confirmed_competitors']:
# Trouver les détails dans les données brutes
for candidate in competitors_data.get('competitor_candidates', []):
if candidate['name'] == c['name']:
competitors_list.append({
"name": candidate['name'],
"url": candidate['url'],
"description": candidate['description'],
"source_url": candidate['source_url']
})
break
# --- Construire le résultat final ---
final_output = {
"product": product,
"competitors": competitors_list,
"media_analysis": patterns,
"outreach_targets": []
}
Instructions IA : Pour chaque canal de Niveau 1, créez une entrée outreach_target avec :
- Les détails du canal
- Le journaliste ou l'hôte (si trouvé)
- Angle d'article obligatoire : Rédigez un angle spécifique pour l'histoire, basé sur les preuves. Exemple : "Alors que les équipes de devops adoptent les runners auto-hébergés pour la conformité, le Produit X les aide à gérer des pipelines complexes avec une génération dynamique d'étapes." Ne copiez pas un titre de résultat de recherche textuellement comme angle -- synthétisez un nouveau pitch.
- Brouillon de pitch froid obligatoire : Rédigez un pitch de 3-5 phrases. Structure :
- Ligne d'objet : accrocheuse, inclut le nom du produit
- Ouverture : référence au travail du journaliste (tiré des résultats de recherche)
- Corps : angle spécifique + différenciateur du produit + preuve de traction ou citation
- Appel à l'action : suggestion spécifique pour une interview, démo ou commentaire
Règles strictes :
- Chaque angle d'article et pitch doit être unique par canal, même si certains canaux partagent des extraits
- Les angles et pitches doivent provenir des résultats de recherche ET de l'analyse du produit, jamais de connaissances générales
- Si un canal n'a pas de journaliste confirmé, incluez une note sur la façon de trouver le bon contact
# Rassembler les canaux de Niveau 1 avec les journalistes
for channel in patterns['tier_1_channels']:
# Trouver les données du journaliste pour ce canal
journalist_info = {}
for jr in journalist_data.get('results', []):
if jr['channel_name'] == channel['channel_name']:
for s in jr['searches']:
answer = s.get('answer', '')
for item in s.get('results', []):
content = item.get('content', '')
# Chercher des noms dans les résultats
pass
break
target = {
"channel_name": channel['channel_name'],
"channel_url": channel['channel_url'],
"channel_type": channel['channel_type'],
"frequency": channel['frequency'],
"found_in_competitors": channel['found_in_competitors'],
"tier": 1,
"evidence_urls": channel['evidence_urls'],
"virtual_name": "",
"virtual_beat": "",
"story_angle": "", # AI génère ceci
"cold_pitch_draft": "" # AI génère ceci
}
final_output['outreach_targets'].append(target)
# Sauvegarder le résultat final
json.dump(final_output, open('/tmp/cprf-final-output.json', 'w'), indent=2, ensure_ascii=False)
print(f"Résultat final écrit avec {len(final_output['competitors'])} concurrents et {len(final_output['outreach_targets'])} cibles de démarchage.")
PYEOF
Étape 10 : Sortie et Résumé
python3 << 'PYEOF'
import json
final = json.load(open('/tmp/cprf-final-output.json'))
p = final['product']
print("=" * 60)
print(f"RÉSULTAT DE RECHERCHE RP : {p['product_name']}")
print("=" * 60)
print(f"\nDescription : {p['one_line_description']}")
print(f"Industrie : {p['industry_taxonomy']['l1']} > {p['industry_taxonomy']['l2']} > {p['industry_taxonomy']['l3']}")
print(f"Géographie : {p['geography_bias']}")
print(f"\nConcurrents analysés ({len(final['competitors'])}):")
for c in final['competitors']:
print(f" - {c['name']} : {c['description'][:80]}...")
print(f" Preuve : {c['source_url'][:80]}")
ma = final['media_analysis']
print(f"\nCanaux découverts : {ma['cross_competitor_highlights']['total_unique_channels']}")
print(f"Niveau 1 (3+ concurrents) : {len(ma['tier_1_channels'])}")
print(f"Niveau 2 (exactement 2) : {len(ma['tier_2_channels'])}")
print(f"Niveau 3 (1 concurrent) : {len(ma['tier_3_channels'])}")
print(f"Type de canal le plus courant : {ma['cross_competitor_highlights']['most_common_channel_type']}")
print(f"Angle d'article principal : {ma['cross_competitor_highlights']['top_story_angle']}")
print(f"\nCibles de démarchage de Niveau 1 ({len(final['outreach_targets'])}):")
for t in final['outreach_targets']:
print(f"\n Canal : {t['channel_name']} ({t['channel_url'][:50]})")
print(f" Type : {t['channel_type']}")
print(f" Journaliste : {t['virtual_name'] or t.get('journalist_status', 'Non confirmé')}")
print(f" Angle : {t['story_angle'][:100]}...")
print(f" Pitch :\n{t['cold_pitch_draft'][:200]}...")
print(f" Preuves : {', '.join(t['evidence_urls'][:3])}")
print(f"\n{'=' * 60}")
print("Prochaines étapes suggérées :")
print(f" 1. Vérifier les journalistes non confirmés via LinkedIn/Twitter")
print(f" 2. Personnaliser davantage les pitchs avec des exemples spécifiques")
print(f" 3. Envoyer les pitchs espacés de 2-3 jours")
print(f" 4. Suivi après 7 jours si pas de réponse")
print(f"{'=' * 60}")
PYEOF
Signalements de qualité des données :
python3 -c "
import json
f = json.load(open('/tmp/cprf-final-output.json'))
flags = f['media_analysis'].get('data_quality_flags', [])
if flags:
print('AVERTISSEMENTS SUR LA QUALITÉ DES DONNÉES :')
for fl in flags:
print(f' ⚠ {fl}')
else:
print('Aucun avertissement sur la qualité des données.')
"
Post-traitement
- Remplacez tous les tirets cadratins (—) par des traits d'union (-) dans la sortie
- Assurez-vous que chaque URL dans
evidence_urlsest accessible (pas de liens morts) - Vérifiez que les noms de journalistes correspondent textuellement aux extraits de recherche
- Supprimez les fichiers temporaires :
rm -f /tmp/cprf-*.json /tmp/cprf-*.md