Text aus PDF-Dateien extrahieren


Lovis Schmidt


snippets with text

In NLP-Projekten hat man es oft mit PDFs als Ausgangsdokumenten zu tun. Manchmal enthalten die PDFs bereits zugrundeliegende Textinformationen, wodurch es möglich ist, Text ohne den Einsatz von OCR-Werkzeugen zu extrahieren. Im Folgenden möchte ich einige Open-Source-PDF-Werkzeuge vorstellen, die in Python verfügbar sind und mit denen sich Text extrahieren lässt. Ich werde ihre Eigenschaften vergleichen und auf ihre jeweiligen Vor- und Nachteile hinweisen.

Die Tools heißen PyPDF2pdfminer und PyMuPDF.

Es gibt andere Python-PDF-Bibliotheken, die entweder nicht in der Lage sind, Text zu extrahieren oder sich auf andere Aufgaben konzentrieren. Darüber hinaus gibt es Werkzeuge, die in der Lage sind, Text aus PDF-Dokumenten zu extrahieren, die aber in Python nicht verfügbar sind. Auf beide wird hier nicht eingegangen.  

Besuchen Sie außerdem die vergangenen dida-Projekte, in denen wir eine Informationsextraktion mit KI für Produktbeschreibungen, eine Informationsextraktion aus Kundenanfragen oder eine Informationsextraktion aus PDF-Rechnungen entwickelt haben.


Einführung


Wir haben bereits verschiedene OCR-Tools zur automatischen Extraktion von Text aus Dokumenten diskutiert. Obwohl es gut funktionierende Tools gibt, machen sie immer noch Fehler. Wenn man also Informationen aus Dokumenten extrahieren will, muss man entweder robuste Modelle erstellen, die mit kleinen Fehlern umgehen können, oder nach alternativen Wegen der Textextraktion suchen. Für Bilder und Dokumente ohne zugrundeliegende Textinformationen sind OCR-Werkzeuge ohne Alternative. Bei PDF-Dokumenten mit zugrundeliegendem Text stellt sich jedoch die Frage, ob man auf diese Textinformationen direkt zugreifen und so mögliche OCR-Fehler umgehen könnte. Ich möchte dies diskutieren und Einblicke in unsere Erfahrungen aus den jüngsten Projekten geben.

Zunächst ist zu erwähnen, dass PDF nicht zum Abrufen von Textinformationen geeignet ist. PDF steht für Portable Document Format und wurde von Adobe entwickelt. Das Hauptziel war es, Informationen plattformunabhängig austauschen zu können und dabei den Inhalt und das Layout eines Dokuments zu erhalten und zu schützen. Dies führt dazu, dass PDFs schwer zu bearbeiten und Informationen aus ihnen nur umständlich zu extrahieren sind. Was nicht bedeutet, dass dies unmöglich ist.

Zweitens muss man entscheiden, wie viele Informationen tatsächlich benötigt werden. Brauchen Sie nur die Klartextinformation, brauchen Sie auch die Position des Textes, wollen Sie vielleicht auch eine Informationen über die Schriftart? Das sind Fragen, die auch bei der Entscheidung für ein geeignetes OCR-Tool wichtig sind. Alles ist möglich, aber die Aufgabe wird mit jeder weiteren benötigten Informationsebene komplexer und unübersichtlicher.

Wir werden die drei Bibliotheken an drei einfachen Beispiel-PDFs testen:


PyPDF2


PyPDF2 ist eine reine Python-PDF-Bibliothek, die in der Lage ist, Seiten verschiedener PDF-Dateien zu teilen, zusammenzuführen, zu beschneiden und zu transformieren. Wir können Metadaten aus PDFs abrufen, wie Autor, Ersteller, Erstellungsdatum und andere. Sie kann auch den PDF-Text abrufen, wie er im Content Stream zu finden ist. Das bedeutet, dass der Text möglicherweise nicht logisch geordnet ist, wenn dies nicht in dem mit der PDF-Datei verknüpften Stream-Objekt geschieht. Eine unlogische Anordnung sollte im Allgemeinen nicht vorkommen, aber wenn die Dokumente komplexer werden, könnte die Textanordnung ebenfalls komplex sein.

