68 votes

Comment télécharger un fichier à l'aide de Python de manière plus intelligente ?

Je dois télécharger plusieurs fichiers via http en Python.

La façon la plus évidente de le faire est d'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 devrai m'occuper des URLs qui sont malveillants d'une certaine manière, comme ceci : http://server.com/!Run.aspx/someoddtext/somemore?id=121&m=pdf . Lorsqu'il est téléchargé via le navigateur, le fichier porte un nom lisible par l'homme, à savoir . accounts.pdf .

Existe-t-il un moyen de gérer cela en python, afin que je n'aie pas besoin de connaître les noms de fichiers et de les coder en dur dans mon script ?

41voto

Oli Points 65050

Les scripts de téléchargement de ce type 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 saisir cet en-tête, vous pouvez obtenir le nom de fichier approprié.

Il y a autre sujet qui a un petit peu de code à offrir pour Content-Disposition -Le vol.

remotefile = urllib2.urlopen('http://example.com/somefile.zip')
remotefile.info()['Content-Disposition']

35voto

kender Points 18446

Sur la base des commentaires et de la réponse de @Oli, j'ai trouvé 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 prend le nom du fichier dans Content-Disposition ; s'il n'est pas présent, il utilise le nom du fichier dans l'URL (s'il y a eu redirection, l'URL finale est prise en compte).

23voto

lostlogic Points 659

En combinant une grande partie de ce qui précède, voici une solution plus pythique :

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()

1voto

Denis Barmenkov Points 1190

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 mal formaté comme ["file.ext" ou [file.ext'] ou même être vide et nom local[0] soulèvera une exception. Le code correct peut ressembler à ceci :

localName = localName.replace('"', '').replace("'", "")
if localName == '':
    localName = SOME_DEFAULT_FILE_NAME

0voto

Jaydev Points 972

Utilisation de wget :

custom_file_name = "/custom/path/custom_name.ext"
wget.download(url, custom_file_name)

En utilisant urlretrieve :

urllib.urlretrieve(url, custom_file_name)

urlretrieve crée également la structure du répertoire si elle n'existe pas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X