Analysieren von PDF-Dateien (insbesondere mit Tabellen) mit PDFBox

71

Ich muss eine PDF-Datei analysieren, die tabellarische Daten enthält. Ich verwende PDFBox , um den Dateitext zu extrahieren und das Ergebnis (String) später zu analysieren. Das Problem ist, dass die Textextraktion nicht wie erwartet für Tabellendaten funktioniert. Zum Beispiel habe ich eine Datei, die eine Tabelle wie diese enthält (7 Spalten: Die ersten beiden haben immer Daten, nur eine Komplexitätsspalte enthält Daten, nur eine Finanzierungsspalte enthält Daten):

+----------------------------------------------------------------+
| AIH | Value | Complexity                     | Financing       |
|     |       | Medium | High | Not applicable | MAC/Other | FAE |
+----------------------------------------------------------------+
| xyz | 12.43 | 12.34  |      |                | 12.34     |     |
+----------------------------------------------------------------+
| abc | 1.56  |        | 1.56 |                |           | 1.56|
+----------------------------------------------------------------+

Dann benutze ich PDFBox:

PDDocument document = PDDocument.load(pathToFile);
PDFTextStripper s = new PDFTextStripper();
String content = s.getText(document);

Diese beiden Datenzeilen würden folgendermaßen extrahiert:

xyz 12.43 12.4312.43
abc 1.56 1.561.56

Es gibt keine Leerzeichen zwischen den letzten beiden Zahlen, aber dies ist nicht das größte Problem. Das Problem ist, dass ich nicht weiß, was die letzten beiden Zahlen bedeuten: Mittel, Hoch, Nicht zutreffend? MAC / Andere, FAE? Ich habe keine Beziehung zwischen den Zahlen und ihren Spalten.

Ich muss die PDFBox-Bibliothek nicht verwenden, daher ist eine Lösung, die eine andere Bibliothek verwendet, in Ordnung. Ich möchte in der Lage sein, die Datei zu analysieren und zu wissen, was jede analysierte Nummer bedeutet.

Matheus Moreira
quelle
10
Viel Glück ... Ich bin selbst in der PDF-Hölle und an diesem Punkt ziemlich angewidert von dem Format.
Fosco
24
PDF wurde als Ausgabe-Anzeigeformat und nicht zum Extrahieren konzipiert. Beschuldigen Sie die Benutzer, nicht das Format.
Mark Stephens
Wenn das PDF ein festes Layout hat, gibt es andere Möglichkeiten, die Daten aus den Spalten zu extrahieren. Ich habe gerade ein Tool geschrieben, um PDF-Text aus festen Feldpositionen in einem Formular zu extrahieren. Es wäre interessant, die PDF-Datei zu sehen, auf die sich Matheus bezieht.
Andrew Cash
Leider kann ich die PDF-Datei nicht anzeigen. Es enthält die Kundendaten des Projekts und kann nicht weitergegeben werden.
Matheus Moreira
Ich habe das gleiche Problem mit einer anderen Bibliothek ( pdfparser.org ). Es scheint kein Bibliotheksproblem zu sein. Die Lösung, zu der ich nach langem Suchen gekommen bin, besteht darin, den Prozess in zwei Teile zu unterteilen: 1) PDFtoHTML & 2) HTMLtoTXT.
Kareem

Antworten:

20

Sie müssen einen Algorithmus entwickeln, um die Daten in einem verwendbaren Format zu extrahieren. Unabhängig davon, welche PDF-Bibliothek Sie verwenden, müssen Sie dies tun. Zeichen und Grafiken werden durch eine Reihe von zustandsbehafteten Zeichenvorgängen gezeichnet, dh bewegen Sie sich an diese Position auf dem Bildschirm und zeichnen Sie die Glyphe für das Zeichen 'c'.

Ich schlage vor, dass Sie org.apache.pdfbox.pdfviewer.PDFPageDrawerdie strokePathMethode erweitern und überschreiben . Von dort aus können Sie die Zeichenvorgänge für horizontale und vertikale Liniensegmente abfangen und anhand dieser Informationen die Spalten- und Zeilenpositionen für Ihre Tabelle bestimmen. Dann ist es einfach, Textbereiche einzurichten und zu bestimmen, welche Zahlen / Buchstaben / Zeichen in welchem ​​Bereich gezeichnet werden. Da Sie das Layout der Regionen kennen, können Sie feststellen, zu welcher Spalte der extrahierte Text gehört.