Der Code zum Abrufen des reinen Textes ist recht einfach:

import PyPDF2

with open(pdf_path, "rb") as f:
	reader = PyPDF2.PdfFileReader(f)
    page = reader.getPage(0)
    text = page.extractText()

Test-Ergebnisse

  • Schauen wir uns die Ausgabe an, die wir für die verschiedenen PDFs erhalten:  

  • Beispiel 1: "Adobe Acrobat PDF Files\n \nAdobe® Portable Document Format (PDF) is a universal file format that preserves all \nof the fonts, formatting, colours and graphics of any source document, regardless of the \napplication and platform used to create it.\n \nAdobe PDF is an ideal format for electr\nonic document distribution as it overcomes the \nproblems commonly encountered with electronic file sharing.\n \n\n \nAnyone, anywhere\n \ncan open a PDF file. All you need is the free Adobe Acrobat \nReader. Recipients of other file formats sometimes can't open files beca\nuse they \ndon't have the applications used to create the documents.\n \n\n \nPDF files \nalways print correctly\n \non any printing device.\n \n\n \nPDF files always display \nexactly\n \nas created, regardless of fonts, software, and \noperating systems. Fonts, and graphics are not lost \ndue to platform, software, and \nversion incompatibilities.\n \n\n \nThe free Acrobat Reader is easy to download and can be freely distributed by \nanyone.\n \n\n \nCompact PDF files are smaller than their source files and download a page at a time \nfor fast display on the Web.\n \n"

  • Beispiel 2: "\n\n\n\n\n\n\nˇˇˇ\nˇ\n\n\n\nˆˇ\n˝\n\nˇ˛\nˇ\n\n˚\n˜˙ˆ\n\nˇˆ\n\n\n\nˆ\nˇˇ\n·˘\n·\n· ˜\n·ˆˇ!\n˜ˇ\n\n\n·ˆ\n·ˆˇ"ˇ\n\n\n\n\n\n\n\n\n\n"

  • Beispiel 3: "Example table\n This is an example of a data table.\n Disability \nCategory\n Participants\n Ballots \nCompleted\n Ballots \nIncomplete/\n Terminated\n Results\n Accuracy\n Time to \ncomplete\n Blind\n 5 1 4 34.5%, n=1\n 1199 sec, n=1\n Low Vision\n 5 \n2 \n3 \n98.3% n=2\n (97.7%, n=3)\n 1716 \nsec, n=3\n (1934 sec, n=2)\n Dexterity\n 5 \n4 \n1 \n98.3%, n=4\n 1672.1 sec, n=4\n Mobility\n 3 \n3 \n0 \n95.4%, n=3\n 1416 sec, n=3\n"

Beispiel 2 sieht unbrauchbar aus. PyPDF2 scheint einige Probleme mit dieser Datei zu haben, obwohl sie beim Zugriff mit einem PDF-Viewer ganz normal aussieht. Dies kann passieren, wenn die PDF-Erstellungssoftware bei der Erstellung der PDF-Datei einige Schriftinformationen nicht verlinkt hat. Einige anspruchsvollere PDF-Betrachter und -Pakete sind in der Lage, mit diesen Problemen umzugehen, PyPDF2 versagt bei diesem speziellen Dokument. Bei Beispiel 1 wurden auch einige Escape-Zeichen \n hinzugefügt, wo keine sein sollten (z.B. der fettgedruckte Text). Beispiel 3 sieht recht gut aus.

Zu fehlenden oder zu vielen Textinformationen 

Solche Fehler wie der von PyPDF2 auf Beispiel 2 können sogar bei der Arbeit mit anspruchsvolleren PDF-Bibliotheken auftreten und sind unter Umständen schwer zu erkennen. Darüber hinaus werden die Dinge sehr viel schwieriger, wenn die PDF-Datei eine Mischung aus Text mit verfügbaren zugrundeliegenden Textinformationen und scanähnlichen Bereichen ist, in denen Text sichtbar ist, aber keine Textinformationen erhalten werden können. Dann werden Ihnen einige der Textinformationen entgehen. Umgekehrt ist auch der umgekehrte Weg möglich: Bereiche, in denen kein Text sichtbar ist, aber darunter liegende Textinformationen erhältlich sind. Dann erhalten Sie zu viele Textinformationen (was eine gute Sache sein kann, wenn Sie darauf abzielen, solche versteckten Informationen aufzuspüren). Beides ist schwer zu erkennen und schwer zu bewältigen. Auch hier sind PDFs eine chaotische Angelegenheit.


