Comment télécharger un fichier via HTTP en utilisant Python?

875

J'ai un petit utilitaire que j'utilise pour télécharger un fichier MP3 à partir d'un site Web selon un calendrier, puis crée / met à 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 .batfichier Windows pour télécharger le fichier MP3 réel. Je préférerais que l'utilitaire entier soit écrit en Python.

J'ai eu du mal à trouver un moyen de télécharger réellement le fichier en Python, c'est pourquoi j'ai eu recours à l'utilisation wget.

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

Owen
la source
La plupart des réponses ci-dessous ne remplacent pas de manière satisfaisante wget. Entre autres choses, wget(1) préserve les horodatages (2) détermine automatiquement le nom de fichier à partir de l'URL, ajoute .1(etc.) si le fichier existe déjà (3) a de nombreuses autres options, dont certaines peuvent avoir été insérées dans votre .wgetrc. Si vous en voulez, vous devez les implémenter vous-même en Python, mais il est plus simple de simplement les invoquer wgetdepuis Python.
ShreevatsaR
2
Solution courte pour Python 3:import urllib.request; s = urllib.request.urlopen('http://example.com/').read().decode()
Basj

Réponses:

450

En Python 2, utilisez urllib2 fourni avec la bibliothèque standard.

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

Il s'agit de la manière la plus simple d'utiliser la bibliothèque, sans gestion des erreurs. Vous pouvez également effectuer des tâches plus complexes telles que la modification des en-têtes. La documentation se trouve ici.

Corey
la source
11
Cela ne fonctionnera pas s'il y a des espaces dans l'URL que vous fournissez. Dans ce cas, vous devrez analyser l'URL et urlencode le chemin.
Jason Sundram
91
Voici la solution Python 3: stackoverflow.com/questions/7243750/…
tommy.carstensen
6
Juste pour référence. La façon d'urlencode le chemin esturllib2.quote
André Puel
11
@JasonSundram: S'il y a des espaces, ce n'est pas un URI.
Zaz
1
Cela ne fonctionne pas sur les fenêtres contenant des fichiers plus volumineux. Vous devez lire tous les blocs!
Avia
1115

Un de plus, en utilisant urlretrieve:

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

(pour Python 3+ import urllib.requestet 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()
PabloG
la source
1
Curieusement, cela a fonctionné pour moi sur Windows alors que la méthode urllib2 ne le ferait pas. La méthode urllib2 fonctionnait cependant sur Mac.
InFreefall
6
Bug: file_size_dl + = block_sz devrait être + = len (buffer) car la dernière lecture n'est souvent pas un block_sz complet. Sous Windows, vous devez également ouvrir le fichier de sortie en tant que "wb" s'il ne s'agit pas d'un fichier texte.
Aubergine Jeff
1
Moi aussi, urllib et urllib2 ne fonctionnaient pas mais urlretrieve fonctionnait bien, devenait frustré - merci :)
funk-shun
2
Enveloppez le tout (sauf la définition de nom_fichier) avec if not os.path.isfile(file_name):pour éviter d'écraser les podcasts! utile lors de son exécution en tant que cronjob avec les URL présentes dans un fichier .html
Sriram Murali
2
@PabloG c'est un peu plus de 31 votes maintenant;) Quoi qu'il en soit, la barre d'état était amusante donc je vais +1
Cinder
340

En 2012, utilisez la 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 courir pip install requestspour l'obtenir.

Les demandes présentent de nombreux avantages par rapport aux alternatives car l'API est beaucoup plus simple. Cela est particulièrement vrai si vous devez effectuer une authentification. urllib et urllib2 sont assez peu intuitifs et douloureux 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 maintenant plusieurs solutions standard, 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 @kvance décrite il y a 30 mois.

