E-Mail PDF-Anhang speichern – Python-Script für ecoDMS Übergabe

Nachdem ich zuhause das DMS-System aufgesetzt und meine ganzen Dokumente digitalisiert habe stand das Thema der PDF-Dokumente welche per Mail reinkommen auf dem Plan. Ein Script welches mir E-Mail PDF-Anhang speichern kann musst her.

Archivierung von Rechnungen aus E-Mails

Egal wo man heute etwas bestellt, meist kommen die Rechnungen in elektronischer Form. Doch wie kann ich diese Rechnungen nun am Einfachsten in meinem DMS-System archivieren? Eigentlich ging ich davon aus dass ein System wie z.B. ecoDMS ein solches E-Mail Gateway automatisiert mitliefert, leider war dies jedoch nicht der Fall.

Somit musste eine eigene Lösung her. Kein Problem, ein kleines Script sollte das Problem schnell Lösen. Die Grundidee: Ich erzeuge mir ein Mailpostfach an welches ich alle E-Mails, deren PDF-Anhänge archiviert werden sollen weiterleite. Dieses Postfach wird von einem Cronjob abgeholt, die PDF-Anhänge aus den Mails heruntergeladen und in den Übergabeordner von ecoDMS gelegt.

Nach einigen Versuchen ist das folgende Python-Script entstanden welches bei mir nun schon seit über 2 Jahren produktiv im Einsatz ist und stabil seinen Dienst verrichtet.

E-Mail PDF-Anhang speichern – Python-Script

#!/usr/bin/env python
"""
importDmsMails.py

Check emails at :const:`PROVIDER` for attachments and save them to
:const:`SAVEDIR`.
"""
from __future__ import absolute_import, division, print_function
import email
import os
import poplib

PROVIDER = 'XXXX'
USER = 'XXXX'
PASSWORD = 'XXXX'

SAVE_DIR = '/opt/ecodms/workdir/scaninput/'

def getNotExistingFileName(saveDir, srcFileName):
    counter = 1
    if (srcFileName.endswith("pdf?=")):
        srcFileName = "scanbymail.pdf"
    while (True):
        currentName = srcFileName
        if (counter > 1):
            currentName = str(counter) + "_" + srcFileName
        if (os.path.isfile(saveDir + currentName)):
            counter = counter + 1
        else:
            return currentName

def save_attachments(mail_string):
    attachments = list()
    for part in email.message_from_string(mail_string).walk():
        name = part.get_filename()
        if name:
            print('Handle Attachment {0!r}.'.format(name))
            if name.lower().endswith(("pdf", "pdf?=")):
                data = part.get_payload(decode=True)
                name = name.replace("/", "_")
                targetFileName = getNotExistingFileName(SAVE_DIR, name)
                f = file(os.path.join(SAVE_DIR, targetFileName), 'wb')
                f.write(data)
                f.close()
                print('Found and saved attachment {0!r}.'.format(targetFileName))

def main():
    try:
        client = poplib.POP3_SSL(PROVIDER)
        client.user(USER)
        client.pass_(PASSWORD)
        message_numbers = (int(s.split()[0]) for s in client.list()[1])
        for message_number in message_numbers:
            save_attachments('\n'.join(client.retr(message_number)[1]))
            client.dele(message_number)
    finally:
        client.quit()


if __name__ == '__main__':
    main()

Was genau tut das Script?

Das hier aufgeführte Python-Script wird bei mir regelmäßig durch einen Cronjob ausgeführt. Das Script verbindet sich zu meinem Mailserver und lädt sich alle Mails herunter. Hängt an einer Mail ein PDF-Anhang so wird dieser Anhang im Eingangsverzeichnis von ecoDMS gesichert. Bei der Speicherung der Datei prüft das Script ob hier bereits eine Datei mit gleichem Namen vorliegt. Ist dies der Fall so wird der Dateiname durch einen Zähler erweitert so dass keine bereits existierende Datei überschrieben wird.

Wer möchte kann das Script natürlich gerne verwenden und auf seine Bedürfnisse anpassen. Natürlich übernehme ich keine Garantie für die Funktion des Scriptes, die Verwendung erfolgt auf eigene Gefahr 🙂

Update – 11. Juni 2021

Ich habe gestern eine Mail erhalten welche mich auf ein Problem hinwies. Thomas erhielt beim Ausführen des Scriptes den folgenden Fehler:

