web-dev-qa-db-fra.com

Comment télécharger un fichier en utilisant python d'une manière "plus intelligente"?

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?

67
kender

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']
41
Oli

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

35
kender

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

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
1
Denis Barmenkov

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. 

0
Jaydev