Hughdbrown
la source
comment enregistrer ou extraire si le fichier zip est en fait un dossier contenant de nombreux fichiers?
Abdul Muneer
6
Comment cela gère-t-il les fichiers volumineux, tout est-il stocké en mémoire ou peut-il être écrit dans un fichier sans mémoire importante?
bibstha
8
Il est possible de diffuser des fichiers volumineux en définissant stream = True dans la demande. Vous pouvez ensuite appeler iter_content () sur la réponse pour lire un morceau à la fois.
kvance
7
Pourquoi une bibliothèque d'URL aurait-elle besoin d'une fonction de décompression de fichier? Lisez le fichier à partir de l'URL, enregistrez-le, puis décompressez-le de la manière qui fait flotter votre bateau. Un fichier zip n'est pas non plus un «dossier» comme il apparaît dans Windows, c'est un fichier.
Harel
2
@Ali:: r.textpour le contenu texte ou unicode. Renvoyé en unicode. r.content: Pour le contenu binaire. Renvoyé en octets. Lisez à ce sujet ici: docs.python-requests.org/en/latest/user/quickstart
hughdbrown
159
import urllib2
mp3file = urllib2.urlopen("http://www.example.com/songs/mp3.mp3")
with open('test.mp3','wb') as output:
  output.write(mp3file.read())

Le wbin open('test.mp3','wb')ouvre un fichier (et efface tout fichier existant) en mode binaire afin que vous puissiez enregistrer des données avec lui au lieu de simplement du texte.

Subvention
la source
30
L'inconvénient de cette solution est que le fichier entier est chargé dans le ram avant d'être enregistré sur le disque, juste quelque chose à garder à l'esprit si vous l'utilisez pour de gros fichiers sur un petit système comme un routeur avec un ram limité.
tripplet
2
@tripplet alors comment pourrions-nous résoudre ce problème?
Lucas Henrique
11
Pour éviter de lire l'intégralité du fichier en mémoire, essayez de passer un argument file.readqui correspond au nombre d'octets à lire. Voir: gist.github.com/hughdbrown/c145b8385a2afa6570e2
hughdbrown
@hughdbrown J'ai trouvé votre script utile, mais j'ai une question: puis-je utiliser le fichier pour le post-traitement? supposons que je télécharge un fichier jpg que je souhaite traiter avec OpenCV, puis-je utiliser la variable 'data' pour continuer à travailler? ou dois-je le relire à partir du fichier téléchargé?
Rodrigo E. Principe
5
Utilisez shutil.copyfileobj(mp3file, output)plutôt.
Aurélien Ooms
130

Python 3

  • urllib.request.urlopen

    import urllib.request
    response = urllib.request.urlopen('http://www.example.com/')
    html = response.read()
  • urllib.request.urlretrieve

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

    Remarque: Selon la documentation, urllib.request.urlretrieveest une "interface héritée" et "pourrait devenir obsolète à l'avenir" (merci gerrit )

Python 2

  • urllib2.urlopen(merci Corey )

    import urllib2
    response = urllib2.urlopen('http://www.example.com/')
    html = response.read()
  • urllib.urlretrieve(merci PabloG )

    import urllib
    urllib.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')
bmaupin
la source
2
Cela a certainement pris du temps, mais là, enfin, c'est l'api simple et simple que j'attends d'un stdlib en python :)
ThorSummoner
Très belle réponse pour python3, voir aussi docs.python.org/3/library/…
Edouard Thiel
@EdouardThiel Si vous cliquez urllib.request.urlretrievedessus, cela vous amènera à ce lien exact. À votre santé!
bmaupin
2
urllib.request.urlretrieveest documenté comme une "interface héritée" et "pourrait devenir obsolète à l'avenir".
gerrit
@gerrit J'ai ajouté une note, merci pour l'avertissement!
bmaupin
32

utiliser le module wget:

import wget
wget.download('url')
Sara Santana
la source
21

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)
Stan
la source
Je supprimerais les parenthèses de la première ligne, car ce n'est pas une fonctionnalité trop ancienne.
Arpad Horvath
21