python3 importDmsMails.py
Traceback (most recent call last):

  File "importDmsMails.py", line 61, in <module>

    main()

  File "importDmsMails.py", line 54, in main

    save_attachments('\n'.join(client.retr(message_number)[1]))

TypeError: sequence item 0: expected str instance, bytes found

Bei der Analyse der Meldung stellte sich heraus, dass ich eine andere Python-Version als Thomas verwende. Bei mir läuft Python in der Version 2.7.10, bei ihm auf Python 3.

Ich habe für dieses Problem eine neue Version des Scriptes bereit gestellt. Mehr dazu findet Ihr hier:
E-Mail Anhänge nach ecoDMS importieren – mit Python 3

Artikel teilen

This article was written by Thomas Schiffler

Alles was mit IT zu tun hat steht bei mir hoch im Kurs. Hierbei dreht sich vieles um Java, Python oder auch mal PHP. Unser Zuhause ist mit diversen Raspberry PIs ausgestattet mit welchen ich versuche unser Leben durch etwas Automatisierung ein wenig smarter zu gestalten. Hierbei möchte ich die Technik und die dahinter eingesetzten Tools / Frameworks verstehen und nicht einfach nur Anwenden. Ich selbst bezeichne mich als ITler aus Leidenschaft :) Seit 2020 ist das Thema Chatbot / Voicebot / Conversational.ai in meinen Focus gerückt. In diesem Bereich investiere ich gerade viel Zeit.

0 thoughts on “E-Mail PDF-Anhang speichern – Python-Script für ecoDMS Übergabe”

  1. Guten Tag Herr Schiffler,

    Ihr Python-Script um PDF-Anhänge aus eMails zu extrahieren finde ich sehr praktisch und spannend.
    Jedoch bekam ich als Python-Anfänger Schwierigkeiten die Einrückungen richtig zu setzen, um Codeblöcke zu bestimmen.
    Damit verbrachte ich mehrere Stunden, natürlich machte ich mir anhand des Programmablaufes Gedanken, wo beginnt und endet ein Codeblock und an welcher Stelle ist dieser vermutlich verschachtelt. Leider hatte ich keinen Erfolg und konnte Ihr Script nicht nutzen.
    Wäre es Ihnen möglich Ihr Python-Script mit Einrückungen abzubilden, um zum einen, andere Python-Anfänger nicht gleich zu entmutigen, wenn diese ebenso auf dieses Problem stoßen und zum Anderen zu sehen, welche Fehler ich bei der Umsetzung der korrekten Einrückungen gemacht habe.

    Viele Grüße aus der sächsischen Schweiz und bleiben Sie gesund
    Rainer Pfau

    1. Hallo Rainer,

      danke für den Hinweis. In der Tat ist das mit dem Lesen ein wenig schwer wenn die nötigen CSS-Files nicht richtig geladen sind. Ich schau mir das mal an und überlege mir was

      Gruß Thomas

  2. Hallo Thomas,

    da ich ebenfalls die Python-Version 3.7.3 nutze stieß ich auf das Problem, welches in Ihrem Artikel unter “Update – 11. Juni 2021” hingewiesen wurde.
    Füge ich wie in Ihrem Artikel beschrieben die Option “b” in die Befehlszeile “save_attachments(b’ …” hinzu erscheint folgende Fehlermeldung:
    $ python mail_anhang_dms.py

    Traceback (most recent call last):
    File “mail_anhang_dms.py”, line 58, in
    main()
    File “mail_anhang_dms.py”, line 52, in main
    save_attachments(b’\n’.join(client.retr(message_number)[1]))
    File “mail_anhang_dms.py”, line 33, in save_attachments
    for part in email.message_from_string(mail_string).walk():
    File “/usr/lib/python3.7/email/__init__.py”, line 38, in message_from_string
    return Parser(*args, **kws).parsestr(s)
    File “/usr/lib/python3.7/email/parser.py”, line 68, in parsestr
    return self.parse(StringIO(text), headersonly=headersonly)
    TypeError: initial_value must be str or None, not bytes

    Gibt es hierfür eine Lösung?

    Viele Grüße aus der sächsischen Schweiz und ich wünsche ein schönes Wochenende
    Rainer Pfau

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.