如何确定在Python中使用HTTP下载的内容的文件名?

时间:2016-05-05 21:11:48

标签: python http-headers python-requests content-disposition

我使用Python requests库的get函数下载文件。为了存储文件,我想确定一个Web浏览器用于“保存”或“另存为...”对话框的文件名。

简单,对吗?我只能get it from the Content-Disposition HTTP标头,可在响应对象上访问:

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

但是仔细观察这个话题, 就不容易了:

根据RFC 6266 section 4.3section 4.1中的语法,值可以是不带引号的标记(例如the_report.pdf)或带引号的字符串,也可以包含空格(例如{{ 1}})和转义序列。此外,

  

当“filename”和“filename *”都出现在单个标题字段值中时,[我们]应该选择“filename *”并忽略“filename”。

"the report.pdf"的值yet a bit more complicatedfilename*的值{。}}。

此外,RFC似乎允许在filename附近添加额外的空格。

因此,对于examples listed in the RFC,我想要以下结果:

  • =
    文件名:Content-Disposition: Attachment; filename=example.html
  • example.html
    文件名:Content-Disposition: INLINE; FILENAME= "an example.html"
  • an example.html
    文件名:Content-Disposition: attachment; filename*= UTF-8''%e2%82%ac%20rates
  • € rates
    文件名:Content-Disposition: attachment; filename="EURO rates"; filename*=utf-8''%e2%82%ac%20rates 也在这里(不是€ rates,因为EURO rates优先)

现在,我可以很容易地使正则表达式适应filename*周围的变量空格,但是让它处理所有其他变量也会变得相当笨拙。 (通过引用和转义,我甚至不确定RegEx是否可以涵盖所有情况。也许它们可以,因为没有涉及支撑嵌套。)

所以我是否必须实现一个完整的解析器,或者我可以根据RFC 6266通过对HTTP库的一些调用来确定文件名(也许=本身)?由于RFC 6266是HTTP标准的一部分,我可以想象一些专门用于HTTP的库已经涵盖了这一点。 (所以我also asked on Software Recommendations SE。)

2 个答案:

答案 0 :(得分:10)

rfc6266库似乎完全符合您的需要。它可以解析原始标头,requests响应和urllib2响应。它在PyPI上。

一些例子:

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

但需要注意的是:这个库就像标题中的非标准空格一样。

答案 1 :(得分:0)

如果您真的不需要utf-8中的结果

def getFilename(s):
  fname = re.findall("filename\*?=([^;]+)", s, flags=re.IGNORECASE)
  print fname[0].strip().strip('"')

但是如果必须使用utf-8

def getFilename(s):
    fname = re.findall("filename\*=([^;]+)", s, flags=re.IGNORECASE)
    if not fname:
        fname = re.findall("filename=([^;]+)", s, flags=re.IGNORECASE)
    if "utf-8''" in fname[0].lower():
        fname = re.sub("utf-8''", '', fname[0], flags=re.IGNORECASE)
        fname = urllib.unquote(fname).decode('utf8')
    else:
        fname = fname[0]
    # clean space and double quotes
    print fname.strip().strip('"')

# example
getFilename('Attachment; filename=example.html')
getFilename('INLINE; FILENAME= "an example.html"')

getFilename("attachment;filename*= UTF-8''%e2%82%ac%20rates")
getFilename("attachment; filename=\"EURO rates\";filename*=utf-8''%e2%82%ac%20rates")

getFilename("attachment;filename=\"_____ _____ ___ __ ____ _____ Hekayt Bent.2017.mp3\";filename*=UTF-8''%D8%A7%D8%BA%D9%86%D9%8A%D9%87%20%D8%AD%D9%83%D8%A7%D9%8A%D8%A9%20%D8%A8%D9%86%D8%AA%20%D9%84%D9%80%20%D9%85%D8%AD%D9%85%D8%AF%20%D8%B4%D8%AD%D8%A7%D8%AA%D8%A9%20Hekayt%20Bent.2017.mp3")

结果

example.html
an example.html
€ rates
€ rates
اغنيه حكاية بنت لـ محمد شحاتة Hekayt Bent.2017.mp3