Un Python 2 & Python 3moyen simple mais compatible est fourni avec la sixbibliothèque:

from six.moves import urllib
urllib.request.urlretrieve("http://www.example.com/songs/mp3.mp3", "mp3.mp3")
Akif
la source
1
C'est la meilleure façon de le faire pour une compatibilité 2 + 3.
Fush
21
import os,requests
def download(url):
    get_response = requests.get(url,stream=True)
    file_name  = url.split("/")[-1]
    with open(file_name, 'wb') as f:
        for chunk in get_response.iter_content(chunk_size=1024):
            if chunk: # filter out keep-alive new chunks
                f.write(chunk)


download("https://example.com/example.jpg")
HS Umer farooq
la source
17

A écrit la bibliothèque wget en Python pur juste à cet effet. Il est optimisé urlretrieveavec ces fonctionnalités à partir de la version 2.0.

anatoly techtonik
la source
3
Aucune option pour enregistrer avec un nom de fichier personnalisé?
Alex
2
@Alex a ajouté l'option -o FILENAME à la version 2.1
anatoly techtonik
La barre de progression n'apparaît pas lorsque j'utilise ce module sous Cygwin.
Joe Coder
Vous devriez changer de -opour -Oéviter toute confusion, comme c'est le cas dans GNU wget. Ou au moins les deux options doivent être valides.
erik
@eric Je ne suis pas sûr de vouloir faire wget.pyun remplacement sur place pour de vrai wget. Le -ose comporte déjà différemment - il est compatible avec curlcette façon. Une note dans la documentation aiderait-elle à résoudre le problème? Ou est-ce la caractéristique essentielle pour qu'un utilitaire avec un tel nom soit compatible avec la ligne de commande?
anatoly techtonik
16

Voici les appels les plus couramment utilisés pour télécharger des fichiers en python:

  1. urllib.urlretrieve ('url_to_file', file_name)

  2. urllib2.urlopen('url_to_file')

  3. requests.get(url)

  4. wget.download('url', file_name)

Remarque: urlopenet urlretrieves'avèrent relativement mauvais avec le téléchargement de fichiers volumineux (taille> 500 Mo). requests.getstocke le fichier en mémoire jusqu'à la fin du téléchargement.

Jaydev
la source
14

Je suis d'accord avec Corey, urllib2 est plus complet que urllib et devrait probablement être le module utilisé si vous voulez faire des choses plus complexes, mais pour rendre les réponses plus complètes, urllib est un module plus simple si vous voulez juste les bases:

import urllib
response = urllib.urlopen('http://www.example.com/sound.mp3')
mp3 = response.read()

Fonctionnera bien. Ou, si vous ne voulez pas traiter avec l'objet "réponse", vous pouvez appeler directement read () :

import urllib
mp3 = urllib.urlopen('http://www.example.com/sound.mp3').read()
akdom
la source
10

En python3, vous pouvez utiliser les librairies urllib3 et shutil. Téléchargez-les en utilisant pip ou pip3 (selon que python3 est par défaut ou non)

pip3 install urllib3 shutil

Exécutez ensuite ce code

import urllib.request
import shutil

url = "http://www.somewebsite.com/something.pdf"
output_file = "save_this_name.pdf"
with urllib.request.urlopen(url) as response, open(output_file, 'wb') as out_file:
    shutil.copyfileobj(response, out_file)

Notez que vous téléchargez urllib3mais utilisez urllibdans le code

Apoorv Agarwal
la source
7

Vous pouvez également obtenir le retour d'informations sur la progression avec urlretrieve:

def report(blocknr, blocksize, size):
    current = blocknr*blocksize
    sys.stdout.write("\r{0:.2f}%".format(100.0*current/size))

def downloadFile(url):
    print "\n",url
    fname = url.split('/')[-1]
    print fname
    urllib.urlretrieve(url, fname, report)