pdfminer


Im Gegensatz zu PyPDF2 übernimmt pdfminer nicht die Reihenfolge des Textes aus dem Content Stream, sondern extrahiert zusätzliche Informationen wie Textkoordinaten. Mit deren Hilfe versucht es, alle verfügbaren Zeichen zu Wörtern, die Wörter zu zugehörigen Textzeilen und die Zeilen zu absatzartigen Objekten zusammenzufügen. Die folgende Abbildung veranschaulicht diesen Prozess. Die blauen Kästchen sind die Wortobjekte, die grünen Kästchen die Textzeilenobjekte und das rote Kästchen begrenzt das absatzartige Objekt (nicht alle sind hier mit einem Label versehen).

Diese geometrische Analyse kann manipuliert werden, um zu beeinflussen, wie pdfminer Wörter, Textzeilen und Textblöcke findet. Der Code zum Abrufen des reinen Textes ist etwas schwieriger als der für PyPDF2:

from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTFigure, LTTextBox
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfinterp import PDFPageInterpreter, PDFResourceManager
from pdfminer.pdfpage import PDFPage, PDFTextExtractionNotAllowed
from pdfminer.pdfparser import PDFParser

text = ""

with open(pdf_path, 'rb') as f:
    parser = PDFParser(f)
    doc = PDFDocument(parser)
    page = list(PDFPage.create_pages(doc))[0]
    rsrcmgr = PDFResourceManager()
    device = PDFPageAggregator(rsrcmgr, laparams=LAParams())
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    interpreter.process_page(page)
    layout = device.get_result()
    
    for obj in layout:
        if isinstance(obj, LTTextBox):
            text += obj.get_text()
            
        elif isinstance(obj, LTFigure):
            stack += list(obj)

Dies ist ein sehr high-level und sollte nur den reinen Text extrahieren. Wir können die geometrische Analyse mit dem LAParams()-Objekt manipulieren und zusätzlich die zuvor erwähnten geometrischen Informationen der Objekte sowie einige Schriftinformationen abrufen.

Test-Ergebnisse

Schauen wir uns die Ausgabe an, die wir für die verschiedenen PDFs erhalten: 

  • Beispiel 1: "Adobe Acrobat PDF Files \nAdobe® Portable Document Format (PDF) is a universal file format that preserves all \nof the fonts, formatting, colours and graphics of any source document, regardless of the \napplication and platform used to create it. \nAdobe PDF is an ideal format for electronic document distribution as it overcomes the \nproblems commonly encountered with electronic file sharing. \n• Anyone, anywhere can open a PDF file. All you need is the free Adobe Acrobat \nReader. Recipients of other file formats sometimes can't open files because they \ndon't have the applications used to create the documents. \n• PDF files always print correctly on any printing device. \n• PDF files always display exactly as created, regardless of fonts, software, and \noperating systems. Fonts, and graphics are not lost due to platform, software, and \nversion incompatibilities. \n• The free Acrobat Reader is easy to download and can be freely distributed by \nanyone. \n• Compact PDF files are smaller than their source files and download a page at a time \nfor fast display on the Web. \n"

  • Beispiel 2: "Lorem ipsum \nLorem ipsum dolor sit amet, consectetur adipiscing \nelit. Nunc ac faucibus odio. \nVestibulum neque massa, scelerisque sit amet ligula eu, congue molestie mi. Praesent ut\nvarius sem. Nullam at porttitor arcu, nec lacinia nisi. Ut ac dolor vitae odio interdum\ncondimentum. Vivamus dapibus sodales ex, vitae malesuada ipsum cursus\nconvallis. Maecenas sed egestas nulla, ac condimentum orci. Mauris diam felis,\nvulputate ac suscipit et, iaculis non est. Curabitur semper arcu ac ligula semper, nec luctus\nnisl blandit. Integer lacinia ante ac libero lobortis imperdiet. Nullam mollis convallis ipsum,\nac accumsan nunc vehicula vitae. Nulla eget justo in felis tristique fringilla. Morbi sit amet\ntortor quis risus auctor condimentum. Morbi in ullamcorper elit. Nulla iaculis tellus sit amet\nmauris tempus fringilla.\nMaecenas mauris lectus, lobortis et purus mattis, blandit dictum tellus.\n• Maecenas non lorem quis tellus placerat varius. \n• Nulla facilisi. \n• Aenean congue fringilla justo ut aliquam. \n• Mauris id ex erat. Nunc vulputate neque vitae justo facilisis, non condimentum ante\nsagittis. \n• Morbi viverra semper lorem nec molestie. \n• Maecenas tincidunt est efficitur ligula euismod, sit amet ornare est vulputate.\n12\n10\n8\n6\n4\n2\n0\nColumn 1\nColumn 2\nColumn 3\nRow 1\nRow 2\nRow 3\nRow 4\n"

  • Beispiel 3: "Example table \nThis is an example of a data table. \nDisability \nCategory \nParticipants \nBallots \nCompleted \nBallots \nIncomplete/ \nTerminated \nBlind \nLow Vision \nDexterity \nMobility \n \n5 \n5 \n5 \n3 \n1 \n2 \n4 \n3 \n4 \n3 \n1 \n0 \nResults \nAccuracy \nTime to \ncomplete \n34.5%, n=1 \n1199 sec, n=1 \n98.3% n=2 \n1716 sec, n=3 \n(97.7%, n=3) \n(1934 sec, n=2) \n98.3%, n=4 \n1672.1 sec, n=4 \n95.4%, n=3 \n1416 sec, n=3 \n"

