Introdução
Em redes de computadores, o termo download significa receber dados de um sistema remoto, normalmente um servidor: como um servidor Web, um servidor FTP, um servidor de email ou outros sistemas similares. Isso contrasta com o upload, onde os dados são enviados para um servidor remoto.
Um download é um arquivo oferecido para download ou que foi baixado ou o processo de recebimento desse arquivo.
Download com Python
Existem diversas bibliotecas Python que nos auxiliam na tarefa de fazer download de arquivos, neste guia vamos explorar as respectivas:
Veremos exemplos de diferentes scripts e analisaremos os seus funcionamentos. Durante este processo iremos fazer o download de diversos tipos de arquivos.
A Biblioteca wget
No seguinte exemplo vamos fazer o download da arte “A Última Ceia” de Leonardo da Vinci
import wget
url = 'https://arquivos.netlify.com/SantaCeia.jpg'
wget.download(url,'santaceia.jpg')
Observe que não foi necessário muitas linhas de código, apenas importamos a biblioteca, guardamos a URL de nossa imagem em uma variável e por sua vez executamos a função download()
, salvando assim a imagem como santaceia.jpg
no mesmo diretório que o script foi executado.
A Biblioteca PycURL
No seguinte exemplo iremos fazer o download do index.html
da página web https://pythonwebscraping.netlify.com
import pycurl
with open('index.html', 'wb') as f:
curl = pycurl.Curl()
curl.setopt(curl.URL, 'https://pythonwebscraping.netlify.com/')
curl.setopt(curl.WRITEDATA, f)
curl.perform()
curl.close()
Estamos utilizando o statement with
capaz de simplificar o gerenciamento de recursos comuns, como arquivos. Neste caso específico vamos abrir o arquivo index.html
para escrita.
Iniciamos com a criação do objeto curl
, utilizamos a função setopt()
para setarmos a opção da session, e finalizamos com as funções perform()
para execução das ações e close()
para fecharmos a conexão.
Ao executarmos o script teremos o arquivo index.html
salvo no mesmo diretório de nosso script.
A Biblioteca urllib
O urllib é um pacote que contém vários módulos para trabalharmos com URLs. Um deles é o request, que nos permite abrir e ler URLs.
Neste exemplo vamos fazer o download de um arquivo MP3.
import urllib.request
file_url = 'https://arquivos.netlify.com/AnotherBrickInTheWall.mp3'
mp3 = urllib.request.urlopen(file_url)
file_size = int(mp3.length)
print(f'o arquivo possui o seguinte tamanho: {file_size}')
with open('anotherbrickinthewall.mp3', 'wb') as output:
output.write(mp3.read())
Observe que através da função urlopen()
estamos abrindo a URL do arquivo. Na variável file_size
estamos guardando o tamanho do arquivo em bytes e em seguida imprimindo seu valor. Por fim salvamos o arquivo MP3 no mesmo diretório de nosso script.
A Biblioteca HTTPX
O HTTPX é um cliente HTTP completo para Python 3, que fornece APIs síncrona e assíncrona e suporte para HTTP/1.1 e HTTP/2.
No seguinte exemplo vamos utilizar o concurrency model async capaz de nos prover significantes benefícios de perfomance.
Iremos nos conectar com dois endpoints diferentes da API do Github e salvaremos os recursos como arquivos json.
import asyncio
import httpx
import json
async def main():
async with httpx.AsyncClient() as client:
response = await client.get('https://api.github.com/gists/public')
public_gists = json.dumps(response.json(), indent=4, sort_keys=True)
with open('public_gists.json', 'w') as f:
f.write(public_gists)
async with httpx.AsyncClient() as client:
response = await client.get('https://api.github.com/emojis')
emojis = json.dumps(response.json(), indent=4, sort_keys=True)
with open('emojis.json', 'w') as f:
f.write(emojis)
asyncio.run(main())
Observe que estamos utilizando as palavra-chaves async/await
para declararmos nossas Coroutines, também estamos utilizando o AsyncClient
para que consigamos executar requisições asíncronas.
A Biblioteca Requests
Requests é uma biblioteca HTTP elegante e simples para Python, com ela é muito fácil trabalharmos com requisições HTTP.
Exemplo 1: Arquivo Zip
Neste primeiro exemplo faremos o download de um repositório github
from tqdm import tqdm
import requests
url = "https://github.com/donnemartin/data-science-ipython-notebooks/archive/master.zip"
req = requests.get(url, stream=True, headers={"Accept-Encoding": None})
total_size = int(req.headers.get("Content-Length", 0))
block_size = 1024
filename = url.split("/")[-1]
print(f"Downloading {filename}")
t = tqdm(total=total_size, unit="iB", unit_scale=True)
with open("data-science-notebooks.zip", "wb") as f:
for data in req.iter_content(block_size):
t.update(len(data))
f.write(data)
t.close()
Observe que estamos importando a biblioteca requests e também a biblioteca tqdm que nos fornece uma barra de progresso para acompanharmos a evolução de nosso download.
Em seguida estamos utilizando o método get()
para solicitar o arquivo na respectiva URL, veja que estamos usando os parâmetros stream=True
e setando o header "Accept-Enconding": None
.
Obtemos o tamanho do arquivo caso o header Content-Length
esteja disponível e também definimos o tamanho de cada bloco para 1024
. Utilizamos a função split()
para obtermos o nome do arquivo que estamos fazendo download.
Por fim executamos o download através da execução das iterações e salvamos o arquivo no mesmo diretório de nosso script.
Exemplo 2: Arquivo PDF
Neste segundo exemplo faremos o download de um arquivo pdf
from halo import Halo
import requests
import os
spinner = Halo(text='Downloading file...', spinner='dots')
spinner.start()
cwd = os.path.dirname(os.path.realpath(__file__))
url = 'https://arquivos.netlify.com/TheArtOfComputerProgramming.pdf'
file = requests.get(url, allow_redirects=True).content
with open(f'{cwd}/livro.pdf', 'wb') as pdf:
pdf.write(file)
spinner.stop()
Perceba que além da biblioteca requests, também estamos importando halo e os.
A biblioteca halo nos fornecerá uma animação de loading. Iniciamos instanciando o objeto spinner e startamos ele.
Utilizamos a biblioteca os para obtermos o caminho de nosso script para assim salvarmos o arquivo no mesmo diretório do script.
Em seguida fazemos uma requisição através da função get()
, com o parâmetro allow_redirects=True
, permitindo assim o redirecionamento.
Por fim salvamos o arquivo com o nome livro.pdf
e paramos o spinner.
Exemplo 3: Múltiplas Imagens com Paralelismo
Neste exemplo faremos o download de múltiplas imagens. Para esta tarefa contaremos com o auxílio da biblioteca multiprocessing
from multiprocessing.pool import ThreadPool
import requests
import os
cwd = os.path.dirname(os.path.realpath(__file__))
urls = [
(f"{cwd}/1.jpg","https://res.cloudinary.com/theakira/image/upload/v1577882990/books/yibdrffecx9eouoa03wt.jpg"),
(f"{cwd}/2.jpg","https://res.cloudinary.com/theakira/image/upload/v1577796198/books/pshjs5haugk7zjc4cp5v.jpg"),
(f"{cwd}/3.jpg","https://res.cloudinary.com/theakira/image/upload/v1577788502/books/ug85fm5yfmnnvmatexgh.jpg"),
(f"{cwd}/4.jpg","https://res.cloudinary.com/theakira/image/upload/v1577754647/books/sdfsasw1nk3omkyvr5bn.jpg"),
(f"{cwd}/5.jpg","https://res.cloudinary.com/theakira/image/upload/v1577496602/books/upnvtmunzzqp6xtbfjrz.jpg"),
(f"{cwd}/6.jpg","https://res.cloudinary.com/theakira/image/upload/v1577496704/books/v41rhsi8llqunvrplwog.jpg"),
(f"{cwd}/7.jpg","https://res.cloudinary.com/theakira/image/upload/v1575248626/books/akc2bqsllxyblmhertjg.jpg"),
(f"{cwd}/8.jpg","https://res.cloudinary.com/theakira/image/upload/v1577491761/books/nrbjfwvmw2zeik07j4ra.jpg"),
(f"{cwd}/9.jpg","https://res.cloudinary.com/theakira/image/upload/v1571363356/books/aranrpgoypm3prjq6xe2.jpg"),
(f"{cwd}/10.jpg","https://arquivos.netlify.com/SantaCeia.jpg"),
(f"{cwd}/11.jpg","https://res.cloudinary.com/theakira/image/upload/v1577497190/books/ibibnw4najtmnz1gsna1.jpg"),
(f"{cwd}/12.jpg","https://res.cloudinary.com/theakira/image/upload/v1575248527/books/qcamggmgqazt8dbvunac.jpg")
]
def fetch_url(entry):
path, uri = entry
if not os.path.exists(path):
r = requests.get(uri, stream=True)
if r.status_code == 200:
with open(path, 'wb') as f:
for chunk in r:
f.write(chunk)
return path
threads = len(urls)
results = ThreadPool(threads).imap_unordered(fetch_url, urls)
for path in results:
print(path)
O multiprocessing.pool.ThreadPool
se comporta da mesma forma que o multiprocessing.pool.Pool
com a única diferença que usa threads em vez de processos para executar a lógica dos workers.
Novamente estamos definindo o diretório em que os arquivos serão salvos, nesse caso, o mesmo em que o script se encontra.
Guardamos as URLs em uma lista de tuplas: Contendo respectivamente o local que o arquivo será salvo e seu nome, bem como sua URL na web.
Definimos a função fetch_url()
responsável por fazer o download dos arquivos e nos retornar seu respectivo caminho que será salvo.
Por fim, definimos o número de threads baseado na quantidade de urls em nossa lista e iniciamos a execução das threads, imprimindo cada arquivo que baixamos em nosso prompt de comandos.
Conclusão
Neste breve guia apresentamos a você os métodos e bibliotecas mais comuns para fazermos download de arquivos com Python.
Também fomos capazes de explorar técnicas como async/await e threads de forma a maximizarmos a eficiência de nossos scripts.
Você pode utilizar a documentação de cada biblioteca como referência para estudos mais aprofundados.