Integrate tqdm Progress Bar with aiohttp Requests in Python
In this tutorial, you’ll learn how to integrate the tqdm progress bar with aiohttp requests in Python.
This combination allows you to visualize the progress of your HTTP requests.
Basic Integration
To create a progress bar for a single aiohttp request, use the following code:
import asyncio
import aiohttp
from tqdm import tqdm
async def download_with_progress(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
total_size = int(response.headers.get('content-length', 0))
with tqdm(total=total_size, unit='iB', unit_scale=True) as progress_bar:
data = b''
async for chunk in response.content.iter_chunked(102400):
size = len(chunk)
data += chunk
progress_bar.update(size)
return data
url = 'http://localhost/myfile.zip'
asyncio.run(download_with_progress(url))
Output:
100%|██████████| 110M/110M [00:19<00:00, 5.50MiB/s]
This code creates a progress bar that updates as chunks of data are downloaded.
The bar shows the percentage complete, amount downloaded, total size, time elapsed, and download speed.
Handle unknown content length
To handle cases where content length is not provided, you can get file size from the header content-length:
import asyncio
import aiohttp
from tqdm import tqdm
async def download_with_progress(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
total_size = int(response.headers.get('content-length', 0))
with tqdm(total=total_size, unit='iB', unit_scale=True, desc=url) as progress_bar:
data = b''
async for chunk in response.content.iter_chunked(102400):
size = len(chunk)
data += chunk
progress_bar.update(size)
if total_size == 0:
progress_bar.total = progress_bar.n
return data
url = 'http://localhost/unknown_size_file.bin'
asyncio.run(download_with_progress(url))
Output:
http://localhost/unknown_size_file.bin: 100%|██████████| 50.2M/50.2M [00:03<00:00, 1.73MiB/s]
Handle Multiple Requests
To use tqdm with multiple concurrent requests, you can use asyncio.gather() with tqdm for parallel requests
import asyncio
import aiohttp
from tqdm.asyncio import tqdm
async def download_url(session, url):
async with session.get(url) as response:
total_size = int(response.headers.get('content-length', 0))
with tqdm(total=total_size, unit='iB', unit_scale=True, desc=url) as progress_bar:
data = b''
async for chunk in response.content.iter_chunked(102400):
size = len(chunk)
data += chunk
progress_bar.update(size)
return data
async def download_multiple(urls):
async with aiohttp.ClientSession() as session:
tasks = [download_url(session, url) for url in urls]
await asyncio.gather(*tasks)
urls = ['http://localhost/file1.zip', 'http://localhost/file2.zip', 'http://localhost/file3.zip']
asyncio.run(download_multiple(urls))
Output:
http://localhost/file1.zip: 100%|██████████| 110M/110M [01:01<00:00, 1.79MiB/s] http://localhost/file2.zip: 100%|█████████▉| 110M/110M [01:01<00:00, 691kiB/s] http://localhost/file3.zip: 100%|██████████| 110M/110M [01:01<00:00, 687kiB/s]
This code displays individual progress bars for each file being downloaded concurrently.
Monitor Streaming Response Progress
For streaming responses, use the content.read() method and update the progress bar as the chunks are retrieved:
import asyncio
import aiohttp
from tqdm import tqdm
async def stream_download(url, chunk_size=8192):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
total_size = int(response.headers.get('content-length', 0))
with tqdm(total=total_size, unit='iB', unit_scale=True, desc=url) as progress_bar:
data = b''
while True:
chunk = await response.content.read(chunk_size)
if not chunk:
break
data += chunk
progress_bar.update(len(chunk))
return data
url = 'http://localhost/large_stream.mp4'
asyncio.run(stream_download(url))
Output:
http://localhost/large_stream.mp4: 100%|██████████| 153M/153M [00:44<00:00, 3.42MiB/s]
This code streams the response content and updates the progress bar as chunks are received.
Retry with Progress
To implement retry attempts with a progress bar, you can reset the progress bar for each attempt and show which attempt is currently in progress:
import asyncio
import aiohttp
from tqdm import tqdm
async def download_with_reset_on_retry(url, max_retries=3):
async with aiohttp.ClientSession() as session:
for attempt in range(max_retries):
try:
async with session.get(url) as response:
total_size = int(response.headers.get('content-length', 0))
with tqdm(total=total_size, unit='iB', unit_scale=True,
desc=f"{url} (Attempt {attempt+1}/{max_retries})") as progress_bar:
data = b''
async for chunk in response.content.iter_chunked(1024):
size = len(chunk)
data += chunk
progress_bar.update(size)
return data
except aiohttp.ClientError:
if attempt == max_retries - 1:
tqdm.write(f"Failed to download {url} after {max_retries} attempts")
raise
await asyncio.sleep(2 ** attempt)
url = 'http://localhost/unstable_large_file.zip'
asyncio.run(download_with_reset_on_retry(url))
Output:
http://localhost/unstable_large_file.zip (Attempt 1/3): 6%|█████ | 6.56M/110.0M [00:04<02:01, 848kiB/s] http://localhost/unstable_large_file.zip (Attempt 2/3): 100%|██████████| 110.0M/110.0M [00:04<00:00, 598kiB/s]
Upload Files with Progress Bars
To upload files with progress tracking, you can use multipart requests and show progress for each file upload:
import asyncio
import aiohttp
from tqdm import tqdm
import os
async def upload_file(session, url, file_path):
file_size = os.path.getsize(file_path)
with tqdm(total=file_size, unit='iB', unit_scale=True, desc=os.path.basename(file_path)) as progress_bar:
with open(file_path, 'rb') as f:
async with session.post(url, data=ProgressReader(f, progress_bar)) as response:
return await response.text()
class ProgressReader:
def __init__(self, file_obj, progress_bar):
self.file_obj = file_obj
self.progress_bar = progress_bar
async def read(self, size):
data = self.file_obj.read(size)
self.progress_bar.update(len(data))
return data
async def upload_files(url, file_paths):
async with aiohttp.ClientSession() as session:
tasks = [upload_file(session, url, file_path) for file_path in file_paths]
results = await asyncio.gather(*tasks)
return results
url = 'http://localhost/upload'
file_paths = ['large_file1.zip', 'large_file2.pdf', 'large_file3.mp4']
asyncio.run(upload_files(url, file_paths))
Output:
large_file1.zip: 100%|██████████| 50.0M/50.0M [00:25<00:00, 2.00MiB/s] large_file2.pdf: 100%|██████████| 25.0M/25.0M [00:12<00:00, 2.08MiB/s] large_file3.mp4: 100%|██████████| 100M/100M [00:50<00:00, 2.00MiB/s]
Mokhtar is the founder of LikeGeeks.com. He is a seasoned technologist and accomplished author, with expertise in Linux system administration and Python development. Since 2010, Mokhtar has built an impressive career, transitioning from system administration to Python development in 2015. His work spans large corporations to freelance clients around the globe. Alongside his technical work, Mokhtar has authored some insightful books in his field. Known for his innovative solutions, meticulous attention to detail, and high-quality work, Mokhtar continually seeks new challenges within the dynamic field of technology.