Асинхронное скачивание изображений в Python‑проекте

Используйте одну aiohttp.ClientSession и запустите десять задач скачивания конкурентно. Сохраняйте поток частями в папку image под UUID-именами, проверяя HTTP-статус и ограничивая размер.

Короткий ответ

Что ответить на собеседовании

Используйте одну aiohttp.ClientSession и запустите десять задач скачивания конкурентно. Сохраняйте поток частями в папку image под UUID-именами, проверяя HTTP-статус и ограничивая размер.

Подробный разбор

Ответ с пояснениями

Условие

Создать небольшое Python‑приложение, которое:

  • Параллельно загружает 10 картинок из сети;
  • Сохраняет полученные файлы в директорию image, расположенную в корне проекта;
  • Для загрузки использовать асинхронные инструменты (например, aiohttp и asyncio);
  • При отсутствии папки image она должна быть создана автоматически;
  • Каждое изображение сохранять под уникальным именем.

Ответ

Используйте одну aiohttp.ClientSession и запустите десять задач скачивания конкурентно. Сохраняйте поток частями в папку image под UUID-именами, проверяя HTTP-статус и ограничивая размер.

Пример для Python 3.11+, если файл скрипта лежит в корне проекта и urls содержит десять доверенных адресов:

import asyncio
from pathlib import Path
from uuid import uuid4
import aiohttp

ROOT = Path(__file__).resolve().parent / 'image'
LIMIT = 20 * 1024 * 1024

async def download(session, url):
    temporary = ROOT / f'{uuid4().hex}.part'
    try:
        async with session.get(url) as response:
            response.raise_for_status()
            if not response.headers.get('Content-Type', '').startswith('image/'):
                raise ValueError('Expected an image')
            size = 0
            with temporary.open('xb') as file:
                async for chunk in response.content.iter_chunked(64 * 1024):
                    size += len(chunk)
                    if size > LIMIT:
                        raise ValueError('Image too large')
                    file.write(chunk)
        destination = temporary.with_suffix('.img')
        temporary.rename(destination)
        return destination
    except BaseException:
        temporary.unlink(missing_ok=True)
        raise

async def main(urls):
    if len(urls) != 10:
        raise ValueError('Expected ten URLs')
    ROOT.mkdir(parents=True, exist_ok=True)
    timeout = aiohttp.ClientTimeout(total=60)
    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with asyncio.TaskGroup() as group:
            tasks = [group.create_task(download(session, url)) for url in urls]
    return [task.result() for task in tasks]

Вызов: asyncio.run(main(urls)). Сетевые операции конкурентны; небольшие записи в файл здесь синхронные, для медленного диска нужен отдельный файловый исполнитель. Расширение .img нейтральное; Content-Type не доказывает корректность изображения. Для недоверенных URL дополнительно нужна защита SSRF.

Источник: docs.aiohttp.org.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее