Auslesen von Metadaten aus pdf-Dateien

Code-Stücke können hier veröffentlicht werden.
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@TuXX: was ist `gespfad` und existiert die Datei auch? Was und wo ist die Fehlermeldung?
TuXX
User
Beiträge: 24
Registriert: Sonntag 25. Juni 2017, 17:52

Hier noch die Fehlermeldung:

Traceback (most recent call last):
File "C:/Users/User/PycharmProjects/untitled/Schreibtest.py", line 41, in <module>
read_pdf = PyPDF2.PdfFileReader(pdf_file)
File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\pdf.py", line 1084, in __init__
self.read(stream)
File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\pdf.py", line 1697, in read
line = self.readNextEndLine(stream)
File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\pdf.py", line 1937, in readNextEndLine
raise utils.PdfReadError("Could not read malformed PDF file")
PyPDF2.utils.PdfReadError: Could not read malformed PDF file

Process finished with exit code 1

Was läuft hier falsch?
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Fehlermeldung sagt doch ganz eindeutig, dass das Format der PDF-Datei von pypdf nicht gelesen werden kann, entweder weil die Datei kaputt ist, oder in einem Format vorliegt, das PyPDF nicht versteht.
TuXX
User
Beiträge: 24
Registriert: Sonntag 25. Juni 2017, 17:52

Gibt es eine Alternative zu PyPDF2 für das Auslesen von Metadaten? Ich habe ein Paket namens PDFX gesehen.
TuXX
User
Beiträge: 24
Registriert: Sonntag 25. Juni 2017, 17:52

Mittlerweile habe ich die Verzeichnisse noch einmal aufgeräumt (nur noch pdfs). Wenn ich nun das Programm kompiliere, bekomme ich die folgende Fehlermeldung:

Traceback (most recent call last):
File "C:/Users/User/PycharmProjects/untitled/Schreibtest.py", line 60, in <module>
num_pages = read_pdf.trailer["/Root"]["/Pages"]["/Count"]
File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\generic.py", line 516, in __getitem__
return dict.__getitem__(self, key).getObject()
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\generic.py", line 178, in getObject
return self.pdf.getObject(self).getObject()
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\pdf.py", line 1593, in getObject
retval = self._getObjectFromStream(indirectReference)
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\pdf.py", line 1543, in _getObjectFromStream
streamData = BytesIO(b_(objStm.getData()))
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\generic.py", line 841, in getData
decoded._data = filters.decodeStreamData(self)
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\filters.py", line 346, in decodeStreamData
data = FlateDecode.decode(data, stream.get("/DecodeParms"))
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\filters.py", line 111, in decode
data = decompress(data)
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\PyPDF2\filters.py", line 49, in decompress
return zlib.decompress(data)
zlib.error: Error -3 while decompressing data: incorrect header check

Process finished with exit code 1

Muss ich noch ein Paket zlib integrieren? Wer hat eine Idee was hier falsch ist? Gibt es eine Alternative zu PyPDF2 zum Auslesen von PDF Metadaten wie Seitenzahl etc.?
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Entstehen diese Fehler bei verschlüsselten PDF-Dateien?
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@TuXX: ich habe das Gefühl, dass Du Dich gar nicht mit dem Problem beschäftigen willst, sondern immer nur die nächste Fehlermeldung schicktst, die Du bekommst. Erster Schritt wäre, zu schauen, ob es sich überhaupt um eine korrekte PDF-Datei handelt. Falls ja, könnte es sein, dass es sich um ein PDF handelt, bei dem die Meta-Informationen auch in einem verschlüsselten Stream liegen, dann hast Du natürlich ohne Passwort Pech gehabt.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

TuXX hat geschrieben:Mittlerweile bin ich einen kleinen Schritt vorangekommen. Wenn ich einen festen Pfad eingebe kann ich die Anzahl der Seiten auslesen:

pdf_file = open(r'C:\Users\rogbet\Desktop\Problembeschreibung.pdf', 'rb')

read_pdf = PyPDF2.PdfFileReader(pdf_file)
number_of_pages = read_pdf.getNumPages()
print(number_of_pages)

Was nachwievor nicht funktioniert ist das Zählen von Seiten für mittels Skript ermittelte Pfade. Der bisherige Code sieht wie folgt aus:

gespfad = os.path.join(folderName,filename)
pdf_file = open(gespfad, 'rb')
read_pdf = PyPDF2.PdfFileReader(pdf_file)
number_of_pages = read_pdf.getNumPages()
print(number_of_pages)

