1099 votes

Comment télécharger un fichier via HTTP ?

J'ai un petit utilitaire que j'utilise pour télécharger un fichier MP3 à partir d'un site Web selon un calendrier, puis construire/mettre à jour un fichier XML de podcast que j'ai ajouté à iTunes.

Le traitement de texte qui crée/met à jour le fichier XML est écrit en Python. Cependant, j'utilise wget dans un système Windows .bat pour télécharger le fichier MP3. Je préférerais que l'utilitaire soit entièrement écrit en Python.

J'ai eu du mal à trouver un moyen de télécharger le fichier en Python, c'est pourquoi j'ai eu recours à la méthode suivante wget .

Alors, comment puis-je télécharger le fichier en utilisant Python ?

2 votes

Beaucoup des réponses ci-dessous ne remplacent pas de manière satisfaisante wget . Entre autres choses, wget (1) préserve l'horodatage (2) détermine automatiquement le nom du fichier à partir de l'url, en l'ajoutant .1 (etc.) si le fichier existe déjà (3) a beaucoup d'autres options, dont certaines que vous avez peut-être mises dans vos .wgetrc . Si vous voulez l'un de ces éléments, vous devez l'implémenter vous-même en Python, mais il est plus simple de simplement invoquer wget de Python.

3 votes

Solution courte pour Python 3 : import urllib.request; s = urllib.request.urlopen('http://example.com/').read().decode(‌​)

1319voto

PabloG Points 9308

Encore une fois, en utilisant urlretrieve :

import urllib
urllib.urlretrieve ("http://www.example.com/songs/mp3.mp3", "mp3.mp3")

(pour Python 3+, utilisez import urllib.request et urllib.request.urlretrieve )

Encore un autre, avec une "barre de progression".

import urllib2

url = "http://download.thinkbroadband.com/10MB.zip"

file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)

file_size_dl = 0
block_sz = 8192
while True:
    buffer = u.read(block_sz)
    if not buffer:
        break

    file_size_dl += len(buffer)
    f.write(buffer)
    status = r"%10d  [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
    status = status + chr(8)*(len(status)+1)
    print status,

f.close()

1 votes

Curieusement, cela a fonctionné pour moi sous Windows alors que la méthode urllib2 n'a pas fonctionné. La méthode urllib2 a fonctionné sur Mac, par contre.

7 votes

Bogue : file_size_dl += block_sz devrait être += len(buffer) car la dernière lecture n'est souvent pas un block_sz complet. De plus, sous Windows, vous devez ouvrir le fichier de sortie en tant que "wb" s'il ne s'agit pas d'un fichier texte.

1 votes

Moi aussi urllib et urllib2 n'ont pas fonctionné mais urlretrieve a bien fonctionné, j'étais frustré - merci :)

550voto

Corey Points 5286

Utilisez urllib.request.urlopen() :

import urllib.request
with urllib.request.urlopen('http://www.example.com/') as f:
    html = f.read().decode('utf-8')

Il s'agit de la façon la plus basique d'utiliser la bibliothèque, sans la gestion des erreurs. Vous pouvez aussi faire des choses plus complexes comme changer les en-têtes.

Sur Python 2, la méthode se trouve dans urllib2 :

import urllib2
response = urllib2.urlopen('http://www.example.com/')
html = response.read()

13 votes

Cela ne fonctionnera pas si l'url que vous fournissez contient des espaces. Dans ce cas, vous devrez analyser l'url et encoder le chemin.

113 votes

Voici la solution Python 3 : stackoverflow.com/questions/7243750/

6 votes

Juste pour référence. La façon de coder le chemin est la suivante urllib2.quote

417voto

hughdbrown Points 15770

En 2012, utilisez le bibliothèque de requêtes python

>>> import requests
>>> 
>>> url = "http://download.thinkbroadband.com/10MB.zip"
>>> r = requests.get(url)
>>> print len(r.content)
10485760

Vous pouvez exécuter pip install requests pour l'obtenir.

