Jak mogę pobrać plik przez HTTP za pomocą Pythona?

874

Mam małe narzędzie, którego używam do pobierania pliku MP3 ze strony internetowej zgodnie z harmonogramem, a następnie buduję / aktualizuje plik XML podcastu, który dodałem do iTunes.

Przetwarzanie tekstu, które tworzy / aktualizuje plik XML, jest napisane w języku Python. Jednak używam wget wewnątrz .batpliku Windows , aby pobrać rzeczywisty plik MP3. Wolałbym mieć całe narzędzie napisane w Pythonie.

Z trudem znalazłem sposób na pobranie pliku w Pythonie, dlatego też zdecydowałem się go użyć wget.

Jak mogę pobrać plik za pomocą Pythona?

piekarnik
źródło
Wiele z poniższych odpowiedzi nie jest zadowalającym zamiennikiem wget. Między innymi wget(1) zachowuje znaczniki czasu (2) automatycznie określa nazwę pliku z adresu URL, dołącza .1(itp.), Jeśli plik już istnieje (3), ma wiele innych opcji, z których niektóre możesz umieścić w swoim .wgetrc. Jeśli chcesz którekolwiek z nich, musisz je zaimplementować w Pythonie, ale łatwiej jest wywołać je wgetz Pythona.
ShreevatsaR
2
Krótkie rozwiązanie dla Pythona 3:import urllib.request; s = urllib.request.urlopen('http://example.com/').read().decode()
Basj

Odpowiedzi:

450

W Pythonie 2 użyj urllib2, który jest dostarczany ze standardową biblioteką.

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

Jest to najbardziej podstawowy sposób korzystania z biblioteki, pomijając obsługę błędów. Możesz także wykonywać bardziej złożone czynności, takie jak zmiana nagłówków. Dokumentację można znaleźć tutaj.

Corey
źródło
11
To nie zadziała, jeśli w podanym adresie URL będą znajdować się spacje. W takim przypadku musisz przeanalizować adres URL i urlencode ścieżkę.
Jason Sundram
91
Oto rozwiązanie Python 3: stackoverflow.com/questions/7243750/...
tommy.carstensen
6
Tylko w celach informacyjnych. Sposób na urlencode ścieżki jesturllib2.quote
André Puel
11
@JasonSundram: Jeśli są w nim spacje, nie jest to identyfikator URI.
Zaz
1
Nie działa to w systemie Windows z większymi plikami. Musisz przeczytać wszystkie bloki!
Avia,
1115

Jeszcze jedno, używając urlretrieve:

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

(do użytku w Python 3+ import urllib.requesti urllib.request.urlretrieve)

Jeszcze jeden, z „paskiem postępu”

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
źródło
1
O dziwo, zadziałało to w systemie Windows, gdy metoda urllib2 nie zadziałała. Jednak metoda urllib2 działała na komputerach Mac.
InFreefall
6
Błąd: file_size_dl + = block_sz powinien być + = len (bufor), ponieważ ostatni odczyt często nie jest pełnym block_sz. Również w systemie Windows musisz otworzyć plik wyjściowy jako „wb”, jeśli nie jest to plik tekstowy.
Bakłażan Jeff
1
Ja też urllib i urllib2 nie działał, ale urlretrieve działa dobrze, był sfrustrowany - Dzięki :)
funk-Shun
2
Zawiń całą rzecz (oprócz definicji nazwa_pliku), if not os.path.isfile(file_name):aby uniknąć nadpisywania podcastów! przydatne, gdy jest uruchamiany jako kronika z adresami URL znajdującymi się w pliku .html
Sriram Murali
2
@PabloG to teraz nieco więcej niż 31 głosów;) W każdym razie pasek stanu był fajny, więc daję +1
Cinder
340

W 2012 r. Użyj biblioteki żądań python

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

Możesz pobiec, pip install requestsaby go zdobyć.

Żądania mają wiele zalet w porównaniu z alternatywami, ponieważ interfejs API jest znacznie prostszy. Jest to szczególnie prawdziwe, jeśli musisz dokonać uwierzytelnienia. Urllib i urllib2 są w tym przypadku dość nieintuicyjne i bolesne.


