Python-Bibliothek zum Abrufen des Dateinamens von Inhalten, die mit HTTP heruntergeladen wurden

Ich lade eine Datei mit der getFunktion der Python- requestsBibliothek herunter. Zum Speichern der Datei möchte ich den Dateinamen bestimmen, den ein Webbrowser für den Dialog "Speichern" oder "Speichern unter ..." verwenden würde.

Einfach richtig? Ich kann es einfach aus demContent-Disposition HTTP-Header abrufen, auf den über das Antwortobjekt zugegriffen werden kann:

import re
d = r.headers['content-disposition']
fname = re.findall("filename=(.+)", d)

Aber wenn man sich dieses Thema genauer ansieht, ist es gar nicht so einfach:

Gemäß RFC 6266 Abschnitt 4.3 und der Grammatik in Abschnitt 4.1 kann der Wert ein Token ohne Anführungszeichen (z. B. the_report.pdf) oder eine Zeichenfolge in Anführungszeichen sein, die auch Leerzeichen (z. B. "the report.pdf") und Escape-Sequenzen enthalten kann (von letzteren wird jedoch abgeraten, daher ihre Handhabung ist keine harte Anforderung für mich). Des Weiteren,

Wenn sowohl "Dateiname" als auch "Dateiname*" in einem einzigen Header-Feldwert vorhanden sind, SOLLTEN [wir] "Dateiname*" auswählen und "Dateiname" ignorieren.

Der Wert von filename*ist jedoch etwas komplizierter als der von filename.

Außerdem scheint der RFC zusätzliche Leerzeichen um die =.

Daher möchte ich für die im RFC aufgeführten Beispiele die folgenden Ergebnisse:

- Inhaltsdisposition: Anhang; Dateiname=Beispiel.html Dateiname:example.html

    Content-Disposition: INLINE; FILENAME= "an example.html"

Dateiname:an example.html

    Content-Disposition: attachment;
                         filename*= UTF-8''%e2%82%ac%20rates

Dateiname:€ rates

    Content-Disposition: attachment;
                         filename="EURO rates";
                         filename*=utf-8''%e2%82%ac%20rates

Dateiname: € ratesauch hier (nicht EURO rates, da filename*Vorrang)

Ich könnte das Parsen des Content-DispositionHeaders, von dem ich komme, requestsentsprechend selbst implementieren, aber wenn ich es vermeiden und stattdessen eine vorhandene bewährte Implementierung verwenden kann, würde ich das vorziehen.

Gibt es eine Python-Bibliothek, die das kann?

Anforderungen

Die Bibliothek müsste

  • Stellen Sie eine Funktion bereit, die den richtigen Dateinamen (falls vorhanden) aus einer übergebenen requestsAntwort extrahiert und zurückgibt
  • Stellen Sie eine Funktion bereit, die den richtigen Dateinamen (falls vorhanden) aus einem übergebenen Content-DispositionHeader-Feldwert (einer Zeichenfolge) extrahiert und zurückgibt,
    oder
  • Stellen Sie eine Funktion bereit, die dieselben Parameter akzeptiert wie requests.getdie, die die Anforderung ausführt, und die Antwort sowie den Dateinamen (falls vorhanden) zurückgibt,
    oder
  • bietet etwas ähnlich Praktisches

Nicht-Anforderungen

Was es nicht bewältigen muss (aber wenn doch, noch besser), da ich das selbst machen kann:

  • Werte so bereinigen, dass sie außer einem einzelnen Dateinamen keine Verzeichnisnamen oder andere Pfadelemente enthalten, sodass das Speichern unter diesem Namen nicht dazu führt, dass Dateien an beliebigen Orten erstellt oder überschrieben werden

  • "save"-Dateinamenerweiterungen erzeugen, die "optimal zum Medientyp der empfangenen Payload passen" (siehe Abschnitt 4.3 )

  • Dateinamen bereinigen, um Benutzerverwirrung zu vermeiden ( Abschnitt 4.3 erwähnt das Ersetzen von "Steuerzeichen und führenden und nachgestellten Leerzeichen")

  • einen Rückfall bieten

    • denn wenn weder der filenamenoch der filename*Dispositionsparameter vorhanden sind oder
    • denn wenn die vorhandenen nicht geparst werden können oder
    • wenn der komplette Content-DispositionHeader fehlt

    Obwohl es dies konsistent melden sollte (sei es durch Erhöhen oder durch Zurückgeben Nonevon oder ''), damit ich meinen eigenen Fallback einsetzen kann.

Ich denke, es wäre eine gute Idee, in StackOverflow und nicht in SoftwareRecommendation zu fragen.
@YoshiBotX, obwohl ich ausdrücklich nach einer Bibliotheksempfehlung frage? Das sind Off-Topic auf Stack Overflow.
Ihre Frage ist spezifisch genug, "wie man den Dateinamen bestimmt ...", dass ich es dort versuchen würde.

Antworten (1)

Siehe rfc6266 . Es scheint alles zu tun, was Sie wollen. Es ist unter der LGPL 3.0 lizenziert. Die Hauptgabel ist möglicherweise nicht besonders aktiv, und einige andere Gabeln haben möglicherweise mehr Leckereien.

Hier ist der Dünne:

>>> from rfc6266 import *

>>> parse_headers('Attachment; filename=example.html', relaxed=True)
ContentDisposition(u'Attachment', {u'filename': u'example.html'}, None)

>>> parse_headers('INLINE; FILENAME= "an example.html"', relaxed=True)
ContentDisposition(u'INLINE', {u'filename': u'an example.html'}, None)

>>> h='''attachment;
...                      filename*= UTF-8''%e2%82%ac%20rates'''
>>> parse_headers(h, relaxed=True)
ContentDisposition(u'attachment', {u'filename*': LangTagged(string=u'\u20ac rates', langtag=None)}, None)

>>> h='''attachment;
...                      filename="EURO rates";
...                      filename*=utf-8''%e2%82%ac%20rates'''
>>> parse_headers(h, relaxed=True)
ContentDisposition(u'attachment', {u'filename*': LangTagged(string=u'\u20ac rates', langtag=None), u'filename': u'EURO rates'}, None)
@das-g Ich bin ein Idiot, ich hatte nicht gesehen, dass du das auf SO gepostet hast und dass es dort beantwortet wurde .... Notiz an mich selbst für 2017: Lies die Kommentare, bevor du antwortest!
Eh, macht Sinn, diese Antwort an beiden Stellen zu haben. ( Hier ist das Äquivalent bei der Cross-Posted-Frage.)