Das Parsen von PDFs, einschließlich Text, ist mit Python einfach ... Ich hatte eine Zeit, in der ich so dachte.

Vorwort

Das Analysieren von PDFs mit Text ist mit Python einfach ...

image.png

Wenn Zeicheninformationen enthalten sind, können Sie einfach einen Webdienst erstellen, indem Sie Zeichen- und Tabelleninformationen aus der PDF-Datei extrahieren und diese Daten verwenden. Das Ergebnis eines einfachen Gedankens lautet wie folgt. Ich werde.

** Ich habe versucht, PDF-Daten der medizinischen Online-Versorgung zu verwenden, die auf der Ausbreitung einer neuen Coronavirus-Infektion basieren ** https://qiita.com/mima_ita/items/c0f28323f330c5f59ed8

Das wichtigste Ergebnis, das ich hier erhalten habe, ist ** "Lesen Sie keine PDF-Daten auf einem Computer, es ist etwas, das Menschen lesen" ** </ font> und einige Umgang mit PDF mit Python.

Dieses Mal werde ich erklären, wie man mit PDF mit diesem kleinen Python umgeht. Die experimentelle Umgebung ist Window10 Python 3.7.5 64bit.

PDF-Analyse

Operanden und Operatoren

Alle PDF-Zeichen und Grafiken bestehen aus Operatoren und Operatoren, deren Spezifikationen unten aufgeführt sind. https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf

Es gibt verschiedene Bibliotheken, die zum Lesen von PDF in Python nützlich sind, aber hier werde ich PyPDF2 verwenden, um das PDF zu lesen. Das Merkmal dieser Bibliothek ist, dass sie vollständig in Python geschrieben ist, sodass Sie überprüfen können, wie das PDF auf Operator- und Operatorebene aussieht.

Lassen Sie uns überprüfen, aus welcher Art von Operatoren und Operatoren das folgende einfache PDF tatsächlich besteht. http://needtec.sakura.ne.jp/doc/hello.pdf

Der folgende Code listet Operatoren und Operatoren auf.

import PyPDF2
from PyPDF2.pdf import ContentStream

with open("hello.pdf", "rb") as fp:
    pdf = PyPDF2.PdfFileReader(fp)
    for page_no in range(pdf.numPages):
        page = pdf.getPage(page_no)
        content = page['/Contents'].getObject()
        if not isinstance(content, ContentStream):
            content = ContentStream(content, pdf)
        for operands, operator in content.operations:
            print(operands, operator)

Das Ergebnis davon mit dem einfachen PDF oben ist wie folgt.

[1, 0, 0, 1, 0, 0] b'cm'
[] b'BT'
['/F1', 12] b'Tf'
[14.4] b'TL'
[] b'ET'
[] b'n'
[10, 10, 200, 200] b're'
[] b'S'
[] b'BT'
[1, 0, 0, 1, 100, 50] b'Tm'
['Hello'] b'Tj'
[] b'T*'
[] b'ET'

Technische Daten Folgendes kann beim Lesen der Anhang A-Bedienerübersicht analysiert werden. Ich verstehe.

Operands Operator Description Seitenzahl
x y width height re Untere linke Ecke(x,y)Fügen Sie einen quadratischen Pfad von hinzu 133p
- S Zeichnen Sie eine Linie entlang des aktuellen Pfads 135p
a b c d e f Tm Gibt die Matrix an, die die Position des Textes bestimmt.
image.png
249
string Tj Zeichen anzeigen 250

Mit anderen Worten wird die Zeichnung wie folgt sein. (1) Zeichnen Sie ein Quadrat mit einer Breite von 200 und einer Höhe von 200 aus (10,10) mit dem unteren linken Rand als Ursprung. (2) Schreiben Sie das Zeichen "Hallo" aus (100, 50)

Diesmal war es ein einfaches Beispiel, damit ich es lesen konnte, aber das Zeichnen des Textes ist sehr mühsam. Wenn ich das Verhalten von Textpositionierungsoperatoren und Textanzeigeoperatoren nicht verstehe, werde ich Zeichen aus PDF und deren Positionen extrahieren Und die Größe kann nicht bekannt sein.

Das folgende PDF ist beispielsweise verfügbar. http://needtec.sakura.ne.jp/doc/hello2.pdf

Zum Zwecke der Betrachtung gibt es nur noch wenige japanische und Tabellenmatrizen, aber es ist schwierig, dies auf die gleiche Weise zu interpretieren.