30.12.2015

Ludzie wyrazili podziw dla paska postępu. Jasne, na pewno. Obecnie dostępnych jest kilka gotowych rozwiązań, w tym 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)

Zasadniczo jest to implementacja @kvance opisana 30 miesięcy temu.

Hughdbrown
źródło
jak zapisać lub wyodrębnić, jeśli plik zip jest w rzeczywistości folderem z wieloma plikami?
Abdul Muneer
6
Jak radzi sobie z dużymi plikami, czy wszystko jest przechowywane w pamięci lub czy można to zapisać do pliku bez dużego zapotrzebowania na pamięć?
bibstha,
8
Możliwe jest przesyłanie strumieniowe dużych plików, ustawiając stream = True w żądaniu. Następnie można wywołać iter_content () w odpowiedzi, aby odczytać porcję na raz.
kvance
7
Dlaczego biblioteka url miałaby mieć funkcję rozpakowywania plików? Przeczytaj plik z adresu URL, zapisz go, a następnie rozpakuj w dowolny sposób, w jaki płynie twoja łódź. Również plik zip nie jest „folderem”, tak jak pokazuje się w systemie Windows, jest plikiem.
Harel
2
@Ali:: r.textDla treści tekstowej lub Unicode. Zwrócono jako Unicode. r.content: W przypadku treści binarnych. Zwrócone jako bajty. Przeczytaj o tym tutaj: 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())

Plik wbin open('test.mp3','wb')otwiera plik (i usuwa każdy istniejący plik) w trybie binarnym, dzięki czemu można zapisywać z nim dane zamiast samego tekstu.

Dotacja
źródło
30
Wadą tego rozwiązania jest to, że cały plik jest ładowany do pamięci RAM przed zapisaniem na dysku, tylko o czym należy pamiętać, jeśli używasz go do dużych plików w małym systemie, takim jak router z ograniczonym pamięci RAM.
tripplet
2
@tripplet, więc jak to naprawimy?
Lucas Henrique
11
Aby uniknąć wczytywania całego pliku do pamięci, spróbuj przekazać argument, file.readktóry jest liczbą bajtów do odczytania. Zobacz: gist.github.com/hughdbrown/c145b8385a2afa6570e2
hughdbrown
@hughdbrown Uważam, że skrypt jest przydatny, ale mam jedno pytanie: czy mogę użyć pliku do przetwarzania końcowego? załóżmy, że pobieram plik jpg, który chcę przetworzyć za pomocą OpenCV, czy mogę używać zmiennej „data”, aby nadal działać? czy muszę go ponownie przeczytać z pobranego pliku?
Rodrigo E. Principe
5
Użyj shutil.copyfileobj(mp3file, output)zamiast tego.
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')

    Uwaga: zgodnie z dokumentacją urllib.request.urlretrievejest „starszym interfejsem” i „może stać się przestarzały w przyszłości” (dzięki gerrit )

Python 2

  • urllib2.urlopen(dzięki Corey )

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

    import urllib
    urllib.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')
bmaupin
źródło
2
Zajęło to trochę czasu, ale w końcu jest łatwy, prosty interfejs API, którego oczekuję od stdlib python :)
ThorSummoner
Bardzo ładna odpowiedź na python3, patrz także docs.python.org/3/library/…
Edouard Thiel,
@EdouardThiel Jeśli klikniesz na urllib.request.urlretrievepowyższe, przeniesie Cię do tego dokładnego linku. Twoje zdrowie!
bmaupin,
2
urllib.request.urlretrievejest udokumentowany jako „starszy interfejs” i „może stać się przestarzały w przyszłości”.
gerrit
@gerrit Dodałem notatkę, dziękuję za zgłoszenie się!
bmaupin
32

użyj modułu wget:

import wget
wget.download('url')
Sara Santana
źródło
21

Ulepszona wersja kodu PabloG dla Pythona 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
źródło
Usunąłbym nawiasy z pierwszego wiersza, ponieważ nie jest to zbyt stara funkcja.
Arpad Horvath
21

Prosty, ale Python 2 & Python 3kompatybilny sposób pochodzi z sixbiblioteki:

