Aller au contenu
English

Analyser et normaliser

  • Leçon 4
  • 50 min
  • Module 2 : Ingérer les documents de façon reproductible

Ce que vous saurez faire

Transformez les documents PDF et HTML de Larkfield en texte propre que la recherche peut utiliser. Conservez la page et la section de chaque partie. Les quatre documents illisibles pour la référence de base du module 1 rejoignent alors les 32 autres.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 50 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

  • Le projet policy-assistant à la fin du module 1, dans ~/projects/policy-assistant, avec .venv actif. python -m pytest affiche 11 passed (11 tests réussis).
  • Le dépôt des exercices dans ~/projects/nextia-learning-labs, présenté dans Le corpus et les questions.
  • Si votre projet manque ou ne fonctionne plus, copiez l’instantané end-of-m01. Utiliser un instantané donne les étapes.

Testé avec Python 3.12.13 et pypdf 6.19.0 sur macOS (Apple silicon). Les commandes Windows et Linux n’ont pas été testées.

Un collègue téléphone à Grace et lui pose une question sur la garantie. Grace a la politique imprimée devant elle et la lit à voix haute. Elle ne lit pas la ligne au haut de chaque page, ni « Page 1 sur 2 ». Au tableau, elle lit une ligne de gauche à droite : « Nettoyeurs à pression : 2 ans, la pompe 3 ans. » Elle ne lit pas la première colonne de haut en bas, puis la deuxième. Quand un mot est coupé en fin de ligne, « greenhou- » et « ses », elle dit « greenhouses » (serres). Et quand le collègue demande « Où est-ce écrit? », elle répond « Page 1, sous Durées de garantie. »

Une personne fait tout cela sans y penser. Un programme ne fait rien de tout cela sans instructions.

Le problème : quatre documents que la recherche ne peut pas lire

Section intitulée « Le problème : quatre documents que la recherche ne peut pas lire »

La référence de base du module 1 cherchait dans les sections des 32 documents Markdown. Quatre documents étaient exclus. La version 4 de la politique de retour et la politique de livraison sont des pages HTML. La politique de garantie et le guide d’entretien du PW-2200 sont des fichiers PDF. Les conséquences étaient visibles dans la référence de base. Pour les questions sur les tableaux, son hit@5 était de 0,125. Le tableau de garantie contient les réponses, et il est dans le PDF. « Quel ensemble de pompe convient au PW-2200? » renvoyait d’abord le tableau des pièces du PW-2400. Le guide du PW-2200 est un PDF.

Pourquoi ne pas extraire le texte des fichiers et y faire une recherche? Python peut le faire en trois lignes. Amira a d’abord essayé avec pypdf, une bibliothèque courante pour les fichiers PDF. Voici ce qu’elle donne pour la page 2 de la politique de garantie. (Vous exécuterez les mêmes lignes dans l’exercice guidé, après avoir installé pypdf.)

Python session, in ~/projects/policy-assistant
from pypdf import PdfReader
reader = PdfReader("corpus/formats/warranty-policy.pdf")
for line in reader.pages[1].extract_text().splitlines()[:10]:
print(repr(line))

repr affiche chaque ligne avec ses guillemets, ce qui permet de voir les caractères qui ne s’affichent pas. La sortie :

'Larkfield | Larkfield warranty policy | Version 5, effective from 2026-01-01'
'Printed copies are not controlled. Check the help centre for the current version.'
'Page 2 of 2'
'\x7f'
'Damage from the wrong fuel, voltage or chemicals.'
'\x7f'
'Commercial or trade use, except for the Larkfield Pro range.'
'\x7f'
'Consumable parts, such as blades, nozzles, filters and cutting line, unless the table lists them.'
'How to make a claim'

Trois problèmes apparaissent déjà à l’écran. Les trois premières lignes sont l’en-tête et le pied de page, présents sur chaque page. '\x7f' est un caractère de contrôle invisible : pypdf représente la puce de la liste par ce caractère. Et la liste Ce qui n’est pas couvert commence à la page 1 et continue ici. L’en-tête se retrouve donc au milieu de la liste. C’est une limite de page : l’endroit où une page finit et où la suivante commence.