Marcin Cuprjak
la source
7

Si vous avez installé wget, vous pouvez utiliser parallel_sync.

pip installer parallel_sync

from parallel_sync import wget
urls = ['http://something.png', 'http://somthing.tar.gz', 'http://somthing.zip']
wget.download('/tmp', urls)
# or a single file:
wget.download('/tmp', urls[0], filenames='x.zip', extract=True)

Doc: https://pythonhosted.org/parallel_sync/pages/examples.html

C'est assez puissant. Il peut télécharger des fichiers en parallèle, réessayer en cas d'échec, et il peut même télécharger des fichiers sur une machine distante.

max
la source
Notez que ceci est pour Linux uniquement
jjj
4

Si la vitesse vous importe, j'ai fait un petit test de performance pour les modules urllibet wget, en ce qui concerne wgetj'ai essayé une fois avec la barre d'état et une fois sans. J'ai pris trois fichiers différents de 500 Mo pour tester (différents fichiers - pour éliminer le risque de mise en cache sous le capot). Testé sur une machine Debian, avec python2.

Premièrement, voici les résultats (ils sont similaires dans différentes séries):

$ python wget_test.py 
urlretrive_test : starting
urlretrive_test : 6.56
==============
wget_no_bar_test : starting
wget_no_bar_test : 7.20
==============
wget_with_bar_test : starting
100% [......................................................................] 541335552 / 541335552
wget_with_bar_test : 50.49
==============

La façon dont j'ai effectué le test utilise un décorateur de "profil". Voici le code complet:

import wget
import urllib
import time
from functools import wraps

def profile(func):
    @wraps(func)
    def inner(*args):
        print func.__name__, ": starting"
        start = time.time()
        ret = func(*args)
        end = time.time()
        print func.__name__, ": {:.2f}".format(end - start)
        return ret
    return inner

url1 = 'http://host.com/500a.iso'
url2 = 'http://host.com/500b.iso'
url3 = 'http://host.com/500c.iso'

def do_nothing(*args):
    pass

@profile
def urlretrive_test(url):
    return urllib.urlretrieve(url)

@profile
def wget_no_bar_test(url):
    return wget.download(url, out='/tmp/', bar=do_nothing)

@profile
def wget_with_bar_test(url):
    return wget.download(url, out='/tmp/')

urlretrive_test(url1)
print '=============='
time.sleep(1)

wget_no_bar_test(url2)
print '=============='
time.sleep(1)

wget_with_bar_test(url3)
print '=============='
time.sleep(1)

urllib semble être le plus rapide

Omer Dagan
la source
Il doit y avoir quelque chose de complètement horrible sous le capot pour que la barre augmente tellement le temps.
Alistair Carscadden
4

Par souci d'exhaustivité, il est également possible d'appeler n'importe quel programme pour récupérer des fichiers à l'aide du subprocesspackage. Les programmes dédiés à la récupération de fichiers sont plus puissants que les fonctions Python urlretrieve. Par exemple, wgetpeut télécharger des répertoires de manière récursive ( -R), peut gérer le FTP, les redirections, les proxys HTTP, peut éviter de retélécharger les fichiers existants ( -nc), et aria2peut effectuer des téléchargements à connexions multiples qui peuvent potentiellement accélérer vos téléchargements.

import subprocess
subprocess.check_output(['wget', '-O', 'example_output_file.html', 'https://example.com'])

Dans Jupyter Notebook, on peut également appeler des programmes directement avec la !syntaxe:

!wget -O example_output_file.html https://example.com
Robin Dinse
la source
3

Le code source peut être:

import urllib
sock = urllib.urlopen("http://diveintopython.org/")
htmlSource = sock.read()                            
sock.close()                                        
print htmlSource  
Olu Smith
la source
3

Vous pouvez utiliser PycURL sur Python 2 et 3.

import pycurl

