I need to download several files via http in Python.
The most obvious way to do it is just using urllib2:
import urllib2
u = urllib2.urlopen('http://server.com/file.html')
localFile = open('file.html', 'w')
localFile.write(u.read())
localFile.close()
But I'll have to deal with the URLs that are nasty in some way, say like this: http://server.com/!Run.aspx/someoddtext/somemore?id=121&m=pdf
. When downloaded via the browser, the file has a human-readable name, ie. accounts.pdf
.
Is there any way to handle that in python, so I don't need to know the file names and hardcode them into my script?
Antworten:
Download scripts like that tend to push a header telling the user-agent what to name the file:
Content-Disposition: attachment; filename="the filename.ext"
If you can grab that header, you can get the proper filename.
There's another thread that has a little bit of code to offer up for
Content-Disposition
-grabbing.remotefile = urllib2.urlopen('http://example.com/somefile.zip') remotefile.info()['Content-Disposition']
quelle
Basierend auf Kommentaren und @ Olis Antwort habe ich eine Lösung wie diese gefunden:
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()
Es übernimmt den Dateinamen von Content-Disposition; Wenn es nicht vorhanden ist, wird der Dateiname aus der URL verwendet (wenn eine Umleitung erfolgt ist, wird die endgültige URL berücksichtigt).
quelle
urlsplit(url)[2]
mit einem Aufruf anurllib.unquote
abschließen, sonst würden die Dateinamen prozentual codiert. Hier ist, wie ich es mache:return basename(urllib.unquote(urlsplit(url)[2]))
Hier finden Sie eine pythonischere Lösung:
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()
quelle
2 Kender :
if localName[0] == '"' or localName[0] == "'": localName = localName[1:-1]
Es ist nicht sicher - der Webserver kann einen falsch formatierten Namen als ["file.ext] oder [file.ext '] übergeben oder sogar leer sein und localName [0] löst eine Ausnahme aus. Der richtige Code kann folgendermaßen aussehen:
localName = localName.replace('"', '').replace("'", "") if localName == '': localName = SOME_DEFAULT_FILE_NAME
quelle
local_name.strip('\'"')
- das wird sich nur von Anfang bis Ende ablösen und ist auch prägnanter.Verwenden von
wget
:custom_file_name = "/custom/path/custom_name.ext" wget.download(url, custom_file_name)
Verwenden von urlretrieve:
urlretrieve erstellt auch die Verzeichnisstruktur, falls nicht vorhanden.
quelle