Bei der Kompilierung werden Fehler angezeigt. Weiss jemand was hier schief läuft?
Verschlüsselte PDFs habe ich nicht zu liegen, da kann ich nichts zu sagen. Doch was liefert Dein Script für Pfade und was ist in "gespfad" enthalten. Danach wurde bereits gefragt und eine Antwort hast Du nicht geliefert.

Wie sehen Deine Schleifen aus und was hat "open" da verloren, wenn Du nur die Seiten zählen möchtest? Habe ein paar Varianten getestet und die Seitenzahlen lassen sich gut zählen, ohne dass da etwas mit open eingelesen oder geöffnet werden soll, das macht wohl der Reader von sich aus, nehme ich an.

Das mit normcase im ersten Beispiel könnte man wohl auch noch weglassen, ich schreibe es halt mit.
Die beiden anderen Beispiele ließen sich vielleicht noch besser schreiben, doch die laufen zumindest und erfüllen ihren Zweck.

Eine einzelne Datei:

Code: Alles auswählen

from PyPDF2 import PdfFileReader 
import os

pdf_file = os.path.normcase("C:/.../datei.pdf")
read_pdf = PdfFileReader(pdf_file)
number_of_pages = read_pdf.getNumPages()
print(number_of_pages)
Oder Verzeichnisse durchlaufen:

Code: Alles auswählen

from PyPDF2 import PdfFileReader 
import os

# Ab der Verzeichnisebene auf dem das Script liegt
def liefere_seitenzahlen():
    endungen = [".pdf",".PDF"]

    for root, dirs, files in os.walk("."):
        for name in files:
            extension = os.path.splitext(name)[1]
            if extension in endungen:                
                read_pdf = PdfFileReader(os.path.join(root, name))
                number_of_pages = read_pdf.getNumPages()
                print("Datei: {} - Seiten: {}".format(name, number_of_pages))

if __name__ == "__main__":
    liefere_seitenzahlen()

Code: Alles auswählen

from PyPDF2 import PdfFileReader 
import os

# Ab der Verzeichnisebene auf dem das Script liegt mit kompletten Pfaden.
def liefere_seitenzahlen():
    pfad = os.path.abspath(".")
    endungen = [".pdf",".PDF"]

    for root, dirs, files in os.walk("."):
        for name in files:
            extension = os.path.splitext(name)[1]
            if extension in endungen:
                komplett = os.path.join(pfad, os.path.relpath(root), name)
                read_pdf = PdfFileReader(komplett)
                number_of_pages = read_pdf.getNumPages()
                print("Kompletter Pfad: {} - Seiten: {}"
                      .format(komplett, number_of_pages))

if __name__ == "__main__":
    liefere_seitenzahlen() 
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Melewo:
normcase() ist in diesem Zusammenhang unnötig. Denn es drückt ja die Bedeutung eines relativen Pfades vom aktuellen Standort im Dateisystem als absolute Pfadangabe aus. Das ist sinnvoll, wenn man die Pfadangabe von einem anderen Ort im Dateisystem benutzen möchte. Oder manchmal auch, wenn man den Pfad für den Benutzer anzeigen will. Wenn man das aber quasi nur für den internen Bereich benutzt, ohne das sich der Standort im Dateisystem geändert hat, dann ist es im Endeffekt das selbe wie die relative Angabe.

Und das Prüfen auf eine PDF-Endung für den Dateinamen würde ich so machen:

Code: Alles auswählen

extension = os.path.splitext(filename)[1]
if extension.lower() == 'pdf':
    # ...
Dann hat man alle denkbaren Schreibweisen abgedeckt und braucht keine Liste dafür.

EDIT:
Das mit der absoluten Angabe bei normase() stimmt nicht mal. Macht es beim internen Gebrauch jedoch nicht weniger überflüssig.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Ja, das mit normcase sehe ich ein. Ich benutze es nur, weil es dann unter Windows die Slashes in Backslashes umwandelt, ohne dass man die allein so schreiben müsste und dann mit einem zweiten Backslash \\ maskieren. Aber ich weiß, Windows liest die auch richtig, wenn man die nur als einfache / Slashes schreibt.

Code: Alles auswählen

import os
 
pdf_file = os.path.normcase("C:/verzeichnis/verzeichnis/datei.pdf")
print(pdf_file)

# Ausgabe:
# c:\verzeichnis\verzeichnis\datei.pdf
Stimmt, das mit extension.lower() hätte besser ausgesehen und die Liste wäre überflüssig gewesen.
Antworten