Darüber hinaus verfügt PyPDF2 über eine Funktion zum Extrahieren von Seiten mit dem Namen page.extractText (). Für nichtamerikanische Benutzer ist dies jedoch sehr schwierig. https://github.com/mstamy2/PyPDF2/issues

Analysieren Sie PDF-Zeichen mit PDFMiner

PDFMiner erleichtert das Extrahieren der Zeichen im PDF.

Im Folgenden finden Sie ein Beispiel zum Extrahieren der Zeichen in der PDF-Datei.

from pdfminer.high_level import extract_text
print(extract_text('hello2.pdf'))

Darüber hinaus besteht der wahre Wert von PDFMiner nicht nur darin, Zeichen zu extrahieren, sondern auch die Koordinaten und die Größe der zu zeichnenden Zeichen zu ermitteln. Unten finden Sie ein Beispielprogramm, das bestimmte PDF-Zeichen und deren Koordinateninformationen extrahiert.

from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfpage import PDFTextExtractionNotAllowed
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import (
    LAParams,
    LTContainer,
    LTTextLine,
)

def get_objs(layout, results):
    if not isinstance(layout, LTContainer):
        return
    for obj in layout:
        if isinstance(obj, LTTextLine):
            results.append({'bbox': obj.bbox, 'text' : obj.get_text(), 'type' : type(obj)})
        get_objs(obj, results)

def main(path):
    with open(path, "rb") as f:
        parser = PDFParser(f)
        document = PDFDocument(parser)
        if not document.is_extractable:
            raise PDFTextExtractionNotAllowed
        # https://pdfminersix.readthedocs.io/en/latest/api/composable.html#
        laparams = LAParams(
            all_texts=True,
        )
        rsrcmgr = PDFResourceManager()
        device = PDFPageAggregator(rsrcmgr, laparams=laparams)
        interpreter = PDFPageInterpreter(rsrcmgr, device)
        for page in PDFPage.create_pages(document):
            interpreter.process_page(page)
            layout = device.get_result()
            results = []
            print('objs-------------------------')
            get_objs(layout, results)
            for r in results:
                print(r)


main('hello2.pdf')


Das Ergebnis der Ausführung dieses Programms mit PDF mit gemischtem Japanisch ist wie folgt.

