2011-11-07 24 views
21

jestem pobieranie pliku z Mechanize i w nagłówkach odpowiedzi nie jest ciągiem:Jak uzyskać nazwę pliku z Content-Disposition w nagłówkach

Content-Disposition: attachment; filename=myfilename.txt 

Czy istnieje szybki standardowy sposób, aby uzyskać tę wartość nazwy pliku? Co mam na myśli teraz to:

filename = f[1]['Content-Disposition'].split('; ')[1].replace('filename=', '') 

Ale wygląda na to rozwiązanie quick'n'dirty.

+0

Podobnie jak ostrzeżenie, nazwa pliku może być cytowana (jak większość nagłówków wiadomości) i mieć sekwencje specjalne. Tak szybkie hacki łańcuchowe mogą prowadzić do problemów. –

Odpowiedz

24

najpierw uzyskać wartość nagłówka przez za pomocą mechanize, a następnie przeanalizuj nagłówek za pomocą wbudowanego modułu cgi.

wykazać:

>>> import mechanize 
>>> browser = mechanize.Browser() 
>>> response = browser.open('http://example.com/your/url') 
>>> info = response.info() 
>>> header = info.getheader('Content-Disposition') 
>>> header 
'attachment; filename=myfilename.txt' 

Wartość Nagłówek może być następnie analizowany:

>>> import cgi    
>>> value, params = cgi.parse_header(header) 
>>> value 
'attachment' 
>>> params 
{'filename': 'myfilename.txt'} 

params jest prosty DICT tak params['filename'] jest to, czego potrzebujesz. Nie ma znaczenia, czy nazwa pliku jest zawijana w cudzysłów, czy nie.

+0

Nie znaczy to, że to nie działa, jeśli twoja nazwa pliku jest zakodowana, w którym to przypadku paramenty będą zawierały "filename *" zamiast "filename" i będziesz musiał zanotować i odkodować nazwę pliku na ciąg znaków Unicode. –

0

chciałbym spróbować coś takiego:

import re 
filename = re.findall("filename=(\S+)", f[1]['Content-Disposition']) 

uchwyty to cytaty i URL uciekających na nazwach plików.

+1

Ale to zwraca listę, a nie ciąg, więc prawdopodobnie chcesz 'filename [0]' lub coś. Również zwraca cytaty jako część nazwy pliku. Tak naprawdę nie działa przykład. – unkulunkulu

5

Te wyrażenia regularne są oparte na gramatyce z RFC 6266, ale zmodyfikowane tak, aby akceptować nagłówki bez typu dyspozycji, np. Content-Disposition: filename = przyklad.html

tj. [Typ dyspozycji ";" ] disposition-parm (";" disposition-parm) */disposition-type

Posłuży do obsługi parametrów pliku z cudzysłowami i bez cudzysłowów oraz nie ma wartości notowanych par z wartości w cudzysłowie, np. filename = „foo \” bar „-> foo” bar

będzie obsługiwać pliku * rozszerzone parametry i wolą pliku * rozszerzony parametr nad parametru nazwy pliku, niezależnie od kolejności, w jakiej pojawiają się w nagłówku

To paski informacje o nazwach folderów, np/etc/passwd -> passwd, a domyślnie jest to nazwa systemu podstawowego ze ścieżki URL pod nieobecność parametru nazwy pliku (lub nagłówka, lub jeśli wartość parametru jest pustym ciągiem)

Wyrażenia regularne token i qdtext są oparte na gramatyki z RFC 2616, wyrażeniach regularnych mimeCharset and valueChars są na podstawie gramatyki z RFC 5987, a język wyrażenie regularne jest oparty na gramatyce z RFC 5646

import re, urllib 
from os import path 
from urlparse import urlparse 

# content-disposition = "Content-Disposition" ":" 
#      disposition-type *(";" disposition-parm) 
# disposition-type = "inline" | "attachment" | disp-ext-type 
#      ; case-insensitive 
# disp-ext-type  = token 
# disposition-parm = filename-parm | disp-ext-parm 
# filename-parm  = "filename" "=" value 
#      | "filename*" "=" ext-value 
# disp-ext-parm  = token "=" value 
#      | ext-token "=" ext-value 
# ext-token   = <the characters in token, followed by "*"> 

