Files
2026-09-09 15:59:41 +02:00

18 KiB
Raw Permalink Blame History

PdfScrapper

Navigateur visuel qui permet de sélectionner du texte sur des pages web et de l'accumuler dans un PDF, au fil de la navigation.

Fonctionnalités

  • Navigateur intégré (moteur Chromium via QtWebEngine) : barre d'adresse, précédent / suivant / recharger, session persistante (cookies/connexions conservées entre deux lancements).

  • Sauvegarde de projet ("📂 Ouvrir…" / "💾 Enregistrer" / "💾 Enregistrer sous…", dans la barre d'outils) : un projet regroupe les extraits capturés, le style PDF et la page en cours, dans un fichier .pdfscrap (JSON). Enregistrer réutilise le fichier déjà nommé (ou propose Enregistrer sous la première fois) ; Ouvrir recharge un projet précédent (avec confirmation si des extraits non enregistrés seraient remplacés). Une sauvegarde automatique tourne aussi en tâche de fond, toutes les 60 secondes et à la fermeture de l'appli : tant qu'aucun fichier n'a été nommé explicitement, elle écrit dans un fichier de récupération par défaut, rechargé automatiquement au prochain lancement (comme la restauration de session d'un navigateur) — dès qu'un Enregistrer sous est fait, elle bascule sur ce fichier.

  • Capture de texte (sélection manuelle) : sélectionnez du texte dans la page puis cliquez sur "➕ Ajouter la sélection au PDF" (ou Ctrl+Shift+A). L'extrait est stocké avec le titre de la page, son URL et l'heure de capture.

  • Capture de bloc au survol : le bloc de contenu sous le curseur (paragraphe, item de liste, section, article...) se surligne automatiquement quand le mode est actif ; un clic dessus l'ajoute directement au PDF, sans avoir à sélectionner le texte à la main. Deux façons de l'activer, combinables :

    • Maintenir Ctrl enfoncé dans la page : actif tant que la touche est tenue, désactivé dès qu'on la relâche (pratique pour capturer un bloc à la volée sans repasser par la souris ensuite).
    • Ctrl+Shift+B (ou le bouton "🎯 Mode capture au survol") : reste actif en continu jusqu'à nouvelle bascule, y compris après avoir suivi un lien vers la page suivante.

    Tant que le mode est actif (par l'une ou l'autre méthode), le clic normal (suivre les liens) est suspendu sur la page.

  • Panneau latéral : liste des extraits capturés (réorganisables, supprimables, renommables). Si un extrait est sélectionné dans la liste, chaque nouvelle capture s'insère juste à côté de lui — au-dessus ou en dessous selon la case à cocher "Insérer les nouvelles captures au-dessus de la sélection" (cochée par défaut = au-dessus) — et les captures suivantes continuent de s'enchaîner du même côté du repère sans avoir à re-sélectionner à chaque fois. Sans sélection, les captures s'accumulent simplement à la fin, quel que soit ce réglage.

  • Renommer un extrait : double-cliquez sur un extrait dans la liste (ou sélectionnez-le puis "✏️ Renommer") pour changer son titre affiché — utilisé aussi comme titre de section dans le PDF exporté.

  • Tableau des extraits : le panneau latéral affiche les extraits capturés dans un vrai tableau (colonnes Heure, Titre, Aperçu, Lien, avec en-tête), plutôt qu'une simple liste de texte.

  • Lier plusieurs extraits en un seul chapitre : quand un chapitre est réparti sur plusieurs pages web (donc plusieurs extraits capturés séparément), l'icône de la colonne Lien permet de relier un extrait au suivant dans le tableau — à la manière du maillon de chaîne entre calques adjacents dans GIMP. Les extraits reliés sont exportés comme une seule section dans le PDF : un seul titre (celui du premier extrait du groupe, toujours renommable) et une seule entrée de sommaire, sans saut de page entre eux. Les extraits qui suivent le premier d'un groupe sont repérés par un préfixe "↳" (colonne Titre, et onglet "Corriger le texte"). Deux extraits directement reliés ne peuvent pas être réordonnés (▲/▼) sans être déliés au préalable, pour éviter d'inverser silencieusement le sens du lien.

  • Aperçu et style du PDF ("🖌 Aperçu et style du PDF…", barre d'outils) : fenêtre dédiée qui affiche un rendu en direct du PDF final (via QtPdf) pendant que vous ajustez les réglages, répartis en deux onglets :

    • 🎨 Style : titre du document, format de page (A4/Letter), police globale (Helvetica/Times/Courier — s'applique à tout le document), taille du texte et des titres, couleur des titres, couleur du texte, couleur d'arrière-plan, marges, affichage ou non de l'URL/date sous chaque titre, saut de page entre extraits ou non, et inclusion d'un sommaire (voir ci-dessous).
    • ✏️ Corriger le texte : liste des extraits, avec un champ titre et une zone de texte éditable pour corriger le contenu capturé directement (fautes de frappe, nettoyage de texte scrappé, etc.) — les modifications s'appliquent immédiatement à l'extrait (y compris dans le panneau latéral de la fenêtre principale) et l'aperçu se régénère automatiquement.

    L'aperçu se régénère automatiquement (avec un léger délai anti-rebond) à chaque changement. Les réglages de style et les corrections de texte faits ici restent actifs pour l'export "normal" depuis le panneau latéral, même après avoir fermé cette fenêtre.

  • Sommaire cliquable : activable depuis l'onglet Style ("Inclure un sommaire", actif par défaut dès qu'il y a plus d'un extrait). Insère une page de sommaire juste après le titre du document, avec un lien cliquable vers chaque section — liens internes PDF standard (annotations /Link avec destination directe), utilisables dans n'importe quel lecteur PDF.

  • Détection de liens de "suite" : les liens dont le texte — ou le texte d'un conteneur voisin proche (icône + légende, ex. "Partie Suivante" à côté du lien) — ressemble à suivant / next / chapitre suivant / lire la suite / part 2 etc. (ou une balise <link rel="next">) sont surlignés en orange et listés dans le panneau, pour enchaîner facilement la lecture et la capture sans les chercher manuellement. La détection tourne en continu (MutationObserver JS, résultats poussés vers Python via le pont QWebChannel) plutôt qu'une seule fois au chargement — nécessaire car beaucoup de sites (SPA, pagination injectée après coup en JavaScript) ajoutent ce genre de lien bien après le signal de fin de chargement.

  • Scraping assisté par IA locale (llama-cpp-python, aucune donnée envoyée en ligne) : configurez un modèle via "⚙️ Configurer l'IA locale" (fichier .gguf de votre choix — non fourni avec l'appli, liens de téléchargement vérifiés dans la section Installation ci-dessous). Trois usages, combinables :

    • 🤖 Extraire (IA) : demande au modèle d'extraire le texte narratif principal de la page (menus/pubs/commentaires écartés) et l'ajoute au PDF — alternative à la sélection manuelle/au mode survol, utile sur les pages au contenu trop fragmenté pour un clic unique.
    • 🤖 Lien suivant (IA) : demande au modèle de désigner, parmi tous les liens de la page, celui qui mène le plus probablement à la suite — complète la détection heuristique quand celle-ci ne trouve rien (structures de page trop inhabituelles pour un motif texte). Pour chaque lien, le texte du conteneur parent proche est aussi transmis au modèle quand il diffère du texte du lien lui-même — utile pour les liens de pagination réduits à une icône/un chiffre sans texte propre (ex. "2", "›"), fréquents quand un chapitre est réparti sur plusieurs pages web. La fin du contenu de la page actuelle (déjà extrait, voir 🤖 Extraire (IA) — ou relu pour l'occasion pour le bouton manuel) est aussi transmise : un texte qui s'interrompt en plein milieu d'une scène est un indice fort de pagination interne, qu'un simple texte de lien ne suffit pas toujours à révéler. Le modèle indique aussi si le lien choisi mène à la suite du même chapitre (pagination interne) ou à un nouveau chapitre.
    • 🔁 Scraping auto (IA) : combine les deux en boucle — extrait, capture, trouve et suit le lien suivant, répète — jusqu'à un nombre de chapitres maximum fixé au démarrage (garde-fou demandé à chaque lancement, 0 = illimité, voir ci-dessous) ou jusqu'à ce qu'aucune suite ne soit trouvée ; une détection de boucle (retour vers une page déjà visitée) arrête aussi le processus automatiquement. Le bouton devient "⏹ Arrêter le scraping" tant qu'il tourne. L'inférence tourne sur un thread dédié (en streaming) pour ne jamais geler l'interface, avec un délai d'inactivité (2 min sans le moindre fragment de réponse généré) au-delà duquel le scraping s'arrête proprement plutôt que de rester bloqué indéfiniment si l'IA ne répond vraiment plus jamais (modèle figé, fichier corrompu...) — chaque fragment généré réarme ce délai, donc une analyse simplement lente (mais qui progresse encore) n'est jamais interrompue à tort, contrairement à une limite sur la durée totale de l'appel. Les éventuelles erreurs d'inférence n'affichent plus de boîte de dialogue bloquante pendant un scraping automatique (juste un arrêt + un message dans le journal), pour ne jamais donner l'impression que l'appli s'est figée faute d'un clic sur une fenêtre passée inaperçue. Une fenêtre de suivi (non modale, se réaffiche à chaque lancement) s'ouvre en même temps : journal horodaté de chaque étape (lecture de la page, extraction IA, recherche puis choix du lien suivant, arrêt et sa raison...), barre de progression "chapitre X/N" (animation indéterminée en mode illimité), et son propre bouton "⏹ Arrêter le scraping". La fermer (X) la masque seulement — le scraping continue en arrière-plan si on ne l'arrête pas, la fenêtre se rouvrant au prochain démarrage de scraping. Chapitres multipages liés automatiquement : quand le modèle juge que la page suivante est la suite du même chapitre (voir ci-dessus), l'extrait qui vient d'être capturé est automatiquement marqué 🔗 (même mécanisme que la liaison manuelle, voir Lier plusieurs extraits en un seul chapitre ci-dessous) — les pages d'un même chapitre réparti sur plusieurs URLs ressortent donc comme une seule section dans le PDF final, sans action manuelle.
    • Détection des tables des matières : avant de tenter d'en extraire le contenu, chaque page est d'abord analysée pour savoir si c'est une table des matières / liste de chapitres plutôt qu'un chapitre en soi (page d'accueil d'une série, sommaire...). Si oui : aucun faux "chapitre" n'est capturé depuis cette liste de liens, et la recherche du lien suivant vise explicitement le premier chapitre de la liste plutôt qu'une suite arbitraire parmi tous les chapitres listés — sans cette détection, le scraping automatique tend à se perdre dès qu'il démarre sur une page de ce type. S'applique aussi bien au scraping automatique qu'au bouton manuel "🤖 Lien suivant (IA)".
    • Limite "0" = illimité / détection automatique : entrer 0 comme nombre maximum de chapitres tente d'abord de déterminer le nombre total de chapitres à partir du titre/texte de la page de départ (ex. une table des matières, "Chapitre 3 sur 12"...) avant même de commencer — trouvé, ce nombre devient la limite ; indéterminable, le scraping tourne sans limite fixe et s'arrête de lui-même dès qu'aucune suite n'est trouvée (voir garde-fous ci-dessus).
    • Prompt personnalisable : le prompt système utilisé pour le choix du lien suivant (Lien suivant (IA) et Scraping auto) est éditable dans "⚙️ Configurer l'IA locale" — utile pour affiner le comportement sur un site particulièrement atypique. Un bouton "↺ Réinitialiser au défaut" restaure le texte intégré à l'appli. Laisser le champ tel quel revient à utiliser ce prompt par défaut ; le modifier doit conserver le format de réponse JSON décrit dans le texte (index/same_chapter), sous peine de casser l'analyse de la réponse du modèle.

Tous les boutons ci-dessus (projet, capture, aperçu, IA, export) sont regroupés dans la barre d'outils en haut de la fenêtre ; le panneau latéral ne garde que le tableau des extraits, ses actions de réorganisation, et les liens suggérés sur la page courante.

Installation

python -m venv .venv
.venv\Scripts\activate      # Windows
pip install -r requirements.txt

Lancement

python run.py

llama-cpp-python compile une extension native au moment de l'installation (pas de wheel précompilé pour toutes les configurations) — l'installation peut donc prendre plusieurs minutes selon la machine. Les fonctionnalités IA restent optionnelles : sans modèle configuré, le reste de l'application fonctionne normalement (les boutons IA affichent un message clair invitant à configurer un modèle plutôt que d'échouer silencieusement).

IA locale (optionnel)

  1. Téléchargez un modèle au format GGUF (liens vérifiés ci-dessous — le fichier n'est pas fourni avec l'appli, à télécharger une seule fois).
  2. Dans PdfScrapper, ouvrez "⚙️ Configurer l'IA locale" et indiquez le chemin du fichier .gguf.
  3. Les boutons "🤖 Extraire (IA)", "🤖 Lien suivant (IA)" et "🔁 Scraping automatique (IA)" sont alors utilisables.

Modèles GGUF recommandés

Existence, nom de fichier exact et taille vérifiés au moment de la rédaction. Prendre la variante Q4_K_M (bon compromis qualité/vitesse/ taille pour du CPU) sauf indication contraire — c'est le lien direct donné ci-dessous.

Modèle Taille (Q4_K_M) Pourquoi celui-ci Téléchargement
Qwen3-4B-Instruct-2507 (recommandé par défaut) ~2,5 Go Le plus récent de la liste, très bon suivi d'instructions (utile pour la sortie JSON du choix de lien) et bon en français, reste rapide sur CPU lien direct · page du modèle
Qwen2.5-3B-Instruct (le plus léger) ~1,9 Go Le plus petit/rapide de la liste, correct en français, suffisant pour dépanner sur une machine modeste lien direct · page du modèle
Mistral-7B-Instruct-v0.3 (meilleur français) ~4,4 Go Modèle Mistral AI (français), référence historique en qualité de français ; demande plus de RAM/CPU lien direct · page du modèle
Llama-3.2-3B-Instruct (alternative légère) ~2,0 Go Généraliste léger, bonne alternative si Qwen ne convient pas lien direct · page du modèle
Qwen2.5-7B-Instruct (meilleure qualité) ~4,7 Go Si ≥ 8 Go de RAM disponibles et qu'on veut la meilleure qualité d'extraction lien direct · page du modèle

Pour une machine avec peu de RAM, préférer les modèles 3-4B. Pour plus de qualité (et si le CPU/la RAM suit), les 7B donnent de meilleurs résultats sur l'extraction de contenu comme sur le choix du lien suivant.

Structure du projet

pdfscrapper/
    main.py            # point d'entrée (QApplication)
    main_window.py      # fenêtre principale : navigateur + panneau latéral
    link_detector.py     # heuristiques JS de détection des liens "suivant"
    block_picker.py      # heuristique JS de surlignage/capture de bloc au survol
    bridge.py             # pont QWebChannel (JS -> Python) pour la capture de bloc
    clip_table.py           # tableau des extraits (panneau latéral) : colonnes Heure/Titre/Aperçu/Lien
    pdf_style.py           # dataclass PdfStyle (réglages de mise en forme du PDF)
    pdf_builder.py          # génération du PDF (reportlab) : style, fond, sommaire/liens
    preview_window.py        # fenêtre d'aperçu en direct (QtPdf), style et correction du texte
    ai_config.py               # dataclass AiConfig, persistée via QSettings
    ai_settings_dialog.py       # fenêtre de configuration du modèle IA local (.gguf)
    ai_worker.py                  # exécution des appels IA sur un thread dédié (non bloquant)
    local_ai.py                    # inférence llama-cpp-python : extraction + choix du lien suivant
    auto_scrape_dialog.py           # fenêtre de suivi du scraping auto (journal + progression)
    models.py                        # dataclass Clip (un extrait capturé) + regroupement en chapitres
    project_io.py                     # sauvegarde/chargement de projet (.pdfscrap) + pointeur de session
run.py                                # lance l'application

Pistes d'évolution

  • Réglage des motifs de détection de liens depuis l'interface.
  • Export additionnel en .txt / .epub.