FILE_DEST = 'pycurl.html'
FILE_SRC = 'http://pycurl.io/'

with open(FILE_DEST, 'wb') as f:
    c = pycurl.Curl()
    c.setopt(c.URL, FILE_SRC)
    c.setopt(c.WRITEDATA, f)
    c.perform()
    c.close()
gzerone
la source
2

J'ai écrit ce qui suit, qui fonctionne en vanilla Python 2 ou Python 3.


import sys
try:
    import urllib.request
    python3 = True
except ImportError:
    import urllib2
    python3 = False


def progress_callback_simple(downloaded,total):
    sys.stdout.write(
        "\r" +
        (len(str(total))-len(str(downloaded)))*" " + str(downloaded) + "/%d"%total +
        " [%3.2f%%]"%(100.0*float(downloaded)/float(total))
    )
    sys.stdout.flush()

def download(srcurl, dstfilepath, progress_callback=None, block_size=8192):
    def _download_helper(response, out_file, file_size):
        if progress_callback!=None: progress_callback(0,file_size)
        if block_size == None:
            buffer = response.read()
            out_file.write(buffer)

            if progress_callback!=None: progress_callback(file_size,file_size)
        else:
            file_size_dl = 0
            while True:
                buffer = response.read(block_size)
                if not buffer: break

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

                if progress_callback!=None: progress_callback(file_size_dl,file_size)
    with open(dstfilepath,"wb") as out_file:
        if python3:
            with urllib.request.urlopen(srcurl) as response:
                file_size = int(response.getheader("Content-Length"))
                _download_helper(response,out_file,file_size)
        else:
            response = urllib2.urlopen(srcurl)
            meta = response.info()
            file_size = int(meta.getheaders("Content-Length")[0])
            _download_helper(response,out_file,file_size)

import traceback
try:
    download(
        "https://geometrian.com/data/programming/projects/glLib/glLib%20Reloaded%200.5.9/0.5.9.zip",
        "output.zip",
        progress_callback_simple
    )
except:
    traceback.print_exc()
    input()

Remarques:

  • Prend en charge un rappel "barre de progression".
  • Le téléchargement est un test .zip de 4 Mo sur mon site Web.
imallett
la source
fonctionne très bien, exécutez-le via jupyter a obtenu ce que je veux :-)
Samir Ouldsaadi
1

Cela peut être un peu tard, mais j'ai vu le code de pabloG et je n'ai pas pu m'empêcher d'ajouter un os.system ('cls') pour lui donner un aspect IMPRESSIONNANT! Vérifiez-le :

    import urllib2,os

    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)
    os.system('cls')
    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()

Si vous exécutez dans un environnement autre que Windows, vous devrez utiliser autre chose que «cls». Sous MAC OS X et Linux, cela devrait être «clair».

JD3
la source
3
clsne fait rien sur mon OS X ou sur un de mes serveurs Ubuntu. Certaines clarifications pourraient être utiles.
kqw
Je pense que vous devriez utiliser clearpour linux, ou encore mieux remplacer la ligne d'impression au lieu d'effacer toute la sortie de la ligne de commande.
Arijoon
4
cette réponse copie simplement une autre réponse et ajoute un appel à une fonction obsolète ( os.system()) qui lance un sous-processus pour effacer l'écran à l'aide d'une commande spécifique à la plate-forme ( cls). Comment cela at-il des votes positifs ?? "Réponse" absolument sans valeur à mon humble avis.
Corey Goldberg
1

urlretrieve et requests.get sont simples, mais pas la réalité. J'ai récupéré des données pour quelques sites, y compris du texte et des images, les deux ci-dessus résolvent probablement la plupart des tâches. mais pour une solution plus universelle, je suggère l'utilisation de l'urlopen. Comme il est inclus dans la bibliothèque standard de Python 3, votre code peut s'exécuter sur n'importe quelle machine exécutant Python 3 sans préinstaller le site-package

