Scaricare immagini e altri file dal web in Python (individualmente o in batch)

Attività commerciale

Quanto segue spiega come specificare l'URL di un'immagine, ZIP, PDF o altro file sul web in Python, scaricarlo e salvarlo come file locale.

  • Scarica le immagini specificando l'URL.
    • Esempio di codice
    • urllib.request.urlopen():Aprire l'URL
    • open():Scrivere su un file in modalità binaria
    • Un esempio di codice più semplice
  • Scaricare file ZIP, PDF, ecc.
  • Estrarre l'URL dell'immagine sulla pagina web.
    • Se il numero è sequenziale
    • Estratto con bella zuppa
  • Scarica in batch più immagini da un elenco di URL

Scarica le immagini specificando l'URL.

Puoi usare la libreria standard solo per scaricare singoli file specificando i loro URL; non è richiesta alcuna installazione aggiuntiva.

Esempio di codice

Quello che segue è un esempio di una funzione che scarica e salva un file specificando l'URL e il percorso di destinazione, e il suo utilizzo. Questo codice è un po' prolisso per amore della spiegazione. Un semplice esempio è dato di seguito.

import os
import pprint
import time
import urllib.error
import urllib.request

def download_file(url, dst_path):
    try:
        with urllib.request.urlopen(url) as web_file:
            data = web_file.read()
            with open(dst_path, mode='wb') as local_file:
                local_file.write(data)
    except urllib.error.URLError as e:
        print(e)
url = 'https://www.python.org/static/img/python-logo.png'
dst_path = 'data/temp/py-logo.png'
download_file(url, dst_path)

Per specificare la directory di destinazione e salvare il file con il nome del file URL, fare come segue

def download_file_to_dir(url, dst_dir):
    download_file(url, os.path.join(dst_dir, os.path.basename(url)))

dst_dir = 'data/temp'
download_file_to_dir(url, dst_dir)

Estrae il nome del file dall'URL con os.path.basename() e lo unisce alla directory specificata con os.path.join() per generare il percorso di destinazione.

Le sezioni seguenti descrivono la parte dell'acquisizione dei dati e la parte del salvataggio dei dati come file.

urllib.request.urlopen(): Aprire l'URL

Usate urllib.request.urlopen() per aprire l'URL e recuperare i dati. Si noti che urllib.urlopen() è stato deprecato in Python 2.6 e precedenti. urllib.request.urlretrieve() non è stato ancora deprecato, ma potrebbe esserlo in futuro.

Per evitare di fermarsi quando si verifica un'eccezione, catturate l'errore con try ed except.

Nell'esempio, urllib.error è importato e solo urllib.error.URLError è catturato esplicitamente. Il messaggio di errore viene visualizzato quando l'URL del file non esiste.

url_error = 'https://www.python.org/static/img/python-logo_xxx.png'
download_file_to_dir(url_error, dst_dir)
# HTTP Error 404: Not Found

Se volete anche catturare le eccezioni (FileNotFoundError, ecc.) quando salvate localmente, fate come segue.
(urllib.error.URLError, FileNotFoundError)

È anche possibile utilizzare la libreria di terze parti Requests invece della libreria standard urllib per aprire l'url e ottenere i dati.

Scrivere su un file in modalità binaria in open()

I dati che possono essere ottenuti con urllib.request.urlopen() sono una stringa di byte (tipo bytes).

Open() con mode='wb' come secondo argomento scrive i dati come binario. w significa scrivere e b significa binario.

Un esempio di codice più semplice

Le dichiarazioni annidate con possono essere scritte in una volta sola, separate da virgole.

Usando questo, possiamo scrivere quanto segue.

def download_file(url, dst_path):
    try:
        with urllib.request.urlopen(url) as web_file, open(dst_path, 'wb') as local_file:
            local_file.write(web_file.read())
    except urllib.error.URLError as e:
        print(e)

Scaricare file ZIP, PDF, ecc.

Gli esempi finora sono per il download e il salvataggio di file immagine, ma dato che stiamo semplicemente aprendo un file sul web e salvandolo come file locale, le stesse funzioni possono essere usate per altri tipi di file.

È possibile scaricare e salvare i file specificando l'URL.

url_zip = 'https://from-locas.com/sample_header.csv.zip'
download_file_to_dir(url_zip, dst_dir)

url_xlsx = 'https://from-locas/sample.xlsx'
download_file_to_dir(url_xlsx, dst_dir)

url_pdf = 'https://from-locas/sample1.pdf'
download_file_to_dir(url_pdf, dst_dir)