token = '[-!#-\'*+.\dA-Z^-z|~]+' 
qdtext='[]-~\t !#-[]' 
mimeCharset='[-!#-&+\dA-Z^-z]+' 
language='(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}(?:-[A-Za-z]{3}){,2})?|[A-Za-z]{4,8})(?:-[A-Za-z]{4})?(?:-(?:[A-Za-z]{2}|\d{3}))(?:-(?:[\dA-Za-z]{5,8}|\d[\dA-Za-z]{3}))*(?:-[\dA-WY-Za-wy-z](?:-[\dA-Za-z]{2,8})+)*(?:-[Xx](?:-[\dA-Za-z]{1,8})+)?|[Xx](?:-[\dA-Za-z]{1,8})+|[Ee][Nn]-[Gg][Bb]-[Oo][Ee][Dd]|[Ii]-[Aa][Mm][Ii]|[Ii]-[Bb][Nn][Nn]|[Ii]-[Dd][Ee][Ff][Aa][Uu][Ll][Tt]|[Ii]-[Ee][Nn][Oo][Cc][Hh][Ii][Aa][Nn]|[Ii]-[Hh][Aa][Kk]|[Ii]-[Kk][Ll][Ii][Nn][Gg][Oo][Nn]|[Ii]-[Ll][Uu][Xx]|[Ii]-[Mm][Ii][Nn][Gg][Oo]|[Ii]-[Nn][Aa][Vv][Aa][Jj][Oo]|[Ii]-[Pp][Ww][Nn]|[Ii]-[Tt][Aa][Oo]|[Ii]-[Tt][Aa][Yy]|[Ii]-[Tt][Ss][Uu]|[Ss][Gg][Nn]-[Bb][Ee]-[Ff][Rr]|[Ss][Gg][Nn]-[Bb][Ee]-[Nn][Ll]|[Ss][Gg][Nn]-[Cc][Hh]-[Dd][Ee]' 
valueChars = '(?:%[\dA-F][\dA-F]|[-!#$&+.\dA-Z^-z|~])*' 
dispositionParm = '[Ff][Ii][Ll][Ee][Nn][Aa][Mm][Ee]\s*=\s*(?:({token})|"((?:{qdtext}|\\\\[\t !-~])*)")|[Ff][Ii][Ll][Ee][Nn][Aa][Mm][Ee]\*\s*=\s*({mimeCharset})\'(?:{language})?\'({valueChars})|{token}\s*=\s*(?:{token}|"(?:{qdtext}|\\\\[\t !-~])*")|{token}\*\s*=\s*{mimeCharset}\'(?:{language})?\'{valueChars}'.format(**locals()) 

try: 
    m = re.match('(?:{token}\s*;\s*)?(?:{dispositionParm})(?:\s*;\s*(?:{dispositionParm}))*|{token}'.format(**locals()), result.headers['Content-Disposition']) 

except KeyError: 
    name = path.basename(urllib.unquote(urlparse(url).path)) 

else: 
    if not m: 
    name = path.basename(urllib.unquote(urlparse(url).path)) 

    # Many user agent implementations predating this specification do not 
    # understand the "filename*" parameter. Therefore, when both "filename" 
    # and "filename*" are present in a single header field value, recipients 
    # SHOULD pick "filename*" and ignore "filename" 

    elif m.group(8) is not None: 
    name = urllib.unquote(m.group(8)).decode(m.group(7)) 

    elif m.group(4) is not None: 
    name = urllib.unquote(m.group(4)).decode(m.group(3)) 

    elif m.group(6) is not None: 
    name = re.sub('\\\\(.)', '\1', m.group(6)) 

    elif m.group(5) is not None: 
    name = m.group(5) 

    elif m.group(2) is not None: 
    name = re.sub('\\\\(.)', '\1', m.group(2)) 

    else: 
    name = m.group(1) 

    # Recipients MUST NOT be able to write into any location other than one to 
    # which they are specifically entitled 

    if name: 
    name = path.basename(name) 

    else: 
    name = path.basename(urllib.unquote(urlparse(url).path)) 
+0

Alternatywnie wyrażenia regularne można uprościć, nie sprawdzając poprawności znacznika języka, zwłaszcza, że ​​jest on ignorowany. Znacznik języka może zawierać nieograniczoną liczbę łączników, liczb i liter i jest opcjonalny. Więc po prostu zaakceptuj [- \ dA-Za-z] * dispositionParm = '[Ff] [Ii] [Ll] [Ee] [Nn] [Aa] [Mm] [Ee] \ s * = \ s * (?: ({token}) | "((?: {qdtext} | \\\\ [\ t! - ~]) *) ") | [Ff] [Ii] [Ll] [Ee] [Nn] [Aa] [Mm] [Ee] \ * \ s * = \ s * ({mimeCharset}) \ '[- \ dA-Za-z] * \' ({valueChars}) | {token} \ s * = \ s * (?: {token} | "(? : {qdtext} | \\\\ [\ t! - ~]) * ") | {token} \ * \ s * = \ s * {mimeCharset} \ '[- \ dA-Za-z] * \' {valueChars} '.format (** locals()) – user916968

Powiązane problemy