python http-headers python-requests content-disposition

cómo determinar el nombre de archivo del contenido descargado con HTTP en Python?



http-headers python-requests (1)

Descargo un archivo usando la función get de la biblioteca de requests de Python. Para almacenar el archivo, me gustaría determinar el nombre de archivo de la forma en que lo haría un navegador web para su diálogo ''guardar'' o ''guardar como ...''.

Fácil, ¿verdad? Solo puedo obtenerlo del encabezado HTTP Content-Disposition , accesible en el objeto de respuesta:

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

Pero mirando más de cerca este tema, no es tan fácil:

De acuerdo con RFC 6266 sección 4.3 , y la gramática en la sección 4.1 , el valor puede ser un token sin the_report.pdf (por ejemplo, the_report.pdf ) o una cadena entrecomillada que también puede contener espacios en blanco (por ejemplo, "the report.pdf" ) y secuencias de escape. Promover,

cuando tanto "nombre de archivo" como "nombre de archivo *" están presentes en un solo valor de campo de encabezado, [nosotros] DEBERÍAMOS elegir "nombre de archivo *" e ignorar "nombre de archivo".

El valor de filename* , sin embargo, es un poco más complicado que el de filename .

Además, el RFC parece permitir espacios en blanco adicionales alrededor del = .

Por lo tanto, para los ejemplos enumerados en el RFC , me gustaría obtener los siguientes resultados:

  • Content-Disposition: Attachment; filename=example.html nombre de archivo: example.html

  • Content-Disposition: INLINE; FILENAME= "an example.html" nombre de archivo: an example.html

  • Content-Disposition: attachment; filename*= UTF-8''''%e2%82%ac%20rates nombre de archivo: € rates

  • Content-Disposition: attachment; filename="EURO rates"; filename*=utf-8''''%e2%82%ac%20rates nombre de archivo: las € rates aquí también (no las EURO rates , ya que el filename* tiene prioridad)

Ahora, podría adaptar fácilmente la expresión regular para dar cuenta de los espacios en blanco variables alrededor de la = , pero tener que manejar todas las otras variaciones, también, sería bastante difícil de manejar. (Con las citas y los escapes, ni siquiera estoy seguro de que RegEx pueda cubrir todos los casos. Tal vez puedan hacerlo, ya que no se requiere anudamiento).

Entonces, ¿tengo que implementar un analizador completo, o puedo determinar el nombre del archivo de acuerdo con RFC 6266 mediante algunas llamadas a una biblioteca HTTP (tal vez se requests sí mismo)? Como RFC 6266 es parte del estándar HTTP, podría imaginar que algunas bibliotecas especializadas en HTTP ya lo cubren. (Así que también he pedido en Software Recommendations SE .)


La biblioteca rfc6266 parece hacer exactamente lo que necesita. Puede analizar encabezados sin procesar, respuestas de requests y respuestas urllib2 . Está en PyPI .

Algunos ejemplos:

>>> 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''

Como nota, sin embargo: a esta biblioteca no le gustan los espacios en blanco no estándar en el encabezado.