Das sieht gut aus. pdfminer ist in der Lage, auch den Text in Beispiel 2 zu extrahieren und extrahiert auch den Text aus der darin enthaltenen Abbildung (was ausgeschaltet werden kann). Für Beispiel 1 könnte auch auf die Schriftartinformationen zugegriffen werden, was zu einer besseren Textextraktion führt als PyPDF2, das versucht, fettgedruckten Text durch Gruppierung mit "\n" anzuzeigen. Allerdings ist der Code nicht so einfach wie bei PyPDF2.


PyMuPDF


Sowohl pdfminer als auch PyPDF2 sind reine Python-Bibliotheken. Im Gegensatz dazu basiert PyMuPDF auf MuPDF, einem schlanken, aber umfangreichen PDF-Viewer. Das hat enorme Vorteile bei der Handhabung schwieriger PDFs, ist aber bei der Lizenzierung strenger, da es sich bei MuPDF um ein kommerzielles Produkt handelt. Außerdem behauptet PyMuPDF, bei verschiedenen Aufgaben deutlich schneller als pdfminer und PyPDF2 zu sein. PyMuPDF kann, ebenso wie pdfminer auch geometrische Text- und Schriftinformationen extrahieren, hat aber, wie PyPDF2, auch die Möglichkeit, den reinen Text direkt zu extrahieren. Im Gegensatz zu pdfminer gibt es keine Möglichkeit, den Algorithmus der geometrischen Textanalyse zu manipulieren. PyMuPDF gruppiert den Text wie MuPDF in Textblöcke und Textzeilen.

Der einfache Code zum einfachen Abrufen des Klartextes sieht folgendermaßen aus:

import fitz

doc = fitz.open(pdf_path)
page = doc[0]
text = page.getText("text")

Dies ist einfach und unkompliziert. 

Test-Ergebnisse

