Como faço para baixar um arquivo via HTTP usando Python?

875

Eu tenho um pequeno utilitário que eu uso para baixar um arquivo MP3 de um site em um cronograma e, em seguida, cria / atualiza um arquivo XML de podcast que eu adicionei ao iTunes.

O processamento de texto que cria / atualiza o arquivo XML é escrito em Python. No entanto, eu uso o wget dentro de um .batarquivo do Windows para baixar o arquivo MP3 real. Eu preferiria ter todo o utilitário escrito em Python.

Eu lutei para encontrar uma maneira de realmente fazer o download do arquivo em Python, e por isso comecei a usar wget.

Então, como faço para baixar o arquivo usando Python?

Owen
fonte
Muitas das respostas abaixo não são um substituto satisfatório para wget. Entre outras coisas, wget(1) preserva os carimbos de data / hora (2) determina automaticamente o nome do arquivo do URL, acrescentando .1(etc.) se o arquivo já existe (3) tem muitas outras opções, algumas das quais você pode ter colocado no seu .wgetrc. Se você quiser alguma dessas, precisará implementá-las pessoalmente em Python, mas é mais simples invocar a wgetpartir do Python.
ShreevatsaR
2
Solução curta para Python 3:import urllib.request; s = urllib.request.urlopen('http://example.com/').read().decode()
Basj

Respostas:

450

No Python 2, use urllib2, que vem com a biblioteca padrão.

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

Essa é a maneira mais básica de usar a biblioteca, menos qualquer manipulação de erro. Você também pode fazer coisas mais complexas, como alterar cabeçalhos. A documentação pode ser encontrada aqui.

Corey
fonte
11
Isso não funcionará se houver espaços no URL que você fornecer. Nesse caso, você precisará analisar o URL e codificar o caminho.
Jason Sundram
91
Aqui está a solução Python 3: stackoverflow.com/questions/7243750/…
tommy.carstensen
6
Somente para referência. O caminho para codificar o caminho éurllib2.quote
André Puel 02/08
11
@ JasonSundram: Se houver espaços nele, não será um URI.
Zaz
1
Isso não funciona no Windows com arquivos maiores. Você precisa ler todos os blocos!
Avia
1115

Mais um, usando urlretrieve:

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

(para uso em Python 3+ import urllib.requeste urllib.request.urlretrieve)

Mais um, com uma "barra de progresso"

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
fonte
1
Curiosamente, isso funcionou para mim no Windows quando o método urllib2 não. O método urllib2 funcionou no Mac, no entanto.
InFreefall
6
Bug: file_size_dl + = block_sz deve ser + = len (buffer), pois a última leitura geralmente não é um block_sz completo. Também no Windows, você precisa abrir o arquivo de saída como "wb" se não for um arquivo de texto.
Berinjela Jeff
1
Eu também urllib e urllib2 não funcionou, mas urlretrieve funcionou bem, estava ficando frustrado - graças :)
funk-shun
2
Envolva tudo (exceto a definição de nome_do_arquivo) if not os.path.isfile(file_name):para evitar a substituição de podcasts! útil quando executá-lo como um cron com as URLs encontrados em um arquivo .html
Sriram Murali
2
@PabloG é um pouquinho mais do que 31 votos agora;) De qualquer forma, a barra de status foi divertida, então eu vou marcar com +1 #
Cinder
340

Em 2012, use a biblioteca de solicitações python

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

Você pode correr pip install requestspara obtê-lo.

Os pedidos têm muitas vantagens sobre as alternativas, porque a API é muito mais simples. Isto é especialmente verdade se você precisar fazer autenticação. urllib e urllib2 são bastante pouco intuitivos e dolorosos nesse caso.


30-12-2015

As pessoas expressaram admiração pela barra de progresso. É legal, com certeza. Agora existem várias soluções prontas para uso, incluindo 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)

Essa é essencialmente a implementação @kvance descrita há 30 meses.

hughdbrown
fonte
como faço para salvar ou extrair se o arquivo zip é realmente uma pasta com muitos arquivos?
Abdul Muneer
6
Como isso lida com arquivos grandes, tudo é armazenado na memória ou isso pode ser gravado em um arquivo sem necessidade de grande memória?
Bibstha
8
É possível transmitir arquivos grandes configurando stream = True na solicitação. Você pode chamar iter_content () na resposta para ler um pedaço de cada vez.
kvance
7
Por que uma biblioteca de URL precisa ter um recurso de descompactação de arquivo? Leia o arquivo a partir da URL, salve-o e descompacte-o da maneira que flutuar em seu barco. Também um arquivo zip não é uma 'pasta' como mostra no Windows, é um arquivo.
Harel
2
@ Ali:: r.textPara conteúdo de texto ou unicode. Retornado como unicode. r.content: Para conteúdo binário. Retornado como bytes. Leia sobre isso aqui: 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())

