J'ai besoin de télécharger plusieurs fichiers via http en Python.
La façon la plus évidente de le faire consiste simplement à utiliser urllib2:
import urllib2
u = urllib2.urlopen('http://server.com/file.html')
localFile = open('file.html', 'w')
localFile.write(u.read())
localFile.close()
Mais je vais devoir traiter les URL qui sont méchantes d'une certaine manière, disons comme ceci: http://server.com/!Run.aspx/someoddtext/somemore?id=121&m=pdf
. Lorsqu'il est téléchargé via le navigateur, le fichier a un nom lisible par l'homme, c'est-à-dire. accounts.pdf
.
Est-il possible de gérer cela en python, de sorte que je n'ai pas besoin de connaître les noms de fichiers et de les coder en dur dans mon script?
Les scripts de téléchargement comme celui-ci ont tendance à pousser un en-tête indiquant à l'agent utilisateur comment nommer le fichier:
Content-Disposition: attachment; filename="the filename.ext"
Si vous pouvez récupérer cet en-tête, vous pouvez obtenir le nom de fichier approprié.
Il y a un autre thread qui a un peu de code à offrir pour la capture de Content-Disposition
-.
remotefile = urllib2.urlopen('http://example.com/somefile.Zip')
remotefile.info()['Content-Disposition']
Sur la base des commentaires et de la réponse de @ Oli, j'ai proposé une solution comme celle-ci:
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()
Il faut un nom de fichier de Content-Disposition; s'il n'est pas présent, utilise filename de l'URL (si la redirection s'est produite, l'URL finale est prise en compte).
En combinant une grande partie de ce qui précède, voici une solution plus Pythonic:
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()
2 Kender :
if localName[0] == '"' or localName[0] == "'":
localName = localName[1:-1]
ce n’est pas sûr - le serveur Web peut transmettre un nom formaté incorrect comme ["fichier.ext] ou [fichier.ext '] ou même être vide et nom_local [0] lève une exception ..__ ressemble à ça:
localName = localName.replace('"', '').replace("'", "")
if localName == '':
localName = SOME_DEFAULT_FILE_NAME
Utiliser wget
:
custom_file_name = "/custom/path/custom_name.ext"
wget.download(url, custom_file_name)
Utiliser urlretrieve:
urllib.urlretrieve(url, custom_file_name)
urlretrieve crée également la structure de répertoires s'il n'existe pas.