Descargue imágenes y otros archivos de la web en Python (individualmente o en lotes)

Negocio

A continuación se explica cómo especificar en Python la URL de una imagen, un ZIP, un PDF u otro archivo de la web, descargarlo y guardarlo como archivo local.

  • Descargue las imágenes especificando la URL.
    • Ejemplo de código
    • urllib.request.urlopen():Abrir URL
    • open():Escribir en un archivo en modo binario
    • Un ejemplo de código más sencillo
  • Descargue archivos ZIP, PDF, etc.
  • Extraer la URL de la imagen en la página web.
    • Si el número es secuencial
    • Extracto con Beautiful Soup
  • Descarga por lotes de múltiples imágenes desde una lista de URLs

Descargue las imágenes especificando la URL.

Puede utilizar la biblioteca estándar sólo para descargar archivos individuales especificando sus URL; no se requiere ninguna instalación adicional.

Ejemplo de código

El siguiente es un ejemplo de una función que descarga y guarda un archivo especificando la URL y la ruta de destino, y su uso. Este código es un poco verboso en aras de la explicación. A continuación se ofrece un ejemplo sencillo.

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)

Para especificar el directorio de destino y guardar el archivo con el nombre de archivo URL, haga lo siguiente

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)

Extrae el nombre del archivo de la URL con os.path.basename() y lo une con el directorio especificado con os.path.join() para generar la ruta de destino.

Las siguientes secciones describen la parte de la adquisición de datos y la parte del almacenamiento de datos como un archivo.

urllib.request.urlopen(): Abrir URL

Utilice urllib.request.urlopen() para abrir la URL y recuperar los datos. Tenga en cuenta que urllib.urlopen() ha sido obsoleta en Python 2.6 y anteriores. urllib.request.urlretrieve() no ha sido obsoleta todavía, pero puede serlo en el futuro.

Para evitar que se detenga cuando se produzca una excepción, atrape el error con try y except.

En el ejemplo, se importa urllib.error y sólo se captura explícitamente urllib.error.URLError. El mensaje de error se mostrará cuando la URL del archivo no exista.

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

Si quiere capturar también excepciones (FileNotFoundError, etc.) al guardar localmente, haga lo siguiente.
(urllib.error.URLError, FileNotFoundError)

También es posible utilizar la biblioteca de terceros Requests en lugar de la biblioteca estándar urllib para abrir la url y obtener los datos.

Escribir en un archivo en modo binario en open()

Los datos que se pueden obtener con urllib.request.urlopen() son una cadena de bytes (tipo bytes).

Open() con mode='wb' como segundo argumento escribe los datos como binarios. w significa escritura y b significa binario.

Un ejemplo de código más sencillo

Las declaraciones anidadas con pueden escribirse a la vez, separadas por comas.

Utilizando esto, podemos escribir lo siguiente.

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)

Descargue archivos ZIP, PDF, etc.

Los ejemplos hasta ahora son para descargar y guardar archivos de imagen, pero como simplemente estamos abriendo un archivo en la web y guardándolo como un archivo local, las mismas funciones se pueden utilizar para otros tipos de archivos.

Puedes descargar y guardar archivos especificando la 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)

Tenga en cuenta que la URL especificada en esta función debe ser un enlace al propio archivo.

Por ejemplo, en el caso de un archivo del repositorio de GitHub, la siguiente URL tiene una extensión pdf pero en realidad es una página html. Si se especifica esta URL en la función anterior, se descargará la fuente html.

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

El enlace a la entidad del archivo es la siguiente URL, que debe especificar si desea descargar y guardar el archivo.

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

También hay casos en los que el acceso está restringido por el agente de usuario, el referente, etc., lo que hace imposible la descarga. No garantizamos que se descarguen todos los archivos.

Es fácil utilizar Requests para cambiar o añadir cabeceras de solicitud como el agente de usuario.

Extraer la URL de la imagen en la página web.

Para descargar todas las imágenes de una página a la vez, primero extraiga las URL de las imágenes y cree una lista.

Si el número es secuencial

Si la URL de la imagen que se quiere descargar es un simple número secuencial, es fácil. Si las URLs no son sólo números secuenciales sino que tienen cierta regularidad, es más fácil hacer una lista de URLs según las reglas en lugar de hacer un scraping con Beautiful Soup (ver más abajo).

Utiliza la notación de comprensión de listas.

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

En el ejemplo anterior, {:03} se utiliza para un número secuencial de 3 dígitos rellenado con cero; {} se utiliza cuando no es necesario rellenar con cero, y {:05} se utiliza para un número de 5 dígitos en lugar de 3 dígitos. Para obtener más información sobre el método de formato de la cadena de caracteres, consulte el siguiente artículo.

Además, aquí utilizamos pprint para que la salida sea más fácil de leer.

Extracto con Beautiful Soup

Para extraer las URL de las imágenes de las páginas web de forma masiva, utilice Beautiful Soup.

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

from bs4 import BeautifulSoup

url = 'https://es.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')]

En el ejemplo, se extrae la URL de la imagen en miniatura de este sitio web.

La estructura varía según la página web, pero básicamente se obtiene como sigue.

  • Obtenga una lista de objetos de la etiqueta <img> especificando la clase, el id, etc. del bloque que contiene las múltiples imágenes que desea descargar.
    • soup.find(class_='list').find_all('img')
  • Obtenga la URL de la imagen del elemento src o del elemento data-src de la etiqueta <img>.
    • img.get('data-src')

El código de muestra anterior es sólo un ejemplo y no se garantiza que funcione.

Descarga por lotes de múltiples imágenes desde una lista de URLs

Si tienes una lista de URLs, puedes simplemente convertirla en un bucle for y llamar a la función para descargar y guardar el archivo con la primera URL mostrada. Debido a la lista temporal de URLs, la llamada a la función download_image_dir() se comenta aquí.

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

Para no sobrecargar el servidor, utilizo time.sleep() para crear un tiempo de espera para cada descarga de imágenes. La unidad está en segundos, por lo que en el ejemplo anterior se importa y se utiliza el módulo de tiempo.

El ejemplo es para archivos de imagen, pero también se pueden descargar juntos otros tipos de archivos, siempre que estén en la lista.

Copied title and URL