2016-05-05 7 views
11

Ich lade eine Datei mit der get Funktion von Python requests Bibliothek. Um die Datei zu speichern, möchte ich den Dateinamen so festlegen, wie ein Webbrowser ihn speichern oder speichern würde.Wie ermittelt man den Dateinamen von Inhalten, die mit HTTP in Python heruntergeladen werden?

Einfach, oder? Ich kann es einfach get it from the Content-Disposition HTTP-Header, zugänglich auf dem Antwortobjekt:

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

Aber genaueres Hinsehen zu diesem Thema, es ist nicht dass einfach:

Nach RFC 6266 section 4.3 und der Grammatik in der section 4.1 kann der Wert ein Token ohne Anführungszeichen sein (z. B. the_report.pdf) oder eine Zeichenfolge in Anführungszeichen, die auch Leerzeichen (z. B. "the report.pdf") und Escape-Sequenzen enthalten kann. Weiter,

Wenn sowohl "Dateiname" und "Dateiname *" in einem einzigen Header-Feld Wert vorhanden sind, sollten wir wählen "Dateiname *" und ignorieren "Dateiname".

Der Wert von filename* ist jedoch, yet a bit more complicated als die von filename.

Auch der RFC scheint zusätzliche Whitespace um die zu ermöglichen.

So ist für die examples listed in the RFC, würde ich folgende Ergebnisse wollen:

  • Content-Disposition: Attachment; filename=example.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: € rates hier auch (nicht EURO rates, wie filename* Vorrang) Jetzt

, konnte ich einfach den regulären Ausdruck passen um die =, für variable Leerzeichen zu berücksichtigen, sondern mit damit umgehen auch alle anderen Variationen würden ziemlich unhandlich werden. (Mit dem Zitat und Flucht, ich bin nicht einmal sicher, RegEx kann alle Fälle decken. Vielleicht können sie, da es keine Klammerverschachtelung beteiligt ist.)

Also muss ich einen ausgewachsenen Parser implementieren oder kann ich Dateinamen nach RFC 6266 durch einige wenige Aufrufe an eine HTTP-Bibliothek (vielleicht requests selbst) bestimmen? Da RFC 6266 Teil des HTTP-Standards ist, könnte ich mir vorstellen, dass einige auf HTTP spezialisierte Bibliotheken dies bereits abdecken. (So ​​habe ich also asked on Software Recommendations SE.)

Antwort

8

Die rfc6266 Bibliothek scheint genau das zu tun, was Sie brauchen. Es kann rohe Header, requests Antworten und urllib2 Antworten analysieren. Es ist auf PyPI.

Einige Beispiele:

>>> import rfc6266, requests 
>>> rfc6266.parse_headers('''Attachment; filename=example.html''').filename_unsafe 
'example.html' 
>>> rfc6266.parse_headers('''INLINE; FILENAME= "an example.html"''').filename_unsafe 
'an example.html' 
>>> rfc6266.parse_headers(
    '''attachment; ''' 
    '''filename*= UTF-8''%e2%82%ac%20rates''').filename_unsafe 
'€ rates' 
>>> rfc6266.parse_headers(
    '''attachment; ''' 
    '''filename="EURO rates"; ''' 
    '''filename*=utf-8''%e2%82%ac%20rates''').filename_unsafe 
'€ rates' 
>>> r = requests.get('http://example.com/€ rates') 
>>> rfc6266.parse_requests_response(r).filename_unsafe 
'€ rates' 

Als Hinweis, aber: diese Bibliothek funktioniert nicht wie Nicht-Standard-Leerzeichen im Header.

+0

Was meinen Sie mit "nicht standardmäßigen Leerzeichen"? Leerzeichen an Stellen, an denen der Standard keine Leerzeichen erlaubt? Oder UNICODE-Leerzeichen, die nicht Teil von 7-Bit-ASCII sind? –

+0

@ das-g Habe nicht genug untersucht, um es dir zu sagen. Es stellt sich heraus, dass "parse_headers" eine "entspannte" Option hat, die dabei hilft. Sehen Sie sich den Code [hier] an (https://github.com/g2p/rfc6266/blob/master/rfc6266.py#L209). – Kupiakos