Der Grund dafür, dass Sie möglicherweise keine Leerzeichen zwischen visuell getrenntem Text haben, ist, dass sehr oft kein Leerzeichen von der PDF-Datei gezeichnet wird. Stattdessen wird die Textmatrix aktualisiert und ein Zeichenbefehl für 'Verschieben' ausgegeben, um das nächste Zeichen und eine "Leerzeichenbreite" neben dem letzten zu zeichnen.

Viel Glück.

Purecharger
quelle
Dieses Tool scheint etwas wie oben zu tun, und es gibt einen Quellcode verfügbar jpedal.org/support_egTZ.php
Matthew Lock
Ich habe in letzter Zeit etwas Ähnliches gemacht, außer dass ich mich mit mehreren Textzeilen befassen musste. Schauen Sie sich auch die ExtractText-Klassen an, um herauszufinden, wie Sie den eigentlichen Text herausziehen können, sobald Sie die Spalten und Zeilen haben. Außerdem hatte ich Probleme, die Zeilen korrekt zu machen, konnte mich jedoch anpassen, indem ich beim Zurückgehen der Spalten eine neue Zeile annahm.
Deterb
@deterb Wie haben Sie mit mehrzeiligem Text umgegangen?
Gourav Saklecha
@purecharger gibt es eine Möglichkeit, die Tabellenposition zu identifizieren?
Gourav Saklecha
@GouravSaklecha Sie müssen das Format, das von dem, was auch immer das PDF-Dokument geschrieben hat, verwendet wird, rückentwickeln, um herauszufinden, wie es genau behandelt wird - verschiedene PDF-Generatoren behandeln dies unterschiedlich. Im Allgemeinen können Sie mehrere Linien erkennen, indem Sie sich die Positionen des gezeichneten Textes ansehen.
Deterb
14

Ich hatte viele Tools verwendet, um Tabellen aus PDF-Dateien zu extrahieren, aber es funktionierte nicht für mich.

Also habe ich meinen eigenen Algorithmus implementiert (sein Name ist traprange), um tabellarische Daten in PDF-Dateien zu analysieren.

Im Folgenden finden Sie einige Beispiele für PDF-Dateien und Ergebnisse:

  1. Eingabedatei: sample-1.pdf , Ergebnis: sample-1.html
  2. Eingabedatei: sample-4.pdf , Ergebnis: sample-4.html

Besuchen Sie meine Projektseite bei traprange .

Tho
quelle
Hallo, könntest du bitte Maven-Abhängigkeit für dich geben?
Vahe Harutyunyan
@ VaheHarutyunyan bitte überprüfen Sie in dieser Datei github.com/thoqbk/traprange/blob/master/pom.xml
Tho
@ThomQ Es scheint, dass wir es nicht in Maven Central haben? search.maven.org
Vahe Harutyunyan
@ VaheHarutyunyan nein, wir nicht
Tho
13

Sie können Text nach Bereich in PDFBox extrahieren. Siehe die ExtractByArea.javaBeispieldatei im pdfbox-examplesArtefakt, wenn Sie Maven verwenden. Ein Ausschnitt sieht aus wie

   PDFTextStripperByArea stripper = new PDFTextStripperByArea();
   stripper.setSortByPosition( true );
   Rectangle rect = new Rectangle( 464, 59, 55, 5);
   stripper.addRegion( "class1", rect );
   stripper.extractRegions( page );
   String string = stripper.getTextForRegion( "class1" );

Das Problem besteht darin, zuerst die Koordinaten zu erhalten. Es ist mir gelungen, das Normale zu erweitern TextStripper, processTextPosition(TextPosition text)die Koordinaten für jedes Zeichen zu überschreiben und auszudrucken und herauszufinden, wo sie sich im Dokument befinden.

Aber es gibt einen viel einfacheren Weg, zumindest wenn Sie einen Mac verwenden. Öffnen Sie die PDF-Datei in der Vorschau. ⌘Ich möchte den Inspektor anzeigen, wählen Sie die Registerkarte Zuschneiden und stellen Sie sicher, dass sich die Einheiten in Punkten befinden. Wählen Sie im Menü Extras die Option Rechteckige Auswahl und wählen Sie den gewünschten Bereich aus. Wenn Sie einen Bereich auswählen, zeigt Ihnen der Inspektor die Koordinaten an, die Sie runden und in die RectangleKonstruktorargumente einspeisen können. Sie müssen nur mit der ersten Methode bestätigen, wo der Ursprung liegt.

Emerson Farrugia
quelle
2
Schöne, einfache Lösung, wenn die PDFs ein festes Layout haben! Würde eine weitere Gegenstimme abgeben (wenn ich könnte!) Für den Trick, Preview in macOS zu verwenden. Macht die Extraktion wirklich einfach.
Matthias
11