from six.moves import urllib
urllib.request.urlretrieve("http://www.example.com/songs/mp3.mp3", "mp3.mp3")
Akif
źródło
1
Jest to najlepszy sposób na osiągnięcie zgodności 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
źródło
17

W tym celu napisałem bibliotekę wget w czystym języku Python. Jest urlretrievewyposażony w te funkcje od wersji 2.0.

anatoly techtonik
źródło
3
Brak opcji zapisywania z niestandardową nazwą pliku?
Alex
2
@Alex dodał opcję -o FILENAME do wersji 2.1
anatoly techtonik
Pasek postępu nie pojawia się, gdy korzystam z tego modułu w Cygwin.
Joe Coder,
Powinieneś zmienić z -ona, -Oaby uniknąć nieporozumień, tak jak jest to w GNU wget. Lub przynajmniej obie opcje powinny być prawidłowe.
erik
@ eric Nie jestem pewien, czy chcę dokonać wget.pyprawdziwej zamiany w miejscuwget . -oJuż zachowuje się inaczej - jest to zgodne z curltym sposób. Czy informacja w dokumentacji pomogłaby rozwiązać problem? Czy jest to niezbędna cecha narzędzia o takiej nazwie, aby było kompatybilne z wierszem poleceń?
anatoly techtonik
15

Oto najczęściej używane wezwania do pobierania plików w Pythonie:

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

  2. urllib2.urlopen('url_to_file')

  3. requests.get(url)

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

Uwaga: urlopena urlretrieveokaże się wykonać stosunkowo źle z pobieraniem dużych plików (rozmiar> 500 MB). requests.getprzechowuje plik w pamięci do momentu zakończenia pobierania.

Jaydev
źródło
14

Zgadzam się z Coreyem, urllib2 jest bardziej kompletny niż urllib i prawdopodobnie powinien być modułem używanym, jeśli chcesz robić bardziej złożone rzeczy, ale aby uzupełnić odpowiedzi, urllib jest prostszym modułem, jeśli potrzebujesz tylko podstaw:

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

Będzie działać dobrze. Lub, jeśli nie chcesz zajmować się obiektem „response”, możesz bezpośrednio wywołać read () :

import urllib
mp3 = urllib.urlopen('http://www.example.com/sound.mp3').read()
akdom
źródło
10

W python3 możesz używać bibliotek urllib3 i shutil. Pobierz je przy użyciu pip lub pip3 (w zależności od tego, czy python3 jest domyślny, czy nie)

pip3 install urllib3 shutil

Następnie uruchom ten kod

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)

Pamiętaj, że pobierasz, urllib3ale używasz urllibkodu

Apoorv Agarwal
źródło
7

Możesz również uzyskać informację zwrotną o postępach za pomocą 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
źródło
7

Jeśli masz zainstalowany wget, możesz użyć równoległego synchronizacji.

pip install równoległa_synchronizacja

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)

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

To jest dość potężne. Może pobierać pliki równolegle, próbować w razie awarii, a nawet pobierać pliki na zdalnym komputerze.

max
źródło
Uwaga: dotyczy to tylko Linuksa
jjj
4

Jeśli prędkość ma dla ciebie znaczenie, zrobiłem mały test wydajności dla modułów urllibi wget, i odnośniewget Próbowałem raz pasku stanu, a raz bez. Wziąłem trzy różne pliki 500 MB do przetestowania (różne pliki - aby wyeliminować ryzyko buforowania pod maską). Testowany na maszynie debian z python2.

Po pierwsze, są to wyniki (są podobne w różnych seriach):

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

Sposób, w jaki przeprowadziłem test, to użycie dekoratora „profilowego”. To jest pełny kod:

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 wydaje się być najszybszy

Omer Dagan
źródło
Musi być coś strasznego pod maską, żeby pasek tak bardzo wydłużył czas.
Alistair Carscadden,
4

Ze względu na kompletność można również wywołać dowolny program do pobierania plików za pomocą subprocesspakietu. Programy dedykowane do pobierania plików są bardziej wydajne niż funkcje Pythona urlretrieve. Na przykład, wgetmoże pobierać katalogi rekurencyjnie ( -R), radzić sobie z FTP, przekierowaniami, serwerami proxy HTTP, może unikać ponownego pobierania istniejących plików ( -nc) i aria2może pobierać wiele połączeń, co może potencjalnie przyspieszyć pobieranie.

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

