Convertir un PDF en Texte
La campagne des législatives de 2024 a démontré qu’il était essentiel de disposer de deux types d’outils pour récupérer les textes des programmes électoraux (par exemple) afin de pouvoir en faire une analyse lexicale (Pascal Marchand, 2024). Les programmes électoraux se présentent généralement sous deux formats :
- PDF : Les documents PDF nécessitent une conversion en fichiers texte (.txt) pour être analysés dans un logiciel d’analyse lexicale tel qu’IRaMuTeQ (Pierre Ratinaud).
- Web : Les programmes disponibles en ligne doivent être « scrappés » directement via un outil ou un script de récupération de texte. (Cette approche ne sera pas abordé ici)
Dans cet article, je vous propose un script permettant de convertir des fichiers PDF en texte.
À titre d’exemple, j’ai utilisé le programme d’un parti politique et comparé les résultats obtenus avec un outil en ligne.
Le script
Le script utilise la bibliothèque PyPDF2
pour extraire le texte des pages d’un document PDF.
Les PDF en double colonne peuvent segmenter les mots, ce qui nécessite un nettoyage. Par exemple, la césure du mot « com-patible » est transformée comme suit « compatible » grâce à la fonctionnalité join_hyphenated
qui, lorsqu’elle est activée, joint les mots coupés.
Il est également possible de convertir le texte en minuscule.
Parfois, l’extraction du texte peut être mal formatée, en fonction de la complexité du PDF, cela peut nécessiter une révision manuelle notamment au niveau des titres, et des pieds de page contenant des informations inutiles (comme un numéro de page, des liens web…).
Enfin, le script ne gère pas les images ou les graphiques présents dans les PDF et l’export se fait au format texte (.txt).
import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QFileDialog, QVBoxLayout, QWidget, QLineEdit, QCheckBox from PyQt5.QtCore import Qt import fitz # PyMuPDF import os class ConvertisseurPDFenTexte(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle('Convertisseur PDF en Texte') # Layout self.central_widget = QWidget() self.setCentralWidget(self.central_widget) self.layout = QVBoxLayout(self.central_widget) # Sélection du fichier PDF self.pdf_label = QLabel('Sélectionnez le fichier PDF:', self) self.layout.addWidget(self.pdf_label) self.pdf_path_line = QLineEdit(self) self.layout.addWidget(self.pdf_path_line) self.pdf_button = QPushButton('Parcourir', self) self.pdf_button.clicked.connect(self.selectionner_pdf) self.layout.addWidget(self.pdf_button) # Sélection du répertoire de sortie self.output_label = QLabel('Sélectionnez le répertoire de sortie:', self) self.layout.addWidget(self.output_label) self.output_path_line = QLineEdit(self) self.layout.addWidget(self.output_path_line) self.output_button = QPushButton('Parcourir', self) self.output_button.clicked.connect(self.selectionner_repertoire_de_sortie) self.layout.addWidget(self.output_button) # Option pour unir les mots coupés self.join_hyphenated_words_checkbox = QCheckBox('Unir les mots coupés', self) self.layout.addWidget(self.join_hyphenated_words_checkbox) # Option pour convertir en minuscules self.lowercase_checkbox = QCheckBox('Convertir en minuscules', self) self.layout.addWidget(self.lowercase_checkbox) # Bouton de conversion self.convert_button = QPushButton('Convertir le PDF en Texte', self) self.convert_button.clicked.connect(self.convertir_pdf_en_texte) self.layout.addWidget(self.convert_button) # Bouton pour quitter self.quit_button = QPushButton('Quitter', self) self.quit_button.clicked.connect(QApplication.quit) self.layout.addWidget(self.quit_button) # Étiquette de statut self.status_label = QLabel('', self) self.layout.addWidget(self.status_label) # Lien cliquable self.link_label = QLabel('<a href="https://www.codeandcortex.fr">www.codeandcortex.fr</a>', self) self.link_label.setOpenExternalLinks(True) self.link_label.setAlignment(Qt.AlignCenter) self.layout.addWidget(self.link_label) self.setGeometry(100, 100, 600, 400) self.show() def selectionner_pdf(self): options = QFileDialog.Options() file_name, _ = QFileDialog.getOpenFileName(self, "Sélectionnez le fichier PDF", "", "Fichiers PDF (*.pdf);;Tous les fichiers (*)", options=options) if file_name: self.pdf_path_line.setText(file_name) def selectionner_repertoire_de_sortie(self): options = QFileDialog.Options() directory = QFileDialog.getExistingDirectory(self, "Sélectionnez le répertoire de sortie", options=options) if directory: self.output_path_line.setText(directory) def convertir_pdf_en_texte(self): pdf_path = self.pdf_path_line.text() output_directory = self.output_path_line.text() join_hyphenated_words = self.join_hyphenated_words_checkbox.isChecked() convert_to_lowercase = self.lowercase_checkbox.isChecked() if pdf_path and output_directory: output_path = os.path.join(output_directory, os.path.splitext(os.path.basename(pdf_path))[0] + '.txt') self.pdf_en_texte_avec_colonnes(pdf_path, output_path, join_hyphenated_words, convert_to_lowercase) self.status_label.setText(f'Fichier texte créé : {output_path}') else: self.status_label.setText('Veuillez sélectionner un fichier PDF et un répertoire de sortie.') def pdf_en_texte_avec_colonnes(self, pdf_path, txt_path, join_hyphenated_words, convert_to_lowercase): # Ouvrir le fichier PDF pdf_document = fitz.open(pdf_path) # Initialiser une chaîne de caractères pour contenir le texte extrait text = "" # Parcourir chaque page du PDF for page_num in range(len(pdf_document)): page = pdf_document.load_page(page_num) blocks = page.get_text("blocks") # Récupérer les blocs de texte # Trier les blocs par position verticale, puis par position horizontale blocks.sort(key=lambda b: (b[1], b[0])) # Extraire le texte de chaque bloc for block in blocks: block_text = block[4].replace('\n', ' ') text += block_text + "\n" # Post-traitement pour joindre les lignes lines = text.splitlines() joined_text = "" for i, line in enumerate(lines): if line.strip(): # S'il ne s'agit pas d'une ligne vide if join_hyphenated_words and joined_text.endswith('-'): # Si la ligne précédente se termine par un trait d'union et l'option est activée joined_text = joined_text[:-1] + line.strip() # Joindre sans le trait d'union elif i > 0 and not joined_text.endswith(('.', '!', '?')): # Si la ligne précédente ne se termine pas par une ponctuation joined_text += " " + line.strip() else: joined_text += "\n" + line.strip() else: joined_text += "\n" # Suppression des espaces autour des traits d'union if join_hyphenated_words: joined_text = joined_text.replace('- ', '').replace(' -', '') # Conversion en minuscules if convert_to_lowercase: joined_text = joined_text.lower() # Écrire le texte extrait dans un fichier .txt with open(txt_path, 'w', encoding='utf-8') as txt_file: txt_file.write(joined_text.strip()) print(f"Le texte a été extrait et sauvegardé dans {txt_path}") if __name__ == '__main__': app = QApplication(sys.argv) ex = ConvertisseurPDFenTexte() sys.exit(app.exec_())
Comparaison avec un outil en ligne
Pour valider l’efficacité de ce script, j’ai comparé les résultats avec un outil en ligne pdf2go de conversion de PDF en texte.
Comme illustré dans la capture d’écran ci-dessous, le script ne prend pas en compte la mise en page en deux colonnes du document et ne relie pas les phrases correctement.
Le format de sortie nécessite une retouche manuelle pour joindre les phrases et les mots coupés.
Conclusion
Ce script offre une solution simple et efficace pour convertir des PDF en texte. Bien qu’il ne soit pas parfait, il permet d’extraire rapidement du texte et de l’utiliser (avec quelques corrections) dans des logiciels comme Iramuteq.
Pour un résultat optimal, vous devrez tout de même y mettre un peu d’huile de coude !
[…] to Text : Un outil pour extraire le texte contenu dans des fichiers PDF, permettant ainsi d’analyser des documents officiels, rapports, ou tout autre contenu […]