Sehen wir uns die Ergebnisse an: 

  • Beispiel 1: "Adobe Acrobat PDF Files \nAdobe® Portable Document Format (PDF) is a universal file format that preserves all \nof the fonts, formatting, colours and graphics of any source document, regardless of the \napplication and platform used to create it. \nAdobe PDF is an ideal format for electronic document distribution as it overcomes the \nproblems commonly encountered with electronic file sharing. \n• \nAnyone, anywhere can open a PDF file. All you need is the free Adobe Acrobat \nReader. Recipients of other file formats sometimes can't open files because they \ndon't have the applications used to create the documents. \n• \nPDF files always print correctly on any printing device. \n• \nPDF files always display exactly as created, regardless of fonts, software, and \noperating systems. Fonts, and graphics are not lost due to platform, software, and \nversion incompatibilities. \n• \nThe free Acrobat Reader is easy to download and can be freely distributed by \nanyone. \n• \nCompact PDF files are smaller than their source files and download a page at a time \nfor fast display on the Web. \n"

  • Beispiel 2: "Lorem ipsum \nLorem ipsum dolor sit amet, consectetur adipiscing \nelit. Nunc ac faucibus odio. \nVestibulum neque massa, scelerisque sit amet ligula eu, congue molestie mi. Praesent ut\nvarius sem. Nullam at porttitor arcu, nec lacinia nisi. Ut ac dolor vitae odio interdum\ncondimentum. Vivamus dapibus sodales ex, vitae malesuada ipsum cursus\nconvallis. Maecenas sed egestas nulla, ac condimentum orci. Mauris diam felis,\nvulputate ac suscipit et, iaculis non est. Curabitur semper arcu ac ligula semper, nec luctus\nnisl blandit. Integer lacinia ante ac libero lobortis imperdiet. Nullam mollis convallis ipsum,\nac accumsan nunc vehicula vitae. Nulla eget justo in felis tristique fringilla. Morbi sit amet\ntortor quis risus auctor condimentum. Morbi in ullamcorper elit. Nulla iaculis tellus sit amet\nmauris tempus fringilla.\nMaecenas mauris lectus, lobortis et purus mattis, blandit dictum tellus.\n•\nMaecenas non lorem quis tellus placerat varius. \n•\nNulla facilisi. \n•\nAenean congue fringilla justo ut aliquam. \n•\nMauris id ex erat. Nunc vulputate neque vitae justo facilisis, non condimentum ante\nsagittis. \n•\nMorbi viverra semper lorem nec molestie. \n•\nMaecenas tincidunt est efficitur ligula euismod, sit amet ornare est vulputate.\nRow 1\nRow 2\nRow 3\nRow 4\n0\n2\n4\n6\n8\n10\n12\nColumn 1\nColumn 2\nColumn 3\n"

  • Beispiel 3: "Example table \nThis is an example of a data table. \nDisability \nCategory \nParticipants \nBallots \nCompleted \nBallots \nIncomplete/ \nTerminated \nResults \nAccuracy \nTime to \ncomplete \nBlind \n5 \n1 \n4 \n34.5%, n=1 \n1199 sec, n=1 \nLow Vision \n5 \n2 \n3 \n98.3% n=2 \n(97.7%, n=3) \n1716 sec, n=3 \n(1934 sec, n=2) \nDexterity \n5 \n4 \n1 \n98.3%, n=4 \n1672.1 sec, n=4 \nMobility \n3 \n3 \n0 \n95.4%, n=3 \n1416 sec, n=3 \n \n"

Das sieht ziemlich genau so aus wie bei pdfminer. Auch hier konnte der Text aus jedem Dokument extrahiert werden. Mit verschiedenen Parametern wie dict, rawdict oder xml kann man verschiedene Ausgabeformate mit zusätzlichen Informationen wie Textkoordinaten, Schriftart und Textebene wie Textblock oder Textzeile erhalten.


Fazit


Zusammenfassend lässt sich sagen, dass es in Python verschiedene Werkzeuge mit unterschiedlichen Methoden und Funktionalitäten für die Extraktion von PDF-Texten gibt. Da PDF-Dokumente ziemlich unordentlich sind, würde ich mich immer für Bibliotheken entscheiden, die auf einem existierenden PDF-Viewer basieren, statt für reine Python-Entwicklung.

Nichtsdestotrotz gibt es einige Vor- und Nachteile der Verwendung des einen gegenüber dem anderen. Wenn Sie können, sollten Sie die Informationen, die Sie zu extrahieren versuchen, auf eine direktere Art und Weise abrufen, indem Sie das Schreiben in und Extrahieren aus PDF umgehen.

Für weitere Informationen sowie eine KI-Beratung zur erfolgreichen Umsetzung Ihres Projekts können Sie auch gerne mit unseren KI-Spezialisten sprechen.