O wbin open('test.mp3','wb')abre um arquivo (e apaga qualquer arquivo existente) no modo binário, para que você possa salvar dados com ele em vez de apenas texto.

Conceder
fonte
30
A desvantagem desta solução é que o arquivo inteiro é carregado no RAM antes de ser salvo no disco, apenas algo a ter em mente se for usado para arquivos grandes em um sistema pequeno, como um roteador com RAM limitado.
Tripplet 18/11/2012
2
@ tripplet, então como resolveríamos isso?
Lucas Henrique
11
Para evitar a leitura do arquivo inteiro na memória, tente passar um argumento para file.readesse é o número de bytes a serem lidos. Veja: gist.github.com/hughdbrown/c145b8385a2afa6570e2
hughdbrown
@hughdbrown Achei o seu script útil, mas tenho uma pergunta: posso usar o arquivo para pós-processamento? suponha que eu baixe um arquivo jpg que desejo processar com o OpenCV, posso usar a variável 'data' para continuar trabalhando? ou tenho que lê-lo novamente a partir do arquivo baixado?
Rodrigo E. Principe
5
Use em shutil.copyfileobj(mp3file, output)vez disso.
Aurélien Ooms
129

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

    Nota: De acordo com a documentação, urllib.request.urlretrieveé uma "interface herdada" e "pode ​​ficar obsoleta no futuro" (obrigado gerrit )

Python 2

  • urllib2.urlopen(obrigado Corey )

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

    import urllib
    urllib.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')
bmaupin
fonte
2
Com certeza Demorou um pouco, mas há, finalmente, é a api simples fácil eu esperaria de um stdlib python :)
ThorSummoner
Resposta muito boa para python3, consulte também docs.python.org/3/library/…
Edouard Thiel
@ EdouardThiel Se você clicar urllib.request.urlretrieveacima, ele o levará ao link exato. Felicidades!
bmaupin
2
urllib.request.urlretrieveestá documentado como uma "interface herdada" e "pode ​​ficar obsoleto no futuro".
gerrit 27/03
@gerrit Adicionei uma nota, obrigado pelo aviso!
bmaupin 30/03
32

use o módulo wget:

import wget
wget.download('url')
Sara Santana
fonte
21

Uma versão aprimorada do código PabloG para 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
fonte
Eu removeria os parênteses da primeira linha, porque não é um recurso muito antigo.
Arpad Horvath
21

A Python 2 & Python 3maneira simples, porém compatível, vem com a sixbiblioteca:

from six.moves import urllib
urllib.request.urlretrieve("http://www.example.com/songs/mp3.mp3", "mp3.mp3")
Akif
fonte
1
Essa é a melhor maneira de fazer isso para compatibilidade com 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
fonte
17

Escreveu a biblioteca wget em Python puro apenas para esse fim. É aprimorado urlretrievecom esses recursos a partir da versão 2.0.

anatoly techtonik
fonte
3
Não há opção para salvar com nome de arquivo personalizado?
21414 Alex
2
@Alex adicionou a opção -o FILENAME à versão 2.1
anatoly techtonik
A barra de progresso não aparece quando eu uso este módulo no Cygwin.
Joe Coder 6/15
Você deve mudar de -opara -Opara evitar confusão, como no GNU wget. Ou pelo menos as duas opções devem ser válidas.
Erik
@eric Não tenho certeza se quero fazer wget.pyuma substituição no local de verdade wget. O -ojá se comporta de maneira diferente - é compatível com curlesse caminho. Uma observação na documentação ajudaria a resolver o problema? Ou é o recurso essencial para que um utilitário com esse nome seja compatível com a linha de comando?
Anatoly techtonik
16

A seguir, são apresentadas as chamadas mais usadas para baixar arquivos em 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)

Nota: urlopene urlretrievesão encontrados para desempenho relativamente ruim com o download de arquivos grandes (tamanho> 500 MB). requests.getarmazena o arquivo na memória até que o download seja concluído.

Jaydev
fonte
14

Eu concordo com Corey, urllib2 é mais completo do que urllib e provavelmente deve ser o módulo usado se você quiser fazer coisas mais complexas, mas para tornar as respostas mais completas, urllib é um módulo mais simples se você quiser apenas o básico:

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

Vai funcionar bem. Ou, se você não quiser lidar com o objeto "response", pode chamar read () diretamente:

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

No python3, você pode usar urllib3 e shutil libraires. Faça o download deles usando pip ou pip3 (Dependendo se python3 é o padrão ou não)

pip3 install urllib3 shutil

Em seguida, execute este código

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)