W Jupyter Notebook można również wywoływać programy bezpośrednio za pomocą !składni:

!wget -O example_output_file.html https://example.com
Robin Dinse
źródło
3

Kod źródłowy może być:

import urllib
sock = urllib.urlopen("http://diveintopython.org/")
htmlSource = sock.read()                            
sock.close()                                        
print htmlSource  
Olu Smith
źródło
3

Możesz używać PycURL na Pythonie 2 i 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()
gzeron
źródło
2

Napisałem następujące, które działają w waniliowym Pythonie 2 lub Pythonie 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()

Uwagi:

  • Obsługuje funkcję zwrotną „paska postępu”.
  • Plik do pobrania to 4 MB testowy plik .zip z mojej witryny.
imallett
źródło
działa świetnie, uruchom go przez jupyter dostałem to, czego chcę :-)
Samir Ouldsaadi
1

To może być trochę późno, ale widziałem kod pabloG i nie mogłem się powstrzymać od dodania systemu os.system ('cls'), aby wyglądał NIESAMOWICIE! Sprawdź to :

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

Jeśli działasz w środowisku innym niż Windows, będziesz musiał użyć czegoś innego niż „cls”. W systemach MAC OS X i Linux powinno być „czyste”.

JD3
źródło
3
clsnie robi nic na moim OS X ani na moim serwerze Ubuntu. Pewne wyjaśnienie może być dobre.
kqw
Myślę, że powinieneś użyć clearLinuksa, a nawet lepiej zastąpić linię drukowania zamiast wyczyścić całą linię poleceń.
Arijoon,
4
ta odpowiedź po prostu kopiuje inną odpowiedź i dodaje wywołanie przestarzałej funkcji ( os.system()), która uruchamia podproces w celu wyczyszczenia ekranu za pomocą polecenia specyficznego dla platformy ( cls). Jak to ma jakieś pozytywne opinie? Zupełnie bezwartościowa „odpowiedź” IMHO.
Corey Goldberg,
1

urlretrieve i requests.get są proste, ale w rzeczywistości nie. Pobrałem dane dla kilku witryn, w tym tekst i obrazy, powyższe dwa prawdopodobnie rozwiązują większość zadań. ale dla bardziej uniwersalnego rozwiązania sugeruję skorzystanie z urlopu. Ponieważ jest zawarty w standardowej bibliotece Python 3, Twój kod może działać na dowolnej maszynie, na której działa Python 3 bez wstępnej instalacji pakietu witryny

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

Ta odpowiedź stanowi rozwiązanie dla HTTP 403 Zabronione podczas pobierania pliku przez HTTP za pomocą Pythona. Próbowałem tylko żądań i modułów urllib, drugi moduł może zapewnić coś lepszego, ale tego właśnie użyłem do rozwiązania większości problemów.

Sphynx-HenryAY
źródło
0

Późna odpowiedź, ale python>=3.6możesz użyć:

import dload
dload.save(url)

Zainstaluj za dloadpomocą:

pip3 install dload
CONvid19
źródło
0

Chciałem pobrać wszystkie pliki ze strony internetowej. Próbowałem, wgetale nie udało się, więc zdecydowałem się na trasę w języku Python i znalazłem ten wątek.

Po przeczytaniu napisałem małą aplikację z linii poleceń soupget, rozwijając doskonałe odpowiedzi PabloG i Stana oraz dodając kilka przydatnych opcji.

Korzysta z BeatifulSoup aby zebrać wszystkie adresy URL strony, a następnie pobrać te z pożądanymi rozszerzeniami. Wreszcie może pobierać wiele plików równolegle.

Oto on:

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

Przykładem jego użycia jest:

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

I prawdziwy przykład, jeśli chcesz zobaczyć to w akcji:

python3 soupget.py -p -e .xlsx .pdf .csv -u https://healthdata.gov/dataset/chemicals-cosmetics
gibon
źródło