Für meine Antwort mag es zu spät sein, aber ich denke, das ist nicht so schwer. Sie können die PDFTextStripper-Klasse erweitern und die Methoden writePage () und processTextPosition (...) überschreiben. In Ihrem Fall gehe ich davon aus, dass die Spaltenüberschriften immer gleich sind. Das bedeutet, dass Sie die x-Koordinate jeder Spaltenüberschrift kennen und die x-Koordinate der Zahlen mit denen der Spaltenüberschriften vergleichen können. Wenn sie nahe genug sind (Sie müssen testen, um zu entscheiden, wie nahe sie sind), können Sie sagen, dass diese Nummer zu dieser Spalte gehört.

Ein anderer Ansatz wäre, den Vektor "Zeichen durch Artikel" abzufangen, nachdem jede Seite geschrieben wurde:

@Override
public void writePage() throws IOException {
    super.writePage();
    final Vector<List<TextPosition>> pageText = getCharactersByArticle();
    //now you have all the characters on that page
    //to do what you want with them
}

Wenn Sie Ihre Spalten kennen, können Sie die x-Koordinaten vergleichen, um zu entscheiden, zu welcher Spalte jede Zahl gehört.

Der Grund, warum Sie keine Leerzeichen zwischen Zahlen haben, ist, dass Sie die Worttrennzeichenfolge festlegen müssen.

Ich hoffe, dass dies für Sie oder andere nützlich ist, die ähnliche Dinge ausprobieren.

impeto
quelle
10

Es gibt PDFLayoutTextStripper , das entwickelt wurde, um das Format der Daten beizubehalten .

Aus der README:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.apache.pdfbox.pdfparser.PDFParser;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.util.PDFTextStripper;

public class Test {

    public static void main(String[] args) {
        String string = null;
        try {
            PDFParser pdfParser = new PDFParser(new FileInputStream("sample.pdf"));
            pdfParser.parse();
            PDDocument pdDocument = new PDDocument(pdfParser.getDocument());
            PDFTextStripper pdfTextStripper = new PDFLayoutTextStripper();
            string = pdfTextStripper.getText(pdDocument);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        };
        System.out.println(string);
    }
}
Matthias Braun
quelle
1
Mit der aktuellen Version von pdfbox muss die PDFParserInstanzerstellung auf -PDFParser pdfParser = new PDFParser( new RandomAccessBufferedFileInputStream( "sample.pdf"));
Sabir Khan
4

Ich hatte ordentliche Erfolge beim Parsen von Textdateien, die mit dem Dienstprogramm pdftotext (sudo apt-get install poppler-utils) generiert wurden .

File convertPdf() throws Exception {
    File pdf = new File("mypdf.pdf");
    String outfile = "mytxt.txt";
    String proc = "/usr/bin/pdftotext";
    ProcessBuilder pb = new ProcessBuilder(proc,"-layout",pdf.getAbsolutePath(),outfile); 
    Process p = pb.start();

    p.waitFor();

    return new File(outfile);
}
Scott
quelle
1
Für diese Windows-Benutzer laden Sie Ihre exe-Datei von folgender Adresse herunter: narrabs.com/xpdf/download.html Zeigen Sie mit der Variablen proc auf die Datei pdftotext.exe. Entfernen Sie den Rückgabetyp und das Schlüsselwort return der Funktion, wenn Sie in der Hauptfunktion ausgeführt werden.
Dinesh Ravi
4

Versuchen Sie es mit TabulaPDF ( https://github.com/tabulapdf/tabula ). Dies ist eine sehr gute Bibliothek zum Extrahieren von Tabelleninhalten aus der PDF-Datei. Es ist sehr wie erwartet.

Viel Glück. :) :)

SURESH KUMAR S.
quelle
3
Wenn Sie in diesem Fall ein Beispiel für die Verwendung der Bibliothek angeben, können Sie die Qualität dieser Antwort und die Wahrscheinlichkeit, dass sie bewertet wird, erheblich verbessern.
chb
2

Das Extrahieren von Daten aus PDF ist mit Problemen verbunden. Werden die Dokumente durch einen automatischen Prozess erstellt? In diesem Fall können Sie die PDFs in unkomprimiertes PostScript konvertieren (versuchen Sie es mit pdf2ps) und prüfen, ob das PostScript ein reguläres Muster enthält, das Sie ausnutzen können.

Todd Owen
quelle
2

