orgmode-to-gemini-blog/scan_links.py

174 lines
6.8 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""
Scanne les fichiers Org d'un dossier pour répertorier les liens.
Génère un JSON listant pour chaque article ses liens et statistiques par domaine.
"""
import os
import re
import json
import argparse
import urllib.parse
from datetime import datetime
from collections import Counter, defaultdict
def extract_links_from_org(file_path):
"""Extrait tous les liens d'un fichier Org"""
links = []
try:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
# Pattern pour trouver les liens Org-mode: [[url][description]] ou [[url]]
pattern = r'\[\[((?:https?:\/\/|www\.)[^\]]+)\](?:\[([^\]]*)\])?'
matches = re.finditer(pattern, content)
for match in matches:
url = match.group(1)
description = match.group(2) if match.group(2) else ""
links.append({"url": url, "description": description})
# Chercher aussi les liens simples http:// ou https:// qui ne sont pas dans la syntaxe [[]]
simple_pattern = r'(?<!\[)(?:https?:\/\/)[^\s\]]+(?!\])'
simple_matches = re.finditer(simple_pattern, content)
for match in simple_matches:
url = match.group(0)
links.append({"url": url, "description": ""})
except Exception as e:
print(f"Erreur lors de la lecture de {file_path}: {e}")
return links
def extract_domain(url):
"""Extrait le nom de domaine d'une URL"""
try:
parsed_url = urllib.parse.urlparse(url)
domain = parsed_url.netloc
# Supprimer www. si présent
if domain.startswith('www.'):
domain = domain[4:]
return domain
except Exception:
return url
def scan_directory(directory):
"""Scanne un répertoire et ses sous-répertoires pour trouver des fichiers Org"""
article_links = {}
domain_counter = Counter()
# Parcourir tous les sous-dossiers
for root, _, files in os.walk(directory):
for file in files:
if file.endswith('.org'):
file_path = os.path.join(root, file)
file_links = extract_links_from_org(file_path)
if file_links:
relative_path = os.path.relpath(file_path, os.path.dirname(directory))
article_links[relative_path] = file_links
# Compter les domaines
for link in file_links:
domain = extract_domain(link["url"])
if domain:
domain_counter[domain] += 1
print(f"Trouvé {len(file_links)} liens dans {relative_path}")
return article_links, domain_counter
def merge_scan_results(results1, results2):
"""Combine les résultats de deux scans"""
merged_article_links = {**results1[0], **results2[0]}
merged_domain_counter = results1[1] + results2[1]
return merged_article_links, merged_domain_counter
def main():
parser = argparse.ArgumentParser(description="Scanner les liens dans les fichiers Org")
parser.add_argument("source_dir", help="Dossier source contenant les fichiers Org")
parser.add_argument("--output", default="links_report.json",
help="Fichier JSON de sortie (défaut: links_report.json)")
args = parser.parse_args()
# Construire le nom du fichier de sortie avec le nom du dossier source
if args.output == "links_report.json":
args.output = f"links_report_{args.source_dir}.json"
folder_fr = f'sources/{args.source_dir}/lang_fr'
folder_en = f'sources/{args.source_dir}/lang_en'
if not os.path.exists(folder_fr) and not os.path.exists(folder_en):
print(f"Les dossiers {folder_fr} et {folder_en} n'existent pas.")
return
# Initialiser des résultats vides
combined_article_links = {}
combined_domain_counter = Counter()
# Scanner le dossier français s'il existe
if os.path.exists(folder_fr):
print(f"Scan des liens dans {folder_fr}...")
fr_results = scan_directory(folder_fr)
combined_article_links, combined_domain_counter = fr_results
print(f"Trouvé {len(combined_article_links)} articles en français avec {sum(len(links) for links in combined_article_links.values())} liens")
# Scanner le dossier anglais s'il existe
if os.path.exists(folder_en):
print(f"Scan des liens dans {folder_en}...")
en_results = scan_directory(folder_en)
# Si nous avons déjà des résultats en français, les combiner
if combined_article_links:
combined_article_links, combined_domain_counter = merge_scan_results(
(combined_article_links, combined_domain_counter),
en_results
)
else:
# Sinon, utiliser directement les résultats anglais
combined_article_links, combined_domain_counter = en_results
print(f"Total cumulé: {len(combined_article_links)} articles avec {sum(len(links) for links in combined_article_links.values())} liens")
# Préparer les données pour le JSON
output_data = {
"meta": {
"generated_at": datetime.now().isoformat(),
"source_directory": args.source_dir,
"total_articles": len(combined_article_links),
"total_links": sum(len(links) for links in combined_article_links.values()),
"total_domains": len(combined_domain_counter)
},
"article_links": combined_article_links,
"domains": {
domain: count for domain, count in sorted(
combined_domain_counter.items(), key=lambda x: x[1], reverse=True
)
}
}
# Créer le dossier de sortie si nécessaire
output_dir = os.path.dirname(args.output)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
# Sauvegarder les données dans un fichier JSON
with open(args.output, 'w', encoding='utf-8') as f:
json.dump(output_data, f, ensure_ascii=False, indent=2)
print(f"Rapport généré avec succès dans {args.output}")
print(f"Total articles: {len(combined_article_links)}")
print(f"Total liens: {sum(len(links) for links in combined_article_links.values())}")
print(f"Total domaines: {len(combined_domain_counter)}")
# Afficher les 10 domaines les plus fréquents
print("\nTop 10 des domaines les plus fréquents:")
for domain, count in list(combined_domain_counter.most_common(10)):
print(f" {domain}: {count} liens")
if __name__ == "__main__":
main()