Загрузка данных
import asyncio
import time
from typing import List
# asyncio - для асинхронного программирования
# time - замер времени выполнения
# List - аннотация для списков
import aiohttp # асинхронный HTTP-клиент (pip install aiohttp)
import requests # синхронный HTTP-клиент (pip install requests)
# ===========================================================================
# СИНХРОННОЕ СКАЧИВАНИЕ (один за другим)
# ===========================================================================
def download_sync(urls: List[str]) -> List[bytes]:
"""Скачивает все файлы по очереди (блокирует поток)."""
results = [] # сюда будем складывать скачанные байты
start = time.perf_counter() # точное время начала (в секундах)
for i, url in enumerate(urls, start=1): # i=1,2,3... url=ссылка на файл
print(f"[SYNC] start {i}: {url}") # лог: начали скачивать i-й файл
resp = requests.get(url) # синхронный запрос (блокирует до ответа)
resp.raise_for_status() # если ошибка HTTP 4xx/5xx → выбросит исключение
data = resp.content # получаем содержимое ответа (байты)
results.append(data) # добавляем в список результатов
print(f"[SYNC] done {i}: size={len(data)} bytes") # лог: закончили
total = time.perf_counter() - start # общее время выполнения
print(f"[SYNC] total time: {total:.2f} s\n") # выводим время с 2 знаками после запятой
return results # возвращаем все скачанные файлы
# ===========================================================================
# АСИНХРОННОЕ СКАЧИВАНИЕ ОДНОГО ФАЙЛА С СЕМАФОРОМ
# ===========================================================================
async def fetch_one(
session: aiohttp.ClientSession, # общий HTTP-клиент для всех запросов
url: str, # ссылка на файл
sem: asyncio.Semaphore, # семафор для ограничения параллелизма
index: int, # номер задачи (для логов)
) -> bytes:
"""Скачивает один файл асинхронно с контролем семафора."""
async with sem: # ← КЛЮЧЕВОЕ: одновременно в этом блоке будет максимум K задач!
# sem выдаёт "разрешения" (permits), их всего K штук
print(f"[ASYNC] start {index}: {url} (free permits={sem._value})")
# sem._value - сколько разрешений осталось свободными
async with session.get(url) as resp: # асинхронный HTTP-запрос (не блокирует)
resp.raise_for_status() # проверяем статус ответа
data = await resp.read() # асинхронно читаем тело ответа
# await - "подождать", но не блокирует весь поток asyncio
print(f"[ASYNC] done {index}: size={len(data)} bytes")
return data # возвращаем байты файла
# ===========================================================================
# АСИНХРОННОЕ СКАЧИВАНИЕ ВСЕХ ФАЙЛОВ С ЛИМИТОМ K
# ===========================================================================
async def download_async(urls: List[str], k: int) -> List[bytes]:
"""Скачивает N файлов, но максимум k одновременно."""
start = time.perf_counter() # время начала
sem = asyncio.Semaphore(k) # семафор с лимитом k одновременных задач
async with aiohttp.ClientSession() as session: # один клиент для всех запросов
# создаём задачи сразу на все N файлов
tasks = [
asyncio.create_task(fetch_one(session, url, sem, i))
# create_task - сразу запускает корутину в фоне
for i, url in enumerate(urls, start=1)
]
# ждём завершения ВСЕХ задач, результаты вернутся в том же порядке
results = await asyncio.gather(*tasks)
# gather(*tasks) = жди все задачи и собери результаты списком
total = time.perf_counter() - start
print(f"[ASYNC] total time (K={k}): {total:.2f} s\n")
return results
# ===========================================================================
# ТОЧКА ВХОДА - СРАВНЕНИЕ
# ===========================================================================
if __name__ == "__main__":
N = 10 # количество файлов для скачивания
K = 3 # максимум одновременных асинхронных задач
test_url = "https://httpbin.org/delay/1" # тестовая ссылка, отвечает 1 секунду
urls = [test_url] * N # список из N одинаковых ссылок
print("=== СИНХРОННО (по очереди) ===")
sync_results = download_sync(urls)
print("=== АСИНХРОННО (лимит K=3) ===")
async_results = asyncio.run(download_async(urls, K))
# asyncio.run - запускает асинхронную функцию в новом event loop
print(f"Синхронно скачали: {len(sync_results)} файлов")
print(f"Асинхронно скачали: {len(async_results)} файлов")