Ich hatte das gleiche Problem beim Lesen der PDF-Datei, in der die Daten in Tabellenform vorliegen. Nach dem regulären Parsen mit PDFBox wurde jede Zeile mit einem Komma als Trennzeichen extrahiert ... wobei die Spaltenposition verloren ging. Um dies zu beheben, habe ich PDFTextStripperByArea verwendet und mithilfe von Koordinaten die Daten spaltenweise für jede Zeile extrahiert. Dies setzt voraus, dass Sie ein PDF mit festem Format haben.

        File file = new File("fileName.pdf");
        PDDocument document = PDDocument.load(file);
        PDFTextStripperByArea stripper = new PDFTextStripperByArea();
        stripper.setSortByPosition( true );
        Rectangle rect1 = new Rectangle( 50, 140, 60, 20 );
        Rectangle rect2 = new Rectangle( 110, 140, 20, 20 );
        stripper.addRegion( "row1column1", rect1 );
        stripper.addRegion( "row1column2", rect2 );
        List allPages = document.getDocumentCatalog().getAllPages();
        PDPage firstPage = (PDPage)allPages.get( 2 );
        stripper.extractRegions( firstPage );
        System.out.println(stripper.getTextForRegion( "row1column1" ));
        System.out.println(stripper.getTextForRegion( "row1column2" ));

Dann Reihe 2 und so weiter ...

manu
quelle
2

Mit der PDFBox- PDFTextStripperByAreaKlasse können Sie Text aus einem bestimmten Bereich eines Dokuments extrahieren. Sie können darauf aufbauen, indem Sie die Region jeder Zelle der Tabelle identifizieren. Dies wird nicht DrawPrintTextLocationssofort bereitgestellt, aber die Beispielklasse zeigt, wie Sie die Begrenzungsrahmen einzelner Zeichen in einem Dokument analysieren können (es wäre großartig, Begrenzungsrahmen von Zeichenfolgen oder Absätzen zu analysieren, aber ich habe keine Unterstützung in gesehen PDFBox dazu - siehe diese Frage ). Mit diesem Ansatz können Sie alle berührenden Begrenzungsrahmen gruppieren, um unterschiedliche Zellen einer Tabelle zu identifizieren. Eine Möglichkeit, dies zu tun, besteht darin, eine Reihe boxesvon Rectangle2DRegionen zu verwalten und dann für jedes analysierte Zeichen den Begrenzungsrahmen des Zeichens wie in zu finden DrawPrintTextLocations.writeString(String string, List<TextPosition> textPositions)und ihn mit dem vorhandenen Inhalt zusammenzuführen.

Rectangle2D bounds = s.getBounds2D();
// Pad sides to detect almost touching boxes
Rectangle2D hitbox = bounds.getBounds2D();
final double dx = 1.0; // This value works for me, feel free to tweak (or add setter)
final double dy = 0.000; // Rows of text tend to overlap, so no need to extend
hitbox.add(bounds.getMinX() - dx , bounds.getMinY() - dy);
hitbox.add(bounds.getMaxX() + dx , bounds.getMaxY() + dy);

// Find all overlapping boxes
List<Rectangle2D> intersectList = new ArrayList<Rectangle2D>();
for(Rectangle2D box: boxes) {
    if(box.intersects(hitbox)) {
        intersectList.add(box);
    }
}

// Combine all touching boxes and update
for(Rectangle2D box: intersectList) {
    bounds.add(box);
    boxes.remove(box);
}
boxes.add(bounds);

Sie können diese Regionen dann an übergeben PDFTextStripperByArea.

Sie können auch noch einen Schritt weiter gehen und die horizontalen und vertikalen Komponenten dieser Bereiche trennen und so Bereiche aller Zellen der Tabelle ableiten, unabhängig davon, ob sie dann Inhalte enthalten.

Ich hatte Grund, diese Schritte auszuführen, und schrieb schließlich meine eigene PDFTableStripperKlasse mit PDFBox . Ich habe meinen Code als Kern auf GitHub geteilt . Die mainMethode gibt ein Beispiel dafür, wie die Klasse verwendet werden kann:

try (PDDocument document = PDDocument.load(new File(args[0])))
{
    final double res = 72; // PDF units are at 72 DPI
    PDFTableStripper stripper = new PDFTableStripper();
    stripper.setSortByPosition(true);

    // Choose a region in which to extract a table (here a 6"wide, 9" high rectangle offset 1" from top left of page)
    stripper.setRegion(new Rectangle(
        (int) Math.round(1.0*res), 
        (int) Math.round(1*res), 
        (int) Math.round(6*res), 
        (int) Math.round(9.0*res)));

    // Repeat for each page of PDF
    for (int page = 0; page < document.getNumberOfPages(); ++page)
    {
        System.out.println("Page " + page);
        PDPage pdPage = document.getPage(page);
        stripper.extractTable(pdPage);
        for(int c=0; c<stripper.getColumns(); ++c) {
            System.out.println("Column " + c);
            for(int r=0; r<stripper.getRows(); ++r) {
                System.out.println("Row " + r);
                System.out.println(stripper.getText(r, c));
            }
        }
    }
}
beldaz
quelle
PDFTableStripperByArea Existiert nicht, Sie meinen PDFTextStripperByArea.
Walid Bousseta
0