import urllib.request
url_request = urllib.request.Request(url, headers=headers)
url_connect = urllib.request.urlopen(url_request)

#remember to open file in bytes mode
with open(filename, 'wb') as f:
    while True:
        buffer = url_connect.read(buffer_size)
        if not buffer: break

        #an integer value of size of written data
        data_wrote = f.write(buffer)

#you could probably use with-open-as manner
url_connect.close()

Cette réponse fournit une solution à HTTP 403 Interdit lors du téléchargement de fichiers via http à l'aide de Python. J'ai essayé uniquement les requêtes et les modules urllib, l'autre module peut fournir quelque chose de mieux, mais c'est celui que j'ai utilisé pour résoudre la plupart des problèmes.

Sphynx-HenryAY
la source
0

Réponse tardive, mais pour python>=3.6vous pouvez utiliser:

import dload
dload.save(url)

Installer dloadavec:

pip3 install dload
CONvid19
la source
0

Je voulais télécharger tous les fichiers à partir d'une page Web. J'ai essayé wgetmais cela échouait donc j'ai décidé pour la route Python et j'ai trouvé ce fil.

Après l'avoir lu, j'ai fait une petite application en ligne de commande soupget, développant les excellentes réponses de PabloG et Stan et ajoutant quelques options utiles.

Il utilise BeatifulSoup pour collecter toutes les URL de la page, puis télécharger celles avec les extensions souhaitées. Enfin, il peut télécharger plusieurs fichiers en parallèle.

C'est ici:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import (division, absolute_import, print_function, unicode_literals)
import sys, os, argparse
from bs4 import BeautifulSoup

# --- insert Stan's script here ---
# if sys.version_info >= (3,): 
#...
#...
# def download_file(url, dest=None): 
#...
#...

# --- new stuff ---
def collect_all_url(page_url, extensions):
    """
    Recovers all links in page_url checking for all the desired extensions
    """
    conn = urllib2.urlopen(page_url)
    html = conn.read()
    soup = BeautifulSoup(html, 'lxml')
    links = soup.find_all('a')

    results = []    
    for tag in links:
        link = tag.get('href', None)
        if link is not None: 
            for e in extensions:
                if e in link:
                    # Fallback for badly defined links
                    # checks for missing scheme or netloc
                    if bool(urlparse.urlparse(link).scheme) and bool(urlparse.urlparse(link).netloc):
                        results.append(link)
                    else:
                        new_url=urlparse.urljoin(page_url,link)                        
                        results.append(new_url)
    return results

if __name__ == "__main__":  # Only run if this file is called directly
    # Command line arguments
    parser = argparse.ArgumentParser(
        description='Download all files from a webpage.')
    parser.add_argument(
        '-u', '--url', 
        help='Page url to request')
    parser.add_argument(
        '-e', '--ext', 
        nargs='+',
        help='Extension(s) to find')    
    parser.add_argument(
        '-d', '--dest', 
        default=None,
        help='Destination where to save the files')
    parser.add_argument(
        '-p', '--par', 
        action='store_true', default=False, 
        help="Turns on parallel download")
    args = parser.parse_args()

    # Recover files to download
    all_links = collect_all_url(args.url, args.ext)

    # Download
    if not args.par:
        for l in all_links:
            try:
                filename = download_file(l, args.dest)
                print(l)
            except Exception as e:
                print("Error while downloading: {}".format(e))
    else:
        from multiprocessing.pool import ThreadPool
        results = ThreadPool(10).imap_unordered(
            lambda x: download_file(x, args.dest), all_links)
        for p in results:
            print(p)

Un exemple de son utilisation est:

python3 soupget.py -p -e <list of extensions> -d <destination_folder> -u <target_webpage>

Et un exemple réel si vous voulez le voir en action:

python3 soupget.py -p -e .xlsx .pdf .csv -u https://healthdata.gov/dataset/chemicals-cosmetics
gibbone
la source