ЯдроКодаподготовка к экзаменам
Учебная платформа

Загружаем материалы

Подготавливаем материалы и навигацию по разделу.

Requests и Pillow: получаем изображение по HTTP и делаем миниатюру

Автор: · Обновлено

Получить картинку по HTTP и уменьшить её — задача двух библиотек: Requests работает с сетевым ответом, Pillow декодирует и изменяет изображение. Практикум соединяет их через небольшой сервер на вашем компьютере. Вы полностью контролируете входные данные и можете воспроизвести как успешный ответ, так и ошибку.

Установка Requests и Pillow

В активированном проектном окружении:

python -m pip install requests pillow
python -m pip check

Pillow импортируется через from PIL import Image; команда pip install PIL не является установкой этого дистрибутива. Requests предоставляет модуль requests. Убедитесь, что рядом со скриптом нет файлов requests.py или каталога PIL, заслоняющих библиотеки.

Учебный сервер и клиент в одном файле

Сохраните http_images.py. Сервер слушает только 127.0.0.1; порт выбирает операционная система. Изображение создаётся программой и не скачивается с внешнего сайта.

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from io import BytesIO
from threading import Thread

import requests
from PIL import Image

source = BytesIO()
Image.new("RGB", (200, 120), (40, 130, 210)).save(source, format="PNG")
payload = source.getvalue()


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path != "/image.png":
            self.send_error(404)
            return
        self.send_response(200)
        self.send_header("Content-Type", "image/png")
        self.send_header("Content-Length", str(len(payload)))
        self.end_headers()
        self.wfile.write(payload)

    def log_message(self, format, *args):
        pass


server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
thread = Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
    address = f"http://127.0.0.1:{server.server_port}/image.png"
    with requests.get(address, timeout=(2, 5)) as response:
        response.raise_for_status()
        if response.headers.get("Content-Type") != "image/png":
            raise ValueError("Ожидалось PNG-изображение")
        with Image.open(BytesIO(response.content)) as image:
            image.thumbnail((80, 80), Image.Resampling.LANCZOS)
            image.save("thumbnail.png")
            print("HTTP:", response.status_code)
            print("Миниатюра:", image.size)
finally:
    server.shutdown()
    server.server_close()
    thread.join()

Запустите python http_images.py. Ожидаемый результат:

HTTP: 200
Миниатюра: (80, 48)

Создаётся thumbnail.png. В finally сервер прекращает работу, сокет закрывается, поток завершается. Поэтому повторный запуск не зависит от оставленного вручную процесса. http.server здесь используется как учебная заглушка; справочник стандартного сервера находится в документации Python.

Проверка ответа до обработки пикселей

raise_for_status() превращает HTTP-ошибку в исключение. Заголовок проверяется до открытия тела как картинки, а сам декодер Pillow дополнительно проверяет формат данных. Одно расширение .png в адресе ничего не доказывает. Параметр timeout=(2, 5) задаёт ожидания соединения и чтения; это не общий пятисекундный срок всей операции. Requests Quickstart.

thumbnail((80, 80)) сохраняет пропорции. Исходные 200×120 уменьшаются до 80×48, а не растягиваются в квадрат. Если нужен квадрат 80×80, придётся отдельно выбрать правило обрезки или добавления полей — это другой контракт результата. Pillow Tutorial.

Три независимых класса сбоев

  1. Сервер недоступен: Requests сообщает ошибку соединения или timeout; до Pillow выполнение не доходит.
  2. Сервер вернул 404: соединение состоялось, но raise_for_status() отклоняет ответ.
  3. Ответ 200 содержит неверные байты: заголовок ещё может выглядеть правильным, однако декодирование изображения завершается ошибкой.

Такое разделение помогает не «лечить сеть» переустановкой Pillow. В этом небольшом опыте размер ответа заранее ограничен собственным изображением. При переходе к произвольным внешним файлам понадобится отдельный лимит скачиваемых байтов и размеров изображения; заголовок Content-Length сам по себе не обеспечивает такой лимит.

Практика: управляемые ошибки

Замените адрес /image.png на /missing.png и убедитесь, что программа получает HTTP-ошибку. Затем верните адрес, но отправьте из обработчика b"not an image": это уже ошибка декодера. Поймайте исключения в разных ветках и напечатайте разные сообщения, сохранив закрытие сервера в finally.

Для третьего опыта создайте исходник 120×200. Ожидайте миниатюру 48×80. Проверка пройдена, если вы можете объяснить каждую пару размеров и отличить сетевой сбой от неверного содержимого ответа. Локальный HTTP-запрос и обработка PNG исполнены в Linux; внешних запросов пример не делает.

Источники