Le tableau est pire. Voici les lignes 11 à 26 de la page 1 :

Python session (continued)
lines = reader.pages[0].extract_text().splitlines()
for line in lines[10:26]:
print(repr(line))
'Warranty periods by product category'
' Product category'
'Examples'
'Warranty period'
'Exceptions'
'Hand garden tools'
'spades, rakes, pruners,'
'shears'
'2 years'
'none'
'Garden furniture'
'chairs, tables, loungers,'
'benches'
'2 years'
'cushions and fabric covers: 1'
'year'

Chaque cellule occupe sa propre ligne, et certaines cellules en occupent deux. Les rangées ont disparu. « 2 ans » apparaît deux fois, et seul l’ordre des lignes indique à quel produit cette durée appartient. Plus bas, lines[43:47] montre un mot coupé en deux : 'Sheds and greenhou-' et 'ses' (remises et serres).

La page HTML a le problème inverse : trop de texte. Enlevez toutes les balises, et vous obtenez tout ce que le navigateur affiche autour de la politique :

Python session (continued)
import re
html = open("corpus/formats/warranty-policy.html", encoding="utf-8").read()
text = " ".join(re.sub(r"<[^>]+>", " ", html).split())
print(text[:330])
print(text[-250:])
Larkfield warranty policy | Larkfield Help Centre body { font-family: sans-serif; } .cookie { background: #eee; } window.dataLayer = window.dataLayer || []; dataLayer.push({"page": "warranty-policy"}); We use cookies to improve your visit. Accept all Settings Larkfield Shop Garden Home Help centre My account Help centre &rsaquo;
ety notices win over this policy. Related articles Contact us and opening hours Help centre: frequently asked questions Was this article helpful? Yes No &copy; 2026 Larkfield. All prices in Canadian dollars. Privacy &middot; Terms &middot; Contact us

Règles de style, script, bannière de témoins, menu, liens vers d’autres articles et pied de page : rien de cela n’est la politique. Ce texte s’appelle du texte standard (boilerplate) : le même texte sur chaque page d’un site ou d’un document.

L’idée : analyser en blocs et garder le chemin vers la source

Section intitulée « L’idée : analyser en blocs et garder le chemin vers la source »

Analyser signifie lire un fichier dans son format et le transformer en une structure que votre programme comprend. Normaliser signifie donner la même forme au même contenu, peu importe le fichier d’origine. La normalisation uniformise les caractères, les espaces et les tableaux, et retire ce qui n’est pas du contenu. Ensemble, ces opérations font ce que Grace faisait au téléphone.

Une chose ne doit pas se perdre en chemin : l’origine de chaque morceau de texte. C’est la provenance : le fichier, la version, la section et la page d’un morceau de texte. La réponse de Grace, « page 1, sous Durées de garantie », indique la provenance. Sans elle, l’assistant ne peut pas montrer ses preuves, et personne ne peut vérifier une réponse dans le document réel.

L’analyseur du projet donne la même forme à chaque document, quel que soit son format. Un bloc est une partie du document : le titre, un titre de section, du texte (paragraphe ou liste) ou un tableau. Chaque bloc conserve sa section, sa page dans un PDF et sa position exacte dans le texte propre.

Trois fichiers sources à gauche : warranty-policy.md avec des métadonnées d'en-tête, des titres et un tableau; warranty-policy.html avec des balises meta, une bannière de témoins, un menu, l'article et un pied de page; warranty-policy.pdf avec 2 pages, un en-tête, un pied de page et un tableau tracé avec des lignes. Chacun passe par son propre analyseur : parse_markdown lit les métadonnées d'en-tête et retire les marques de gras; parse_html garde seulement l'article et lit les balises meta; parse_pdf supprime les lignes répétées, reconstruit les rangées du tableau et rejoint les mots coupés. Les trois flèches convergent vers un ParsedDocument avec les métadonnées, puis 20 blocs, chacun avec sa section, sa page et ses caractères, puis le même texte de 2 744 caractères provenant des trois fichiers.
Trois fichiers, trois analyseurs, un seul résultat. Les versions Markdown, HTML et PDF de la politique de garantie donnent les mêmes métadonnées, les mêmes 20 blocs et le même texte.
Description textuelle du diagramme

Trois fichiers sources : la politique de garantie en Markdown (métadonnées d’en-tête, titres commençant par ##, tableau écrit avec des caractères |), en HTML (balises meta, bannière de témoins, menu, article et pied de page) et en PDF (2 pages, en-tête et pied de page sur chacune, tableau tracé avec des lignes). Chacun passe par son propre analyseur. parse_markdown lit les métadonnées d’en-tête et retire les marques de gras **. parse_html garde seulement le <article> et lit les métadonnées dans les balises <meta>. parse_pdf supprime les lignes répétées, reconstruit les rangées du tableau et rejoint les mots coupés. Les trois donnent un ParsedDocument : les métadonnées (identifiant du document, version, dates, étiquette d’accès, langue), puis 20 blocs. Chaque bloc est un titre, un titre de section, du texte ou un tableau, avec sa section, sa page et ses positions de caractères. Le texte compte les mêmes 2 744 caractères pour les trois fichiers.

Le dossier corpus/formats/ existe pour cette raison : il contient quatre documents, chacun dans les trois formats. Si l’analyseur fonctionne bien, les trois fichiers d’un document donnent exactement le même texte. Un test du projet le vérifie pour les quatre documents.

Petit exemple détaillé : comment l’analyseur PDF décide

Section intitulée « Petit exemple détaillé : comment l’analyseur PDF décide »

Un fichier PDF ne stocke pas de paragraphes, de rangées ni de titres. Il stocke des instructions comme « dessiner ce texte à cet endroit, avec cette police ». pypdf peut fournir chaque ligne avec sa position : la page, x et y. x est la distance depuis le bord gauche, et y la distance depuis le bas, en points. Un point vaut 1/72 de pouce, et les pages de Larkfield mesurent 792 points de haut. pypdf donne aussi la police et sa taille.

Avec ces nombres, cinq règles font le travail. Lisez l’image de haut en bas : chaque groupe contient des lignes réelles de la politique de garantie et la règle appliquée.

Cinq groupes de lignes réelles du PDF, chacun avec la page, x, y et la taille de police, et la règle appliquée par parse.py. 1, en-tête et pied de page : l'en-tête à y 742 et le pied de page Page 1 of 2 à y 40 sur la page 1, identiques sur la page 2; même texte au même endroit sur chaque page, en ignorant les chiffres, donc ils sont supprimés. 2, titre et titres de section : la taille 17 indique le titre, la taille 13 en gras indique un titre de section. 3, une rangée de tableau : cinq lignes de taille 9,5 à x 76, 186,6, 322,8 et 407,9; cisailles est 12 points sous bêches, râteaux, sécateurs, donc dans la même cellule; la rangée devient Outils de jardin à main, bêches râteaux sécateurs cisailles, 2 ans, aucune. 4, un mot coupé en fin de ligne : Sheds and greenhou- à y 350 et ses à y 338 deviennent Sheds and greenhouses (remises et serres). 5, une puce : U+007F à x 74 sur la page 2 devient un tiret de liste devant Dommages causés par un carburant inadéquat.
Ce que pypdf lit dans la politique de garantie, et les cinq règles de parse.py. Chaque nombre est une position réelle du fichier.
Description textuelle du diagramme

Cinq groupes de lignes réelles, chacun avec sa page, x, y et sa taille de police, et la règle appliquée par parse.py. Groupe 1, l’en-tête et le pied de page : l’en-tête à y 742 et « Page 1 sur 2 » à y 40 sur la page 1, puis les mêmes lignes sur la page 2. La règle : le même texte au même endroit sur chaque page est un en-tête ou un pied de page. Les chiffres sont ignorés, donc « Page 1 sur 2 » et « Page 2 sur 2 » deviennent tous deux « Page # sur # ». Ces lignes sont supprimées. Groupe 2 : le titre en taille 17 à y 702, et « Durées de garantie par catégorie de produits » en taille 13, en gras. La règle : la taille 17 indique le titre, la taille 13 en gras indique un titre de section. Ce titre nomme la section des blocs qui suivent. Groupe 3, une rangée de tableau : « Outils de jardin à main » à x 76, « bêches, râteaux, sécateurs, » à x 186,6, y 518, « cisailles » à x 186,6, y 506, « 2 ans » à x 322,8 et « aucune » à x 407,9, tous en taille 9,5. La règle : la taille 9,5 indique une cellule de tableau; un même x indique une même colonne. Une ligne 12 points plus bas appartient à la même cellule. Une ligne plus de 15 points plus bas commence une nouvelle rangée. Le résultat : « Outils de jardin à main | bêches, râteaux, sécateurs, cisailles | 2 ans | aucune ». Groupe 4 : « Sheds and greenhou- » à y 350 et « ses » à y 338. La règle : une lettre suivie d’un trait d’union en fin de ligne, puis une lettre minuscule, forment un seul mot : « Sheds and greenhouses » (remises et serres). Groupe 5 : le caractère U+007F à x 74 sur la page 2, et « Dommages causés par un carburant inadéquat, … » à x 86. La règle : pypdf représente la puce de cette police par U+007F, qui devient donc un tiret de liste.

Prenons la rangée du tableau, le cas le plus difficile. « bêches, râteaux, sécateurs, » est à y 518 et « cisailles » à y 506, dans la même colonne. La différence est de 518 − 506 = 12 points, soit une ligne de petit texte : c’est la même cellule. « Mobilier de jardin » est à y 488, 30 points sous « Outils de jardin à main ». C’est plus de 15, donc une nouvelle rangée commence. Les cellules d’une rangée sont jointes avec |, comme dans le tableau Markdown.

La règle du trait d’union demande de la prudence, car les codes de produits en contiennent aussi. « greenhou- » finit par une lettre et un trait d’union, et « ses » commence par une minuscule : un seul mot. Mais une ligne qui finit par « PK- », suivie de « 2200-07 », reste séparée, car un chiffre suit. Un test du projet vérifie les deux cas.

Une autre règle traite les PDF d’une seule page. Rien ne se répète, donc la règle 1 ne trouve aucun en-tête. L’analyseur supprime alors aussi toute ligne dans les 60 points du haut et du bas de la page. C’est là que le gabarit de Larkfield place l’en-tête et le pied de page.

Ces règles conviennent aux fichiers PDF de Larkfield parce qu’ils viennent tous du même gabarit. Elles ne constituent pas un lecteur PDF général.

La commande parse du projet affiche les blocs que l’analyseur trouve. Pour le PDF de la politique de garantie :

Fenêtre de terminal
python -m policy_assistant parse corpus/formats/warranty-policy.pdf
pdf | doc_id=warranty-policy, title=Larkfield warranty policy, version=5, effective_from=2026-01-01, owner=Support (Grace), access=public, language=en, doc_type=policy
[ 0-25 ] p1 title | Larkfield warranty policy
[ 27-51 ] p1 heading What the warranty covers | What the warranty covers
[ 53-186 ] p1 text What the warranty covers | Our warranty covers defects in materials and workmanship under normal…
[ 188-255 ] p1 text What the warranty covers | We repair the item, replace it, or refund it. We choose the remedy.
[ 257-369 ] p1 text What the warranty covers | A repaired or replacement item keeps the remaining period of the orig…
[ 371-407 ] p1 heading Warranty periods by product… | Warranty periods by product category
[ 409-1453 ] p1 table Warranty periods by product… | Product category | Examples | Warranty period | Exceptions Hand garde…
[ 1455-1515 ] p1 text Warranty periods by product… | If a product fits two categories, the longer period applies.
[ 1517-1536 ] p1 heading What is not covered | What is not covered
[ 1538-1967 ] p1 text What is not covered | - Normal wear. - Misuse, accidents, and repairs or changes made by so…
[ 1969-1988 ] p2 heading How to make a claim | How to make a claim
[ 1990-2075 ] p2 text How to make a claim | Contact the help desk with your order number and a photo or short vid…
[ 2077-2173 ] p2 text How to make a claim | For power tools and pressure washers, also give the serial number fro…
[ 2175-2276 ] p2 text How to make a claim | Do not send the product back until we ask you to. We send a free retu…
[ 2278-2295 ] p2 heading Proof of purchase | Proof of purchase
[ 2297-2409 ] p2 text Proof of purchase | Your order number is your proof of purchase. For a gift, the gift rec…
[ 2411-2430 ] p2 heading Customers in Quebec | Customers in Quebec
[ 2432-2579 ] p2 text Customers in Quebec | Customers in Quebec also have the legal warranty under Quebec law, in…
[ 2581-2596 ] p2 heading Safety problems | Safety problems
[ 2598-2744 ] p2 text Safety problems | If a product smokes, sparks, overheats, smells of burning or leaks ga…

La première ligne contient les métadonnées, lues dans le champ Keywords (mots-clés) du PDF. Il y a ensuite une ligne par bloc : ses caractères dans le texte propre, sa page, son type, sa section et le début de son texte. L’en-tête et le pied de page ont disparu. Le tableau forme un bloc, des caractères 409 à 1 453, sur la page 1, dans la section Durées de garantie par catégorie de produits. La liste Ce qui n’est pas couvert forme de nouveau un seul bloc, avec des tirets au lieu de U+007F.

Exécutez la même commande pour warranty-policy.html et warranty-policy.md. Vous obtenez les mêmes 20 lignes, aux mêmes positions, mais sans numéros de page : HTML et Markdown n’ont pas de pages. Pour HTML, les métadonnées viennent des balises <meta>. L’analyseur lit seulement le <article>, donc la bannière, le menu et le pied de page n’atteignent jamais le texte.

L’analyseur nettoie aussi chaque morceau de texte de la même manière. La fonction clean() fait trois petites choses. Elle donne aux lettres accentuées une forme Unicode standard. Ainsi, « é » est toujours un caractère, et non « e » plus un accent; les documents français en ont besoin. Elle transforme les espaces insécables en espaces ordinaires. Et elle remplace une suite d’espaces par un seul espace. Une recherche de « légale » trouve alors « légale » dans chaque document.

Le nettoyage change le texte, donc l’analyseur garde le chemin vers la source. Chaque bloc a start (début) et end (fin) : sa position dans le texte propre du document, doc.text. Pour chaque bloc, doc.text[start:end] correspond exactement à son texte, et un test le vérifie. Dans les deux prochaines leçons, un segment garde le même type d’adresse, qui mène à la section et à la page.

Écrivez de telles règles quand vos documents viennent de quelques gabarits connus, comme ceux de Larkfield. Testez-les sur les fichiers réels et gardez les problèmes du texte brut dans les tests. Le fichier test_parse.py du projet vérifie que le texte PDF brut contient l’en-tête répété, le mot coupé et la puce U+007F. Il vérifie aussi que le texte propre n’en contient aucun.

Ne les utilisez pas pour tous les PDF du monde. Un PDF numérisé contient des images de pages, sans aucun texte. Il nécessite la ROC (reconnaissance optique de caractères), qui lit les lettres dans l’image. Les pages à deux colonnes, ou les tableaux imbriqués, nécessitent un outil qui comprend la mise en page. Quel que soit l’outil, vérifiez sa sortie sur vos propres documents, comme Amira l’a fait plus haut.

Le coût, c’est du code à maintenir. Un nouveau gabarit de l’imprimeur peut rendre une règle incorrecte, sans que personne le remarque, sauf si un test échoue. L’analyseur lui-même est rapide : il lit les 36 documents en moins d’une seconde. La solution la moins coûteuse ne demande souvent aucun code : demandez le fichier source à son propriétaire. L’équipe de Grace écrit la plupart des politiques en Markdown, qui conserve les titres et les tableaux.

L’analyseur est long (environ 340 lignes). Vous le copiez donc depuis l’instantané et le lisez, plutôt que de le taper.

  1. Dans le terminal, allez dans ~/projects et récupérez les plus récents fichiers des exercices :

    Fenêtre de terminal
    cd ~/projects
    git -C nextia-learning-labs pull

    Si vous avez téléchargé les exercices dans un fichier ZIP, téléchargez-le de nouveau à la place.

  2. Allez dans le dossier du projet. Copiez l’analyseur, son test et les nouvelles dépendances depuis l’instantané end-of-m02. Sur macOS et Linux :

    Fenêtre de terminal
    cd policy-assistant
    cp ../nextia-learning-labs/C09/snapshots/end-of-m02/policy_assistant/parse.py policy_assistant/
    cp ../nextia-learning-labs/C09/snapshots/end-of-m02/tests/test_parse.py tests/
    cp ../nextia-learning-labs/C09/snapshots/end-of-m02/requirements.txt .

    Sur Windows, dans PowerShell :

    Fenêtre de terminal
    cd policy-assistant
    Copy-Item ..\nextia-learning-labs\C09\snapshots\end-of-m02\policy_assistant\parse.py policy_assistant\
    Copy-Item ..\nextia-learning-labs\C09\snapshots\end-of-m02\tests\test_parse.py tests\
    Copy-Item ..\nextia-learning-labs\C09\snapshots\end-of-m02\requirements.txt .
  3. Installez le nouveau paquet. requirements.txt contient maintenant pypdf==6.19.0 :

    Fenêtre de terminal
    python -m pip install -r requirements.txt

    La dernière ligne est Successfully installed pypdf-6.19.0 (pypdf-6.19.0 installé avec succès).

  4. Lancez Python avec python et tapez les trois blocs de la section Le problème. Comparez votre sortie avec celle de la leçon. Quittez Python avec exit().

  5. Ouvrez policy_assistant/parse.py dans VS Code. Trouvez parse_pdf et rebuild_table, puis associez-les aux cinq règles de l’image ci-dessus.

  6. Ouvrez policy_assistant/__main__.py. Ajoutez cette ligne sous import sys :

    policy_assistant/__main__.py
    from pathlib import Path
  7. Ajoutez cette fonction sous cmd_questions :

    policy_assistant/__main__.py
    def cmd_parse(args) -> None:
    from .parse import parse
    doc = parse(Path(args.file))
    print(f"{doc.format} | " + ", ".join(f"{k}={v}" for k, v in doc.meta.items()))
    for b in doc.blocks:
    where = f"p{b.page}" if b.page else " "
    print(f"[{b.start:>5}-{b.end:<5}] {where} {b.kind:7} {short(b.section, 28):28} | {short(b.text, 70)}")
  8. Dans main(), ajoutez la commande parse sous la ligne questions. Ajoutez ensuite "parse": cmd_parse au dictionnaire de la dernière ligne. La fin de main() devient :

    policy_assistant/__main__.py, in main()
    s = sub.add_parser("questions"); s.add_argument("--kind")
    s = sub.add_parser("parse"); s.add_argument("file")
    s = sub.add_parser("search"); s.add_argument("query"); s.add_argument("--k", type=int, default=5)
    s = sub.add_parser("eval"); s.add_argument("--k", type=int, default=5)
    args = p.parse_args(argv)
    {"inventory": cmd_inventory, "questions": cmd_questions, "parse": cmd_parse, "search": cmd_search, "eval": cmd_eval}[args.command](args)
  9. Exécutez les tests :

    Fenêtre de terminal
    python -m pytest

    Vous voyez 20 passed (20 tests réussis) : les 11 tests du module 1 et les 9 nouveaux tests de l’analyseur.

  10. Analysez le PDF, puis les versions HTML et Markdown :

    Fenêtre de terminal
    python -m policy_assistant parse corpus/formats/warranty-policy.pdf
    python -m policy_assistant parse corpus/formats/warranty-policy.html
    python -m policy_assistant parse corpus/formats/warranty-policy.md

    Vérifiez que les trois sorties ont les mêmes 20 blocs et que seul le PDF a des pages.

Si l’étape 4 ou 9 affiche ModuleNotFoundError: No module named 'pypdf' (module pypdf introuvable), votre environnement virtuel n’était pas actif à l’étape 3. Activez-le (source .venv/bin/activate, ou .venv\Scripts\Activate.ps1 sur Windows), puis refaites l’étape 3.

Le guide d’entretien du PW-2200 est un PDF d’une seule page : corpus/formats/product-care-pw2200.pdf. Son texte brut commence par le même type d’en-tête et de pied de page, et « Page 1 sur 1 ». Avant d’exécuter parse sur ce fichier, faites deux prédictions. Quelle règle supprime l’en-tête et le pied de page, puisque rien ne se répète? Et dans quel bloc se trouve l’ensemble de pompe PK-2200-07?

Afficher la réponse

La règle 1 ne trouve rien, car un PDF d’une page n’a pas de deuxième page pour la comparaison. La règle des marges supprime l’en-tête (à y 742), le pied de page et « Page 1 sur 1 » (à y 40). Ils se trouvent dans les 60 points du haut et du bas. La sortie commence par le titre, et l’ensemble de pompe se trouve dans le tableau des pièces :

pdf | doc_id=product-care-pw2200, title=PW-2200 electric pressure washer: care and parts, version=2, effective_from=2025-05-01, owner=Product team, access=public, language=en, doc_type=guide
[ 0-48 ] p1 title | PW-2200 electric pressure washer: care and parts
[ 50-64 ] p1 heading Specifications | Specifications
…
[ 1000-1022 ] p1 heading Parts and part numbers | Parts and part numbers
[ 1024-1255 ] p1 table Parts and part numbers | Part | Part number | Notes Pump kit | PK-2200-07 | fits the PW-2200 o…
[ 1257-1265 ] p1 heading Warranty | Warranty
[ 1267-1362 ] p1 text Warranty | See the Larkfield warranty policy for the warranty periods of pressur…

C’est le tableau que la référence de base du module 1 ne pouvait pas voir. Une fois ce guide dans l’index, la question sur le PW-2200 peut trouver son propre tableau des pièces.

Erreur courante

Le pied de page qui répond à une question

Symptôme : Amira a d’abord essayé la méthode rapide. Elle a placé le texte brut de chaque page PDF et HTML dans un index par mots-clés, avec les 32 documents Markdown. Elle a demandé : « Quelle version de la politique est en vigueur? » Deux des cinq premiers résultats étaient des pages entières :

1. 8.777 policy-governance.md | Versions and effective dates
2. 6.622 returns-policy.v3.md | Who this policy is for
3. 6.281 warranty-policy.pdf page 2 (whole page)
4. 4.986 returns-policy.v4.html (whole page)
5. 4.517 safety-notice-kt180.md | Which kettles are affected

Le texte de la page 2 de la politique de garantie ne dit rien sur les versions. La page arrivait au troisième rang à cause de son en-tête et de son pied de page : « Version 5, en vigueur depuis le 1 janvier 2026 » et « Consultez le centre d’aide pour la version en vigueur. »

Cause : l’en-tête et le pied de page faisaient partie du texte recherché. La recherche par mots-clés compte chaque mot. Ainsi, « version en vigueur » dans le pied de page correspondait mieux à la question que la plupart des vraies sections.

Correction : analysez les documents avant de chercher et retirez le contenu répétitif. Avec les documents analysés, la même recherche donne d’abord la section Versions et dates d’entrée en vigueur de la politique de gouvernance. Viennent ensuite les sections À qui s’adresse cette politique et Ce qui a changé dans cette version des politiques de retour. Aucun résultat n’apparaît à cause d’un pied de page. Le script est reference/c09/m02/naive_extraction.py dans le dépôt du cours; vous n’avez pas besoin de l’exécuter.

Pour un vrai assistant : le contenu répétitif n’est pas du bruit inoffensif. La recherche peut trouver ce texte, et l’assistant peut le citer. Examinez ce que votre analyseur conserve avant de construire quoi que ce soit à partir de sa sortie.

Vérifiez votre compréhension

Dans vos mots : qu'est-ce que la provenance, et pourquoi l'analyseur doit-il la conserver pendant qu'il nettoie le texte?

Afficher la réponse

La provenance indique d’où vient un texte : le fichier, la version, la section et, pour un PDF, la page. L’assistant doit montrer ses preuves, et une personne doit pouvoir les vérifier dans le document réel. Le nettoyage modifie le texte : il retire les en-têtes, rejoint les mots coupés et reconstruit les lignes des tableaux. L’analyseur conserve donc la section, la page et la position de chaque bloc dans le texte nettoyé. Sans ces éléments, on ne peut pas vérifier une réponse correcte.

Vérifiez votre compréhension

Faites une prédiction : une ligne PDF « Nettoyeurs à pression » est à x 76, y 300. La ligne suivante du tableau, dans la même colonne, est à x 76, y 288. S'agit-il d'une cellule ou de deux lignes de tableau? Et si la deuxième était à y 270?

Afficher la réponse

300 − 288 = 12 points : une ligne de petit texte. Il s’agit donc de la même cellule, et les deux lignes sont jointes. 300 − 270 = 30 points dépasse 15, donc la deuxième ligne commence une nouvelle ligne de tableau. C’est la règle de rebuild_table dans parse.py.

Vérifiez votre compréhension

Tomás dit : « pypdf nous donne déjà le texte. On peut l'indexer tel quel et gagner une semaine. » Donnez deux problèmes concrets que son plan laisserait dans l'index pour la politique de garantie.

Afficher la réponse

N’importe lesquels de ces deux problèmes. L’en-tête et le pied de page figurent dans le texte de chaque page. Une question sur les versions trouve donc un pied de page (le cas d’échec). Le tableau présente une cellule par ligne. Une ligne comme « Nettoyeurs à pression | … | 2 ans | pompe : 3 ans » est donc perdue. « 2 ans » n’est plus lié à son produit. « greenhou- » et « ses » sont deux mots, donc une recherche de « greenhouses » (serres) manque cette ligne. Les puces sont le caractère invisible U+007F. Et l’en-tête de la page suivante coupe une liste qui s’étend sur deux pages.

Récapitulatif

  • L’analyse transforme chaque format en une même structure. La normalisation donne la même forme au même contenu et retire le contenu répétitif : en-têtes, pieds de page, bannières et menus.
  • Le texte brut du PDF de Larkfield contient un en-tête et un pied de page sur chaque page. Il contient aussi un tableau avec une cellule par ligne, un mot coupé en deux et des puces lues comme U+007F. Le HTML brut contient une bannière de témoins, un menu et un pied de page.
  • parse.py utilise la page, la position et la taille de police de chaque ligne. Les lignes répétées sont supprimées, et la taille permet de repérer le titre et les sous-titres. Les positions permettent de reconstruire les lignes du tableau, et les mots coupés sont rejoints.
  • Chaque bloc conserve sa provenance : sa section, sa page et sa position dans le texte nettoyé. Les versions Markdown, HTML et PDF d’un document donnent le même texte.
  • Ces règles conviennent à quelques modèles de documents connus. Les PDF numérisés ou complexes nécessitent d’autres outils, et chaque analyseur doit être testé sur les fichiers réels.

Ensuite, vous découpez les documents analysés en morceaux que la recherche renvoie : les segments.

Leçon suivante : Découper de façon réfléchie