Requests présente de nombreux avantages par rapport aux autres solutions, car l'API est beaucoup plus simple. Cela est particulièrement vrai si vous devez procéder à une authentification. urllib et urllib2 sont assez peu intuitifs et pénibles dans ce cas.


2015-12-30

Les gens ont exprimé leur admiration pour la barre de progression. C'est cool, bien sûr. Il existe désormais plusieurs solutions prêtes à l'emploi, notamment tqdm :

from tqdm import tqdm
import requests

url = "http://download.thinkbroadband.com/10MB.zip"
response = requests.get(url, stream=True)

with open("10MB", "wb") as handle:
    for data in tqdm(response.iter_content()):
        handle.write(data)

Il s'agit essentiellement de l'implémentation décrite par @kvance il y a 30 mois.

0 votes

Comment sauvegarder ou extraire si le fichier zip est en fait un dossier contenant de nombreux fichiers ?

8 votes

Comment gère-t-il les gros fichiers, est-ce que tout est stocké en mémoire ou est-ce que cela peut être écrit dans un fichier sans avoir besoin de beaucoup de mémoire ?

9 votes

Il est possible de diffuser des fichiers volumineux en définissant stream=True dans la requête. Vous pouvez ensuite appeler iter_content() sur la réponse pour lire un morceau à la fois.

170voto

Grant Points 5366
import urllib2
mp3file = urllib2.urlopen("http://www.example.com/songs/mp3.mp3")
with open('test.mp3','wb') as output:
  output.write(mp3file.read())

Le site wb sur open('test.mp3','wb') ouvre un fichier (et efface tout fichier existant) en mode binaire afin que vous puissiez y enregistrer des données plutôt que du texte.

38 votes

L'inconvénient de cette solution est que le fichier entier est chargé dans la mémoire vive avant d'être sauvegardé sur le disque. C'est une chose à garder à l'esprit si vous utilisez cette solution pour des fichiers volumineux sur un petit système comme un routeur avec une mémoire vive limitée.

2 votes

@tripplet alors comment réparer cela ?

11 votes

Pour éviter de lire le fichier entier en mémoire, essayez de passer un argument à file.read qui est le nombre d'octets à lire. Voir : gist.github.com/hughdbrown/c145b8385a2afa6570e2

26voto

Stan Points 55

Une version améliorée du code PabloG pour Python 2/3 :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import ( division, absolute_import, print_function, unicode_literals )

import sys, os, tempfile, logging

if sys.version_info >= (3,):
    import urllib.request as urllib2
    import urllib.parse as urlparse
else:
    import urllib2
    import urlparse

def download_file(url, dest=None):
    """ 
    Download and save a file specified by url to dest directory,
    """
    u = urllib2.urlopen(url)

    scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
    filename = os.path.basename(path)
    if not filename:
        filename = 'downloaded.file'
    if dest:
        filename = os.path.join(dest, filename)

    with open(filename, 'wb') as f:
        meta = u.info()
        meta_func = meta.getheaders if hasattr(meta, 'getheaders') else meta.get_all
        meta_length = meta_func("Content-Length")
        file_size = None
        if meta_length:
            file_size = int(meta_length[0])
        print("Downloading: {0} Bytes: {1}".format(url, file_size))

        file_size_dl = 0
        block_sz = 8192
        while True:
            buffer = u.read(block_sz)
            if not buffer:
                break

            file_size_dl += len(buffer)
            f.write(buffer)

            status = "{0:16}".format(file_size_dl)
            if file_size:
                status += "   [{0:6.2f}%]".format(file_size_dl * 100 / file_size)
            status += chr(13)
            print(status, end="")
        print()

    return filename

if __name__ == "__main__":  # Only run if this file is called directly
    print("Testing with 10MB download")
    url = "http://download.thinkbroadband.com/10MB.zip"
    filename = download_file(url)
    print(filename)

0 votes

Je supprimerais les parenthèses de la première ligne, car il ne s'agit pas d'une fonctionnalité trop ancienne.

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