objs-------------------------
{'bbox': (90.744, 728.1928, 142.2056, 738.7528), 'text': 'Hallo Welt\n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}
{'bbox': (168.5, 728.1928, 223.8356, 738.7528), 'text': 'Die Katze klingelte\n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}
{'bbox': (168.5, 709.7128, 202.8356, 720.2728), 'text': 'ich bin Gott\n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}
{'bbox': (90.744, 691.1128, 146.0456, 701.6728), 'text': 'Gott ist gestorben\n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}
{'bbox': (168.5, 691.1128, 171.2456, 701.6728), 'text': ' \n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}
{'bbox': (168.5, 672.6328, 255.2756, 683.1928), 'text': 'Aw neo l sf\n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}
{'bbox': (90.744, 709.7128, 93.4896, 720.2728), 'text': ' \n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}
{'bbox': (90.744, 672.6328, 93.4896, 683.1928), 'text': ' \n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}
{'bbox': (85.104, 654.1528, 87.8496, 664.7128), 'text': ' \n', 'type': <class 'pdfminer.layout.LTTextLineHorizontal'>}

Sie können sehen, dass Sie die Koordinaten sowie den Inhalt der Zeichen in der PDF erhalten.

Analysieren Sie die Tabelle als PDF

Es gibt keine Operatoren und Operatoren, die Tabellen in PDF darstellen. Ich vertrete die Tabelle nur mit der bisher beschriebenen quadratischen Zeichnung und Textzeichnung. Daher ist es nicht möglich, PDF-Tabellen so einfach zu analysieren wie HTML-Tabellen und Excel.

Einige Python-Bibliotheken versuchen, die PDF-Tabelle zu analysieren. Dieses Mal verwenden wir camelot, das vollständig in Python implementiert ist.

Unten finden Sie einen Vergleich von camelot mit anderen Bibliotheken. https://github.com/atlanhq/camelot/wiki/Comparison-with-other-PDF-Table-Extraction-libraries-and-tools

Das Ergebnis meines Vergleichs mit tabula-py ist wie folgt. ** [PDF des Ministeriums für Gesundheit, Arbeit und Soziales in CSV oder JSON konvertieren](https://needtec.sakura.ne.jp/wod07672/2020/04/29/%e5%8e%9a%e7%94%9f%e5 % 8a% b4% e5% 83% 8d% e7% 9c% 81% e3% 81% aepdf% e3% 82% 92csv% e3% 82% 84json% e3% 81% ab% e5% a4% 89% e6% 8f % 9b% e3% 81% 99% e3% 82% 8b /) **

Eine einfache Camelot-Probe

Extrahieren wir die Tabelle von früher verwendetes PDF mit camelot.

import camelot
tables = camelot.read_pdf('hello2.pdf')

for ix in tables[0].df.index:
    print(ix, tables[0].df.loc[ix][0], '|', tables[0].df.loc[ix][1])

Ergebnis

0 Hallo Welt|Die Katze klingelte
1  |ich bin Gott
2 Gott starb|
3  |Aw neo l sf

Darüber hinaus können Sie die URL direkt angeben, um die PDF-Datei zu öffnen oder in CSV oder JSON zu exportieren. Einige PDFs können jetzt die Tabelle extrahieren.

Wenn die Tabellenextraktion nicht wie erwartet funktioniert

In den meisten Fällen funktionieren die Standardeinstellungen, aber wenn Sie die PDF-Datei tatsächlich analysieren, verhält sie sich möglicherweise unerwartet. In diesem Fall empfehlen wir Ihnen, das folgende Dokument einmal zu lesen.

Advanced Usage https://camelot-py.readthedocs.io/en/master/user/advanced.html

Passen Sie die an read_pdf übergebenen Parameter an, während Sie überprüfen, was cameralot in Visual Debbug erkennt. ) Könnte funktionieren.

Anpassen der Parameter für PDFMiner

Obwohl es in Tweak Layout Generation erwähnt wird, ist Camelot intern PDFMiner. mit. Wenn die Tabelle mit der oben beschriebenen Methode nicht aus PDF extrahiert werden kann, kann sie möglicherweise durch Anpassen der an PDFMiner übergebenen Parameter gelöst werden.

Wenn Sie beispielsweise das folgende PDF auf dieselbe Weise wie den vorherigen Code analysieren, kann die zweite Zeile nicht gut extrahiert werden. http://needtec.sakura.ne.jp/doc/hello4.pdf

** Ausgabeergebnis **

0 1 |ah ah
1  |2 gut
2 3 |Uuu

Dies ist das Ergebnis der Erkenntnis, dass der Abstand zwischen "2" und "gut" zu eng ist und es sich um dieselbe Zeichenfolge handelt. So passen Sie dies an:

tables = camelot.read_pdf(
    'hello4.pdf',
    layout_kwargs = {
        'char_margin': 0.25
    }
)

layout_kwargs ist ein Objekt von Parametern, die an pdfminer.layout von PDFMiner übergeben werden sollen. char_margin betrachtet zwei Textblöcke, die näher als dieser Wert liegen, als zusammenhängend. Der Standardwert ist 0,5, was kürzer ist und weniger wahrscheinlich als der gleiche Text betrachtet wird.

Das Ergebnis der Ausführung mit diesem Parameter ist wie folgt.

0 1 |ah ah
1  |2 gut
2 3 |Uuu
--------------------------
0 1 |ah ah
1 2 |Gut
2 3 |Uuu

Beim Einfügen einer gepunkteten Linie

Bei der Verarbeitung einer Tabelle mit einer gepunkteten Linie mit Camelot wird die gepunktete Linie nicht erkannt.

Detect dotted line #370 https://github.com/atlanhq/camelot/issues/370

Das folgende PDF ist beispielsweise eines davon. ➀ Vertikale gepunktete Linie https://github.com/atlanhq/camelot/files/3565115/Test.pdf

② Horizontale gepunktete Linie https://github.com/mima3/yakusyopdf/blob/master/20200502/%E5%85%B5%E5%BA%AB%E7%9C%8C.pdf

Diese Lösung kann mit der im folgenden Artikel beschriebenen Methode behandelt werden.

・ ** [Gepunktete Linie als durchgezogene Linie mit Camelot verarbeiten](https://needtec.sakura.ne.jp/wod07672/2020/05/03/camelot%e3%81%a7%e7%82%b9%e7% b7% 9a% e3% 82% 92% e5% ae% 9f% e7% b7% 9a% e3% 81% a8% e3% 81% 97% e3% 81% a6% e5% 87% a6% e7% 90% 86% e3% 81% 99% e3% 82% 8b /) **

Einfach ausgedrückt werden die von Camelot verarbeiteten Bilddaten zwangsweise verarbeitet und die gepunktete Linie durch eine durchgezogene Linie ersetzt, um die Verarbeitung fortzusetzen.

Wenn du nichts machen kannst

Es gibt Fälle, in denen nichts unternommen werden kann, um ein PDF zu erstellen. Beispiel: [Für Daten mit langen Zeichen, die über die Zelle hinausgehen](https://qiita.com/mima_ita/items/c0f28323f330c5f59ed8#pdf%E3%81%8B%E3%82%89%E3%83%86%E3% 83% BC% E3% 83% 96% E3% 83% AB% E3% 82% 92% E6% 8A% BD% E5% 87% BA% E3% 81% 99% E3% 82% 8B% E9% 9A% 9B% E3% 81% AE% E5% 95% 8F% E9% A1% 8C% E7% 82% B9) und so weiter.

Wenn Sie zunächst vergessen, eine Linie zu zeichnen, funktioniert dies nicht ordnungsgemäß.

PDF-Update

Die Methode zum Lesen der PDF-Datei wurde bis zum vorherigen Abschnitt erläutert. Lassen Sie uns als nächstes kurz über das Aktualisieren des PDF nachdenken.

Erstellen Sie ein neues PDF

Mit reportlab können Sie den Inhalt von Zeichentext und Abbildungen als PDF ausgeben.


from io import BytesIO
from reportlab.pdfgen import canvas

with open('hello.pdf', 'wb') as output_stream:
    buffer = BytesIO()
    c = canvas.Canvas(buffer, pagesize=(300, 300))
    c.rect(10, 10, 200, 200, fill=0)
    c.drawString(100, 50, 'Hello')
    c.showPage()
    c.save()
    buffer.seek(0)
    output_stream.write(buffer.getvalue())

Diese Ausgabe ist das zuvor verwendete PDF. http://needtec.sakura.ne.jp/doc/hello.pdf

Schreiben Sie eine vorhandene PDF-Seite neu

Ich habe verschiedene Möglichkeiten untersucht, um ein vorhandenes PDF zu lesen und die grafischen Informationen und den Text auf der Seite neu zu schreiben, aber ehrlich gesagt schien es schwierig zu sein. Die hier vorgestellte Methode ist eine Methode zum Hinzufügen neuer Abbildungen und Texte zur PDF-Seite.

・ ** [Ersetzen Sie die gepunktete PDF-Linie durch eine durchgezogene Linie (PyPDF2 + reportlab)](https://needtec.sakura.ne.jp/wod07672/2020/05/04/pdf%e3%81%ae%e7%82% b9% e7% b7% 9a% e3% 82% 92% e5% ae% 9f% e7% b7% 9a% e3% 81% ab% e3% 81% 8a% e3% 81% 8d% e3% 81% 8b% e3% 81% 88% e3% 82% 8b /) ** ・ ** [Ersetzen Sie die gepunktete PDF-Linie durch eine durchgezogene Linie (PyMuPDF)](https://needtec.sakura.ne.jp/wod07672/2020/05/04/pdf%e3%81%ae%e7%82%b9% e7% b7% 9a% e3% 82% 92% e5% ae% 9f% e7% b7% 9a% e3% 81% ab% e3% 81% 8a% e3% 81% 8d% e3% 81% 8b% e3% 81% 88% e3% 82% 8bpymupdf /) **

Beachten Sie, dass PyPDF2 keine Komprimierungsfunktion hat. Verwenden Sie daher eine andere Methode, um die Datei zu aktualisieren. In meiner Umgebung sind aus 3 MB PDF 440 MB geworden.

Zusammenfassung

Einige von Ihnen finden es möglicherweise einfach, die PDF-Daten in der bisherigen Erklärung zu verwenden. Wenn Sie nicht berechtigt sind, das eingegebene PDF zu ändern, stellen Sie es sich im Grunde als ** Thorn Road ** </ font> vor. In Excel können Sie beispielsweise Zellen unterscheiden, auch wenn Sie vergessen haben, einen Rahmen zu zeichnen, in PDF jedoch nicht.

  • Sie können Linien für alle Zellen mit der Methode zeichnen, mit der die gepunkteten Linien im PDF durch durchgezogene Linien ersetzt werden. Ich weiß jedoch nicht, ob die Ränder dieser Zellen vergessen wurden oder nicht. .. ..

Ich möchte diesen Artikel abschließen, indem ich die wichtigsten Dinge wiederhole, die ich am Anfang erwähnt habe.

** "Lesen Sie keine PDF-Daten auf einem Computer, es ist etwas, das Menschen lesen" ** </ font>

Recommended Posts