Wie wäre es mit Drucken auf Bild und OCR?

Klingt furchtbar ineffektiv, aber es ist praktisch der eigentliche Zweck von PDF, Text unzugänglich zu machen. Sie müssen das tun, was Sie tun müssen.

Carl Smotricz
quelle
Nehmen Sie nicht an, Sie könnten herausfinden, welche OCR Tabellen lesen kann?
Markdigi
@markdigi: Ich habe sehr wenig Erfahrung mit OCR-Software. Etwas sehr ungeschicktes namens ReadIris, das mit meinem HP Drucker kostenlos geliefert wurde, und ein überraschend leistungsfähiges und dennoch preisgünstiges Produkt namens aabby FineReader (glaube ich). Wenn ich mich richtig erinnere, können beide Dokumente mit Tabellen im MS Word-Format lesen, einschließlich Tabellen. Bitte nehmen Sie diese Informationen als Hinweis für die weitere Erkundung, nicht als konkrete Empfehlung.
Carl Smotricz
1
OCRing wäre sinnlos, da Sie zumindest in jeder PDF-API Zugriff auf das tatsächliche Zeichen und die X, Y-Position haben
Matthew Lock
1
Ich verwende Ihre Methode zum ordnungsgemäßen, wenn auch arbeitsintensiven Extrahieren aus einfachen Tabellen in einem PDF-Dokument. Ich verwende die OCR des PDF X-Change-Viewers, verwende dann die Auswahlwerkzeuge, schneide sie aus und füge sie schließlich in eine Tabelle ein. Es ist ein mehrstufiger Prozess. Das Auswahlwerkzeug arbeitet mit der [Alt] -Taste, um nach Spalten auszuwählen, obwohl dies nicht vollständig vorhersehbar ist (Spalten-, Zeilengrenzen gelten nicht immer). Das war besser als das Transkribieren. Bei langen Tabellen, die auf vielen Seiten mit Überschriften aufgeteilt sind, ist diese Methode jedoch langwierig. Mein naives Verständnis ist, dass PDFs keinen ausnutzbaren Tabellenformalismus haben.
Subsci
0

http://swftools.org/ Diese Leute haben eine pdf2swf-Komponente. Sie können auch Tabellen anzeigen. Sie geben auch die Quelle. Sie könnten es also möglicherweise überprüfen.

kaushalc
quelle
0

