Как загрузить файл с использованием python "умнее"?
Мне нужно загрузить несколько файлов через http в Python.
Самый очевидный способ сделать это - просто использовать 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, поэтому мне не нужно знать имена файлов и жестко кодировать их в свой script?
Ответы
Ответ 1
Скачайте такие скрипты, как правило, нажмите заголовок, сообщающий пользователю-агенту, что назвать файл:
Content-Disposition: attachment; filename="the filename.ext"
Если вы можете захватить этот заголовок, вы можете получить правильное имя файла.
Там другой поток, который имеет немного кода для предложения Content-Disposition
-grabbing.
remotefile = urllib2.urlopen('http://example.com/somefile.zip')
remotefile.info()['Content-Disposition']
Ответ 2
Основываясь на комментариях и @Oli anwser, я сделал следующее решение:
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; если он отсутствует, использует имя файла из URL-адреса (если произошло перенаправление, учитывается конечный URL-адрес).
Ответ 3
Объединяя большую часть вышесказанного, вот более питоновское решение:
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()
Ответ 4
2 Kender:
if localName[0] == '"' or localName[0] == "'":
localName = localName[1:-1]
он небезопасен - веб-сервер может передавать неправильное форматированное имя как ["file.ext] или [file.ext '] или даже быть пустым, а localName [0] вызывает исключение.
Правильный код может выглядеть так:
localName = localName.replace('"', '').replace("'", "")
if localName == '':
localName = SOME_DEFAULT_FILE_NAME
Ответ 5
Использование wget
:
custom_file_name = "/custom/path/custom_name.ext"
wget.download(url, custom_file_name)
Использование urlretrieve:
urllib.urlretrieve(url, custom_file_name)
urlretrieve также создает структуру каталогов, если она не существует.