Descarregar imagens e outros ficheiros da web em Python (individualmente ou em lotes)

O negócio

O seguinte explica como especificar o URL de uma imagem, ZIP, PDF, ou outro ficheiro na Web em Python, descarregá-lo, e guardá-lo como um ficheiro local.

  • Descarregar imagens especificando o URL.
    • Exemplo de código
    • urllib.request.urlopen():Abrir URL
    • open():Escrever para um ficheiro em modo binário
    • Um exemplo de código mais simples
  • Descarregar ficheiros ZIP, ficheiros PDF, etc.
  • Extrair o URL da imagem na página web.
    • Se o número for sequencial
    • Extracto com Sopa Bonita
  • Descarregar várias imagens a partir de uma lista de URL

Descarregar imagens especificando o URL.

Pode utilizar a biblioteca padrão apenas para descarregar ficheiros individuais, especificando os seus URLs; não é necessária qualquer instalação adicional.

Exemplo de código

O seguinte é um exemplo de uma função que descarrega e guarda um ficheiro especificando o URL e o caminho de destino, e a sua utilização. Este código é um pouco verboso para fins de explicação. Um exemplo simples é dado abaixo.

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 o directório de destino e guardar o ficheiro com o nome do ficheiro URL, faça o seguinte

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)

Extrai o nome do ficheiro do URL com os.path.basename() e junta-o ao directório especificado com os.path.join() para gerar o caminho de destino.

As secções seguintes descrevem a parte da aquisição de dados e a parte da poupança de dados como um ficheiro.

urllib.request.urlopen(): Abrir URL

Use urllib.request.urlopen() para abrir o URL e recuperar os dados. Note-se que urllib.urlopen() foi depreciado em Python 2.6 e anteriores. urllib.request.urlretrieve() ainda não foi depreciado, mas poderá vir a sê-lo no futuro.

Para evitar parar quando ocorre uma excepção, apanhar o erro com tentativa e excepto.

No exemplo, urllib.error é importado e apenas urllib.error.URLError é explicitamente capturado. A mensagem de erro será exibida quando o URL do ficheiro não existir.

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 também quiser apanhar excepções (FileNotFoundError, etc.) ao salvar localmente, faça o seguinte.
(urllib.error.URLError, FileNotFoundError)

Também é possível utilizar os pedidos de biblioteca de terceiros em vez da biblioteca padrão urllib para abrir a url e obter os dados.

Escrever para um ficheiro em modo binário em aberto()

Os dados que podem ser obtidos com urllib.request.urlopen() são uma cadeia de bytes (tipo bytes).

Open() com mode='wb' como o segundo argumento escreve os dados como binário. w significa escrever e b significa binário.

Um exemplo de código mais simples

Aninhadas com declarações podem ser escritas de uma só vez, separadas por vírgulas.

Usando isto, podemos escrever o seguinte.

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)

Descarregar ficheiros ZIP, ficheiros PDF, etc.

Os exemplos até agora são para descarregar e guardar ficheiros de imagem, mas como estamos simplesmente a abrir um ficheiro na web e a guardá-lo como um ficheiro local, as mesmas funções podem ser usadas para outros tipos de ficheiros.

Pode descarregar e guardar ficheiros, especificando o 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)

Note-se que o URL especificado nesta função deve ser um link para o próprio ficheiro.

Por exemplo, no caso de um ficheiro de repositório GitHub, o seguinte URL tem uma extensão pdf mas na realidade é uma página html. Se este URL for especificado na função acima, a fonte html será descarregada.

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

O link para a entidade do ficheiro é o seguinte URL, que deve especificar se pretende descarregar e guardar o ficheiro.

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

Há também casos em que o acesso é restringido por agente do utilizador, referenciador, etc., tornando impossível o download. Não garantimos que todos os ficheiros serão descarregados.

É fácil de usar Pedidos para alterar ou adicionar cabeçalhos de pedidos, tais como agentes do utilizador.

Extrair o URL da imagem na página web.

Para descarregar todas as imagens de uma página de uma vez, primeiro extraia os URLs das imagens e crie uma lista.

Se o número for sequencial

Se o URL da imagem que deseja descarregar for um número sequencial simples, é fácil. Se os URLs não forem apenas números sequenciais mas também tiverem alguma regularidade, é mais fácil fazer uma lista de URLs de acordo com as regras do que raspar com a Beautiful Soup (ver abaixo).

Utilizar notação de compreensão de 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']

No exemplo acima, {:03} é utilizado para um número sequencial de 3 dígitos com preenchimento zero; {} é utilizado quando o preenchimento zero não é necessário, e {:05} é utilizado para um número de 5 dígitos em vez de 3 dígitos. Para mais informações sobre o método de formato da cadeia de caracteres, ver o artigo seguinte.

Além disso, aqui estamos a utilizar a impressão para facilitar a leitura dos resultados.

Extracto com Sopa Bonita

Para extrair URLs de imagens de páginas web em massa, use Beautiful Soup.

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

from bs4 import BeautifulSoup

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

No exemplo, é extraída a URL da imagem em miniatura deste website.

A estrutura varia dependendo da página web, mas basicamente é obtida da seguinte forma.

  • Obtenha uma lista de objectos <img> tag especificando a classe, id, etc. do bloco que contém as múltiplas imagens que deseja descarregar.
    • soup.find(class_='list').find_all('img')
  • Obter o URL da imagem a partir do elemento src ou elemento data-src da etiqueta <img>.
    • img.get('data-src')

O código de amostra acima é apenas um exemplo e não tem garantia de funcionar.

Descarregar várias imagens a partir de uma lista de URL

Se tiver uma lista de URLs, pode simplesmente transformá-la num loop e chamar a função para descarregar e guardar o ficheiro com o primeiro URL mostrado. Devido à lista temporária de URLs, a função chamada download_image_dir() é comentada aqui.

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

A fim de não sobrecarregar o servidor, uso time.sleep() para criar um tempo de espera para cada descarga de imagem. A unidade é em segundos, pelo que no exemplo acima, o módulo de tempo é importado e utilizado.

O exemplo é para ficheiros de imagem, mas outros tipos de ficheiros também podem ser descarregados em conjunto, desde que estejam listados.

Copied title and URL