Dies funktioniert einwandfrei, wenn die PDF-Datei mit pdfbox 2.0.6 "Nur rechteckige Tabelle" enthält. Funktioniert nicht mit anderen Tabellen. Nur rechteckige Tabellen.

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.PDFTextStripperByArea;
public class PDFTableExtractor {
    public static void main(String[] args) throws IOException {
        ArrayList<String[]> objTableList = readParaFromPDF("C:\\sample1.pdf", 1,1,6);
        //Enter Filepath, startPage, EndPage, Number of columns in Rectangular table
    }
    public static ArrayList<String[]> readParaFromPDF(String pdfPath, int pageNoStart, int pageNoEnd, int noOfColumnsInTable) {
        ArrayList<String[]> objArrayList = new ArrayList<>();
        try {
            PDDocument document = PDDocument.load(new File(pdfPath));
            document.getClass();
            if (!document.isEncrypted()) {
                PDFTextStripperByArea stripper = new PDFTextStripperByArea();
                stripper.setSortByPosition(true);
                PDFTextStripper tStripper = new PDFTextStripper();
                tStripper.setStartPage(pageNoStart);
                tStripper.setEndPage(pageNoEnd);
                String pdfFileInText = tStripper.getText(document);
                // split by whitespace
                String Documentlines[] = pdfFileInText.split("\\r?\\n");
                for (String line : Documentlines) {
                    String lineArr[] = line.split("\\s+");
                    if (lineArr.length == noOfColumnsInTable) {
                        for (String linedata : lineArr) {
                            System.out.print(linedata + "             ");
                        }
                        System.out.println("");
                        objArrayList.add(lineArr);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("Exception " +e);
        }
            return objArrayList;
    }
}
Sunil K Chaudhary
quelle
1
Diese Lösung funktioniert nicht, wenn die Zellenwerte Leerzeichen enthalten.
Manikanta
0

Für alle, die das Gleiche wie OP tun möchten (wie ich), ist Amazon Textract nach Tagen der Recherche die beste Option (wenn Ihr Volumen niedrig ist, kann die kostenlose Stufe ausreichen).

UnderBlue592
quelle
0

Ich muss die PDFBox-Bibliothek nicht verwenden, daher ist eine Lösung, die eine andere Bibliothek verwendet, in Ordnung

Camelot und Excalibur

Vielleicht möchten Sie die Python-Bibliothek Camelot ausprobieren , eine Open-Source-Bibliothek für Python. Wenn Sie nicht geneigt sind, Code zu schreiben, können Sie die um Camelot erstellte Weboberfläche Excalibur verwenden . Sie "laden" das Dokument auf einen localhost-Webserver hoch und "laden" das Ergebnis von diesem localhost-Server herunter.

Hier ist ein Beispiel für die Verwendung dieses Python-Codes:

import camelot
tables = camelot.read_pdf('foo.pdf', flavor="stream")
tables[0].to_csv('foo.csv')

Die Eingabe ist ein PDF mit dieser Tabelle:

PDF-TREX-Beispiel

Beispieltabelle aus dem PDF-TREX-Set

Camelot wird keine Hilfe angeboten, es arbeitet von selbst, indem es die relative Ausrichtung der Textteile betrachtet. Das Ergebnis wird in einer CSV-Datei zurückgegeben:

PDF-Tabelle mit Camelot aus der Probe extrahiert

PDF-Tabelle mit Camelot aus der Probe extrahiert

"Regeln" können hinzugefügt werden, um Camelot dabei zu helfen, zu identifizieren, wo sich Filets in anspruchsvollen Tabellen befinden:

Regel zu Excalibur hinzugefügt
Regel in Excalibur hinzugefügt. Quelle

GitHub:

Die beiden Projekte sind aktiv.

Hier ist ein Vergleich mit anderer Software (mit Test basierend auf tatsächlichen Dokumenten), Tabula , pdfplumber , pdftables , pdf-table-extract .


Ich möchte in der Lage sein, die Datei zu analysieren und zu wissen, was jede analysierte Nummer bedeutet

Sie können dies nicht automatisch tun, da PDF nicht semantisch strukturiert ist.

Buch gegen Dokument

PDF- "Dokumente" sind vom semantischen Standpunkt aus unstrukturiert (es ist wie eine Notizblockdatei). Das PDF-Dokument enthält Anweisungen zum Drucken eines Textfragments, unabhängig von anderen Fragmenten desselben Abschnitts. Es gibt keine Trennung zwischen Inhalten (was gedruckt werden soll) und ob dies ein Fragment eines Titels, einer Tabelle oder einer Fußnote ist) und der visuellen Darstellung (Schriftart, Position usw.). Pdf ist eine Erweiterung von PostScript , die eine Hallo-Welt beschreibt! Seite auf diese Weise:

!PS
 /Courier             % font
 20 selectfont        % size
 72 500 moveto        % current location to print at
 (Hello world!) show  % add text fragment
 showpage             % print all on the page

(Wikipedia).
Man kann sich vorstellen, wie ein Tisch mit den gleichen Anweisungen aussieht.

Wir könnten sagen, HTML ist nicht klarer, aber es gibt einen großen Unterschied: HTML beschreibt den Inhalt semantisch (Titel, Absatz, Liste, Tabellenkopf, Tabellenzelle, ...) und ordnet das CSS zu, um eine visuelle Form zu erzeugen, daher ist der Inhalt voll zugänglich. In diesem Sinne ist HTML ein vereinfachter Nachkomme von SGML , der Einschränkungen für die Datenverarbeitung auferlegt :

Markup sollte die Struktur eines Dokuments und andere Attribute beschreiben, anstatt die auszuführende Verarbeitung anzugeben, da es weniger wahrscheinlich ist, dass es zu Konflikten mit zukünftigen Entwicklungen kommt.

genau das Gegenteil von PostScript / Pdf. SGML wird beim Veröffentlichen verwendet. Pdf bettet diese semantische Struktur nicht ein, sondern enthält nur das CSS-Äquivalent, das einfachen Zeichenfolgen zugeordnet ist, bei denen es sich möglicherweise nicht um vollständige Wörter oder Sätze handelt. Pdf wird für geschlossene Dokumente und jetzt für das sogenannte Workflow-Management verwendet .

Nachdem Sie die Unsicherheit und Schwierigkeit beim Extrahieren von Daten aus PDFs ausprobiert haben, ist es klar, dass PDF überhaupt keine Lösung ist, um einen Dokumentinhalt für die Zukunft zu erhalten (obwohl Adobe von seinen Paaren einen PDF-Standard erhalten hat ).

Was tatsächlich gut erhalten bleibt, ist die gedruckte Darstellung, da das PDF diesem Aspekt bei der Erstellung vollständig gewidmet war. Pdf sind fast so tot wie gedruckte Bücher.

Wenn es darum geht, den Inhalt wiederzuverwenden, muss man sich erneut auf die manuelle Eingabe von Daten verlassen, wie aus einem gedruckten Buch (möglicherweise wird versucht, eine OCR darauf durchzuführen). Dies trifft immer mehr zu, da viele PDF-Dateien sogar die Verwendung von Copy-Paste verhindern, mehrere Leerzeichen zwischen Wörtern einfügen oder ein ungeordnetes Zeichen-Kauderwelsch erzeugen, wenn eine "Optimierung" für die Web-Verwendung durchgeführt wird.

Wenn der Inhalt des Dokuments und nicht seine gedruckte Darstellung wertvoll ist, hat PDF nicht das richtige Format. Selbst Adobe ist nicht in der Lage, die Quelle eines Dokuments aus dem PDF-Rendering perfekt wiederherzustellen.

Offene Daten sollten daher niemals im PDF-Format veröffentlicht werden. Dies beschränkt ihre Verwendung auf das Lesen und Drucken (sofern zulässig) und erschwert die Wiederverwendung.

min
quelle
0
ObjectExtractor oe = new ObjectExtractor(document);

SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm(); // Tabula algo.

Page page = oe.extract(1); // extract only the first page

for (int y = 0; y < sea.extract(page).size(); y++) {
  System.out.println("table: " + y);
  Table table = sea.extract(page).get(y);

  for (int i = 0; i < table.getColCount(); i++) {
    for (int x = 0; x < table.getRowCount(); x++) {
      System.out.println("col:" + i + "/lin:x" + x + " >>" + table.getCell(x, i).getText());
    }
  }
}
Thiago Rabelo
quelle
2
Sie sollten die Bibliothek erwähnen, die Sie verwenden. Ihr Code enthält zahlreiche Klassen, die keine PDFBox- oder Java-Basisklassen sind ...
mkl
-1

Ich bin mit PDFBox nicht vertraut, aber Sie können versuchen, sich den Text anzusehen . Obwohl auf der Homepage die PDF-Generierung angegeben ist, können Sie auch PDF-Manipulationen und -Extraktionen durchführen. Schauen Sie nach, ob es zu Ihrem Anwendungsfall passt.

Paul Sanwald
quelle
1
Haben Sie ein Beispiel für die Verwendung von itext zum Extrahieren von Dateiinhalten?
Matheus Moreira
Ich habe eine einfache Möglichkeit gefunden, den Inhalt mit iText zu lesen, aber es hat mir nicht geholfen. Mit PdfTextExtractor erhalte ich ein ähnliches Ergebnis wie mit PDFBox. :-(
Matheus Moreira
Es ist schon eine Weile her, aber ist es dann nicht PdfReader .getContent ()?
Paul Sanwald
-1

Um den Inhalt der Tabelle aus einer PDF-Datei zu lesen, müssen Sie nur die PDF-Datei mithilfe einer beliebigen API (ich habe PdfTextExtracter.getTextFromPage () von iText verwendet) in eine Textdatei konvertieren und diese TXT-Datei dann von Ihrem Java-Programm lesen ..nun nach dem Lesen ist die Hauptaufgabe erledigt .. Sie müssen die Daten Ihres Bedarfs filtern. Sie können dies tun, indem Sie kontinuierlich die Split-Methode der String-Klasse verwenden, bis Sie einen Datensatz Ihres Interesses finden. Hier ist mein Code, mit dem ich einen Teil des Datensatzes durch eine PDF-Datei extrahieren und in eine CSV-Datei schreiben kann. URL von PDF Datei ist .. http://www.cea.nic.in/reports/monthly/generation_rep/actual/jan13/opm_02.pdf

Code:-

public static void genrateCsvMonth_Region(String pdfpath, String csvpath) {
        try {
            String line = null;
            // Appending Header in CSV file...
            BufferedWriter writer1 = new BufferedWriter(new FileWriter(csvpath,
                    true));
            writer1.close();
            // Checking whether file is empty or not..
            BufferedReader br = new BufferedReader(new FileReader(csvpath));

            if ((line = br.readLine()) == null) {
                BufferedWriter writer = new BufferedWriter(new FileWriter(
                        csvpath, true));
                writer.append("REGION,");
                writer.append("YEAR,");
                writer.append("MONTH,");
                writer.append("THERMAL,");
                writer.append("NUCLEAR,");
                writer.append("HYDRO,");
                writer.append("TOTAL\n");
                writer.close();
            }
            // Reading the pdf file..
            PdfReader reader = new PdfReader(pdfpath);
            BufferedWriter writer = new BufferedWriter(new FileWriter(csvpath,
                    true));

            // Extracting records from page into String..
            String page = PdfTextExtractor.getTextFromPage(reader, 1);
            // Extracting month and Year from String..
            String period1[] = page.split("PEROID");
            String period2[] = period1[0].split(":");
            String month[] = period2[1].split("-");
            String period3[] = month[1].split("ENERGY");
            String year[] = period3[0].split("VIS");

            // Extracting Northen region
            String northen[] = page.split("NORTHEN REGION");
            String nthermal1[] = northen[0].split("THERMAL");
            String nthermal2[] = nthermal1[1].split(" ");

            String nnuclear1[] = northen[0].split("NUCLEAR");
            String nnuclear2[] = nnuclear1[1].split(" ");

            String nhydro1[] = northen[0].split("HYDRO");
            String nhydro2[] = nhydro1[1].split(" ");

            String ntotal1[] = northen[0].split("TOTAL");
            String ntotal2[] = ntotal1[1].split(" ");

            // Appending filtered data into CSV file..
            writer.append("NORTHEN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(nthermal2[4] + ",");
            writer.append(nnuclear2[4] + ",");
            writer.append(nhydro2[4] + ",");
            writer.append(ntotal2[4] + "\n");

            // Extracting Western region
            String western[] = page.split("WESTERN");

            String wthermal1[] = western[1].split("THERMAL");
            String wthermal2[] = wthermal1[1].split(" ");

            String wnuclear1[] = western[1].split("NUCLEAR");
            String wnuclear2[] = wnuclear1[1].split(" ");

            String whydro1[] = western[1].split("HYDRO");
            String whydro2[] = whydro1[1].split(" ");

            String wtotal1[] = western[1].split("TOTAL");
            String wtotal2[] = wtotal1[1].split(" ");

            // Appending filtered data into CSV file..
            writer.append("WESTERN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(wthermal2[4] + ",");
            writer.append(wnuclear2[4] + ",");
            writer.append(whydro2[4] + ",");
            writer.append(wtotal2[4] + "\n");

            // Extracting Southern Region
            String southern[] = page.split("SOUTHERN");

            String sthermal1[] = southern[1].split("THERMAL");
            String sthermal2[] = sthermal1[1].split(" ");

            String snuclear1[] = southern[1].split("NUCLEAR");
            String snuclear2[] = snuclear1[1].split(" ");

            String shydro1[] = southern[1].split("HYDRO");
            String shydro2[] = shydro1[1].split(" ");

            String stotal1[] = southern[1].split("TOTAL");
            String stotal2[] = stotal1[1].split(" ");

            // Appending filtered data into CSV file..
            writer.append("SOUTHERN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(sthermal2[4] + ",");
            writer.append(snuclear2[4] + ",");
            writer.append(shydro2[4] + ",");
            writer.append(stotal2[4] + "\n");

            // Extracting eastern region
            String eastern[] = page.split("EASTERN");

            String ethermal1[] = eastern[1].split("THERMAL");
            String ethermal2[] = ethermal1[1].split(" ");

            String ehydro1[] = eastern[1].split("HYDRO");
            String ehydro2[] = ehydro1[1].split(" ");

            String etotal1[] = eastern[1].split("TOTAL");
            String etotal2[] = etotal1[1].split(" ");
            // Appending filtered data into CSV file..
            writer.append("EASTERN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(ethermal2[4] + ",");
            writer.append(" " + ",");
            writer.append(ehydro2[4] + ",");
            writer.append(etotal2[4] + "\n");

            // Extracting northernEastern region
            String neestern[] = page.split("NORTH");

            String nethermal1[] = neestern[2].split("THERMAL");
            String nethermal2[] = nethermal1[1].split(" ");

            String nehydro1[] = neestern[2].split("HYDRO");
            String nehydro2[] = nehydro1[1].split(" ");

            String netotal1[] = neestern[2].split("TOTAL");
            String netotal2[] = netotal1[1].split(" ");

            writer.append("NORTH EASTERN" + ",");
            writer.append(year[0] + ",");
            writer.append(month[0] + ",");
            writer.append(nethermal2[4] + ",");
            writer.append(" " + ",");
            writer.append(nehydro2[4] + ",");
            writer.append(netotal2[4] + "\n");
            writer.close();

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

    }
Abhishek Yadav
quelle