如何用Python更“智能”地下载文件?
我需要在Python中通过HTTP下载几个文件。
最简单的方法就是用urllib2这个库:
import urllib2
u = urllib2.urlopen('http://server.com/file.html')
localFile = open('file.html', 'w')
localFile.write(u.read())
localFile.close()
但是我会遇到一些比较麻烦的URL,比如这样:http://server.com/!Run.aspx/someoddtext/somemore?id=121&m=pdf
。通过浏览器下载时,文件的名字是人类可读的,比如说accounts.pdf
。
有没有什么办法可以在Python中处理这些情况,这样我就不需要知道文件名,也不用把它们写死在我的代码里?
6 个回答
23
把上面提到的内容结合起来,这里有一个更符合Python风格的解决方案:
import urllib2
import shutil
import urlparse
import os
def download(url, fileName=None):
def getFileName(url,openUrl):
if 'Content-Disposition' in openUrl.info():
# If the response has Content-Disposition, try to get filename from it
cd = dict(map(
lambda x: x.strip().split('=') if '=' in x else (x.strip(),''),
openUrl.info()['Content-Disposition'].split(';')))
if 'filename' in cd:
filename = cd['filename'].strip("\"'")
if filename: return filename
# if no filename was found above, parse it out of the final URL.
return os.path.basename(urlparse.urlsplit(openUrl.url)[2])
r = urllib2.urlopen(urllib2.Request(url))
try:
fileName = fileName or getFileName(url,r)
with open(fileName, 'wb') as f:
shutil.copyfileobj(r,f)
finally:
r.close()
35
根据大家的评论和@Oli的回答,我做了一个这样的解决方案:
from os.path import basename
from urlparse import urlsplit
def url2name(url):
return basename(urlsplit(url)[2])
def download(url, localFileName = None):
localName = url2name(url)
req = urllib2.Request(url)
r = urllib2.urlopen(req)
if r.info().has_key('Content-Disposition'):
# If the response has Content-Disposition, we take file name from it
localName = r.info()['Content-Disposition'].split('filename=')[1]
if localName[0] == '"' or localName[0] == "'":
localName = localName[1:-1]
elif r.url != url:
# if we were redirected, the real file name we take from the final URL
localName = url2name(r.url)
if localFileName:
# we can force to save the file as specified name
localName = localFileName
f = open(localName, 'wb')
f.write(r.read())
f.close()
这个方案会从内容的“Content-Disposition”中获取文件名;如果没有这个信息,就会使用网址中的文件名(如果有重定向的话,会考虑最终的网址)。
40
像这样的下载脚本通常会发送一个头信息,告诉用户的浏览器应该给文件起什么名字:
Content-Disposition: attachment; filename="the filename.ext"
如果你能获取到这个头信息,就能得到正确的文件名。
还有一个讨论串,里面有一些代码可以用来获取Content-Disposition
的信息。
remotefile = urllib2.urlopen('http://example.com/somefile.zip')
remotefile.info()['Content-Disposition']