Si noti che l'URL specificato in questa funzione deve essere un collegamento al file stesso.

Per esempio, nel caso di un file del repository di GitHub, il seguente URL ha un'estensione pdf ma è in realtà una pagina html. Se questo URL è specificato nella funzione di cui sopra, il sorgente html sarà scaricato.

  • https://github.com/from-locals/python-snippets/blob/master/notebook/data/src/pdf/sample1.pdf

Il collegamento all'entità file è il seguente URL, che devi specificare se vuoi scaricare e salvare il file.

  • https://github.com/from-locals/python-snippets/raw/master/notebook/data/src/pdf/sample1.pdf

Ci sono anche casi in cui l'accesso è limitato dall'agente utente, dal referrer, ecc, rendendo impossibile il download. Non garantiamo che tutti i file vengano scaricati.

È facile usare Requests per cambiare o aggiungere intestazioni di richiesta come l'agente utente.

Estrarre l'URL dell'immagine sulla pagina web.

Per scaricare tutte le immagini di una pagina in una volta sola, estrai prima gli URL delle immagini e crea una lista.

Se il numero è sequenziale

Se l'URL dell'immagine che volete scaricare è un semplice numero sequenziale, è facile. Se gli URL non sono solo numeri sequenziali ma hanno anche una certa regolarità, è più facile fare una lista di URL secondo le regole piuttosto che raschiare con Beautiful Soup (vedi sotto).

Usare la notazione di comprensione della lista.

url_list = ['https://example.com/basedir/base_{:03}.jpg'.format(i) for i in range(5)]
pprint.pprint(url_list)
# ['https://example.com/basedir/base_000.jpg',
#  'https://example.com/basedir/base_001.jpg',
#  'https://example.com/basedir/base_002.jpg',
#  'https://example.com/basedir/base_003.jpg',
#  'https://example.com/basedir/base_004.jpg']

Nell'esempio precedente, {:03} è usato per un numero sequenziale di 3 cifre riempito con lo zero; {} è usato quando il riempimento con lo zero non è necessario, e {:05} è usato per un numero di 5 cifre invece di 3 cifre. Per maggiori informazioni sul metodo format di string str, vedi il seguente articolo.

Inoltre, qui stiamo usando pprint per rendere l'output più facile da leggere.

Estratto con bella zuppa

Per estrarre gli URL delle immagini dalle pagine web in massa, usate Beautiful Soup.

import os
import time
import urllib.error
import urllib.request

from bs4 import BeautifulSoup

url = 'https://it.from-locals.com/'
ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) '\
     'AppleWebKit/537.36 (KHTML, like Gecko) '\
     'Chrome/55.0.2883.95 Safari/537.36 '

req = urllib.request.Request(url, headers={'User-Agent': ua})
html = urllib.request.urlopen(req)

soup = BeautifulSoup(html, "html.parser")

url_list = [img.get('data-src') for img in soup.find(class_='list').find_all('img')]

Nell'esempio, viene estratto l'URL dell'immagine in miniatura di questo sito web.

La struttura varia a seconda della pagina web, ma fondamentalmente si ottiene come segue.

  • Ottiene un elenco di oggetti tag <img> specificando la classe, l'id, ecc. del blocco contenente le immagini multiple che si desidera scaricare.
    • soup.find(class_='list').find_all('img')
  • Ottenere l'URL dell'immagine dall'elemento src o data-src del tag <img>.
    • img.get('data-src')

Il codice di esempio qui sopra è solo un esempio e non è garantito che funzioni.

Scarica in batch più immagini da un elenco di URL

Se avete una lista di URL, potete semplicemente girarla in un ciclo for e chiamare la funzione per scaricare e salvare il file con il primo URL mostrato. A causa della lista temporanea di URL, la chiamata alla funzione download_image_dir() è commentata qui.

download_dir = 'data/temp'
sleep_time_sec = 1

for url in url_list:
    print(url)
#     download_file_dir(url, download_dir)
    time.sleep(sleep_time_sec)
# https://example.com/basedir/base_000.jpg
# https://example.com/basedir/base_001.jpg
# https://example.com/basedir/base_002.jpg
# https://example.com/basedir/base_003.jpg
# https://example.com/basedir/base_004.jpg

Per non sovraccaricare il server, uso time.sleep() per creare un tempo di attesa per ogni download di immagini. L'unità è in secondi, quindi nell'esempio sopra, il modulo time è importato e usato.

L'esempio è per i file immagine, ma anche altri tipi di file possono essere scaricati insieme, purché siano elencati.

Copied title and URL