Observe que você baixa, urllib3mas usa urllibno código

Apoorv Agarwal
fonte
7

Você também pode obter o feedback de progresso com 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
fonte
7

Se você tiver o wget instalado, poderá usar o parallel_sync.

instalação do pip 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

Isso é bem poderoso. Ele pode baixar arquivos em paralelo, tentar novamente em caso de falha e até baixar arquivos em uma máquina remota.

max
fonte
Note que este é apenas para Linux
jjj
4

Se a velocidade é importante para você, fiz um pequeno teste de desempenho para os módulos urllibe wget, sobre wgetisso, tentei uma vez com barra de status e outra sem. Peguei três arquivos diferentes de 500 MB para testar (arquivos diferentes - para eliminar a chance de que haja algum armazenamento em cache oculto). Testado na máquina debian, com python2.

Primeiro, estes são os resultados (eles são semelhantes em execuções diferentes):

$ 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
==============

A maneira como realizei o teste está usando o decorador "profile". Este é o código completo:

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 parece ser o mais rápido

Omer Dagan
fonte
Deve haver algo completamente horrível acontecendo sob o capô para fazer a barra aumentar tanto o tempo.
Alistair Carscadden
4

Por uma questão de integridade, também é possível chamar qualquer programa para recuperar arquivos usando o subprocesspacote. Programas dedicados à recuperação de arquivos são mais poderosos do que as funções do Python urlretrieve. Por exemplo, você wgetpode baixar diretórios recursivamente ( -R), pode lidar com FTP, redireciona, proxies HTTP, pode evitar o download de arquivos existentes ( -nc) e aria2pode fazer downloads com várias conexões, o que potencialmente pode acelerar seus downloads.

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

No Jupyter Notebook, também é possível chamar programas diretamente com a !sintaxe:

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

O código fonte pode ser:

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

Você pode usar o PycURL no Python 2 e 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
fonte
2

Eu escrevi o seguinte, que funciona em baunilha 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()

Notas:

  • Suporta um retorno de chamada "barra de progresso".
  • Download é um .zip de teste de 4 MB do meu site.
imallett
fonte
funciona muito bem, executá-lo através jupyter tem o que eu quero :-)
Samir Ouldsaadi
1

Pode ser um pouco tarde, mas eu vi o código de pabloG e não pude deixar de adicionar um os.system ('cls') para torná-lo IMPRESSIONANTE! Confira :

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

Se estiver executando em um ambiente que não seja o Windows, você precisará usar algo diferente de 'cls'. No MAC OS X e Linux, deve ser "claro".

JD3
fonte
3
clsnão faz nada no meu OS X ou nem em um servidor Ubuntu meu. Alguns esclarecimentos podem ser bons.
Kqw # 24/14
Eu acho que você deve usar clearpara linux, ou melhor ainda, substituir a linha de impressão em vez de limpar toda a saída da linha de comando.
Arijoon 21/01
4
essa resposta apenas copia outra resposta e adiciona uma chamada a uma função preterida ( os.system()) que inicia um subprocesso para limpar a tela usando um comando específico da plataforma ( cls). Como é que isto tem qualquer upvotes ?? Absolutamente inútil "resposta" IMHO.
Corey Goldberg
1

urlretrieve e orders.get são simples, porém a realidade não. Busquei dados para sites de casal, incluindo texto e imagens, os dois acima provavelmente resolvem a maioria das tarefas. mas para uma solução mais universal, sugiro o uso de urlopen. Como está incluído na biblioteca padrão do Python 3, seu código pode ser executado em qualquer máquina que execute o Python 3 sem pré-instalar o pacote do site

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

Esta resposta fornece uma solução para o HTTP 403 Proibido ao baixar arquivos por HTTP usando Python. Eu tentei apenas solicitações e módulos urllib, o outro módulo pode oferecer algo melhor, mas esse é o que eu usei para resolver a maioria dos problemas.

Sphynx-HenryAY
fonte
0

Resposta tardia, mas para python>=3.6você poder usar:

import dload
dload.save(url)

Instale dloadcom:

pip3 install dload
CONvid19
fonte
0

Eu queria baixar todos os arquivos de uma página da web. Eu tentei, wgetmas estava falhando, então decidi pela rota Python e encontrei esse segmento.

Depois de ler, fiz um pequeno aplicativo de linha de comando soupget, expandindo as excelentes respostas de PabloG e Stan e adicionando algumas opções úteis.

Ele usa o BeatifulSoup para coletar todos os URLs da página e depois baixar os com as extensões desejadas. Finalmente, ele pode baixar vários arquivos em paralelo.

Aqui está:

#!/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)

Um exemplo de seu uso é:

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

E um exemplo real, se você quiser vê-lo em ação:

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