
Когда то давно мне нужно было автоматически загружать отчёты в систему партнёра: каждый день — новый CSV-файл с данными. Я думал, что мне придётся разбираться с multipart-формами и заголовками вручную, а оказалось — библиотека requests делает это за вас сама автоматически.
В этой статье я покажу пошагово, как:
- Отправлять изображения, PDF, логи и любые файлы через API
- Использовать
requests.post(files=...)правильно - Обрабатывать ошибки и проверять ответ сервера
- Работать с реальными сервисами (например, Telegram Bot API или тестовыми эндпоинтами)
Всё — с рабочим кодом и пояснениями, чтобы вы могли сразу вставить в свой скрипт.
Оглавление
Как работает отправка файлов через HTTP
Когда вы отправляете файл через форму в браузере, используется multipart/form-data — специальный формат, который разделяет данные на части («поля» и «файлы»).
API-серверы ожидают именно такой формат. К счастью, requests автоматически формирует его, если вы передаёте параметр files.
📚 Документация: Requests — File Uploads
Шаг 1. Базовая отправка файла
Подготовка и простой пример
import requests
# Путь к файлу
file_path = "report.pdf"
# Открываем файл в бинарном режиме
with open(file_path, "rb") as f:
files = {"file": f} # "file" — имя поля, как в <input name="file">
response = requests.post("https://httpbin.org/post", files=files)
print("Статус:", response.status_code)
print("Ответ сервера:", response.json()["files"].keys())
✅
httpbin.org/post— отличный демо-сервис для тестирования загрузок.
Шаг 2. Отправка с именем и MIME-типом
Иногда сервер проверяет расширение или тип файла. Укажите их явно:
with open("photo.jpg", "rb") as f:
files = {
"document": (
"my_photo.jpg", # желаемое имя файла
f,
"image/jpeg" # MIME-тип
)
}
response = requests.post("https://httpbin.org/post", files=files)
💡 Распространённые MIME-типы:
image/jpeg,image/pngapplication/pdftext/plain,text/csvapplication/octet-stream(универсальный)
Шаг 3. Отправка нескольких файлов
files = [
("images", ("1.jpg", open("1.jpg", "rb"), "image/jpeg")),
("images", ("2.png", open("2.png", "rb"), "image/png")),
("report", ("data.csv", open("data.csv", "rb"), "text/csv"))
]
response = requests.post("https://httpbin.org/post", files=files)
# Не забудьте закрыть файлы!
for _, (_, f, _) in
f.close()
Или используйте контекстные менеджеры для безопасности.
🔍 Что такое «контекстный менеджер»?
В Python контекстный менеджер — это конструкция with ... as ..., которая автоматически открывает и закрывает ресурс (например, файл), даже если в коде произошла ошибка.
Без него вы должны вручную писать file.close() — и легко забыть это сделать.
С ним — Python сделает всё за вас.
❌ Пример без контекстного менеджера (опасно!)
# Опасный способ
files = []
f1 = open("1.jpg", "rb")
f2 = open("2.png", "rb")
f3 = open("report.csv", "rb")
files = [
("images", ("1.jpg", f1, "image/jpeg")),
("images", ("2.png", f2, "image/png")),
("report", ("report.csv", f3, "text/csv"))
]
try:
response = requests.post("https://httpbin.org/post", files=files)
print("Успех!")
except Exception as e:
print("Ошибка:", e)
# А вот здесь файлы НЕ закрыты! Они останутся открытыми в памяти.
⚠️ Если произойдёт ошибка, файлы не закроются, и это может привести к:
- Утечке памяти
- Блокировке файла (на Windows нельзя удалить открытый файл)
- Достигнутому лимиту открытых файлов в системе
✅ Правильный способ: используем with для каждого файла
# Безопасный способ — один файл
with open("photo.jpg", "rb") as f:
files = {"file": f}
response = requests.post("https://httpbin.org/post", files=files)
# Файл автоматически закрыт здесь — даже если был исключение!
Но как быть с несколькими файлами? Ведь with можно вложить:
# Безопасный способ — несколько файлов
with open("1.jpg", "rb") as f1, \
open("2.png", "rb") as f2, \
open("report.csv", "rb") as f3:
files = [
("images", ("1.jpg", f1, "image/jpeg")),
("images", "2.png", f2, "image/png")),
("report", ("report.csv", f3, "text/csv"))
]
response = requests.post("https://httpbin.org/post", files=files)
# Все файлы закрыты автоматически!
💡 Обратите внимание на **обратный слеш
\** — он позволяет переносить длинную строкуwith.
💡 Альтернатива: временные файлы в списке (для динамического количества)
Если файлов много и их имена хранятся в списке:
file_paths = ["1.jpg", "2.png", "3.gif"]
files_data = []
# Открываем все файлы безопасно
with ExitStack() as stack:
for path in file_paths:
# ExitStack автоматически закроет все файлы
f = stack.enter_context(open(path, "rb"))
mime = "image/jpeg" if path.endswith(".jpg") else "image/png"
files_data.append(("files", (os.path.basename(path), f, mime)))
response = requests.post("https://httpbin.org/post", files=files_data)
Для этого нужно:
from contextlib import ExitStack
import os
📚
ExitStack— встроенный инструмент для работы с динамическим числом ресурсов.
✅ Вывод для начинающего пользователя
Всегда используйте with open(...) as f: — это:
- Гарантирует, что файл закроется
- Делает код короче и надёжнее
- Стандарт де-факто в Python
Простое правило:
Если вы открываете файл — делайте это внутри with. Всегда.
Тогда вы никогда не забудете закрыть файл, даже если скрипт упадёт.
Шаг 4. Реальный пример: отправка фото в Telegram
Подготовка
Вам понадобится:
- Токен бота
- Chat ID
(Как получить — в статье Отправка сообщений в Telegram через API)
Код отправки
def send_photo_to_telegram(photo_path, caption=""):
token = "ваш_токен"
chat_id = "ваш_id"
url = f"https://api.telegram.org/bot{token}/sendPhoto"
with open(photo_path, "rb") as photo:
files = {"photo": photo}
data = {"chat_id": chat_id, "caption": caption}
response = requests.post(url, files=files, data=data)
if response.status_code == 200:
print("✅ Фото отправлено")
else:
print("❌ Ошибка:", response.json())
💡 Это настоящий кейс из автоматизации отчётов.
Шаг 5. Обработка ошибок и проверка ответа
Всегда проверяйте статус и содержимое ответа:
try:
response = requests.post(
"https://api.example.com/upload",
files={"file": open("data.csv", "rb")},
timeout=30
)
response.raise_for_status() # вызовет исключение при 4xx/5xx
result = response.json()
if result.get("success"):
print("Файл принят, ID:", result["file_id"])
else:
print("Сервер отклонил файл:", result.get("error"))
except requests.exceptions.RequestException as e:
print(f"Ошибка сети: {e}")
except ValueError:
print("Ответ не в формате JSON")
finally:
# закрытие файла, если не использовали with
pass
Советы для продвинутого использования
- Используйте
with open()— файлы закроются автоматически - Добавьте
timeout— чтобы скрипт не «завис» - Для больших файлов — рассмотрите стриминг (
stream=True)
print(f"Отправляю {os.path.getsize(path)} байт")
Заключение
Надеюсь статья была полезной и вы теперь вы умеете:
✅ Отправлять любые файлы через API
✅ Указывать имя и MIME-тип
✅ Работать с Telegram, httpbin и другими сервисами
✅ Обрабатывать ошибки и проверять ответы
Этот подход используется в:
- Автоматической отправке отчётов
- Интеграции с CRM и хранилищами
- Тестировании API на бэкенде
🐍
requestsделает загрузку файлов простой — без ручного формирования multipart.
• python api запрос — получение данных и обработка JSON
• python telegram api send message — отправка уведомлений и медиа
• python github api — примеры с авторизацией и пагинацией
📢 Подписывайтесь на Telegram-канал PythonAuto, чтобы не пропустить новые гайды по автоматизации, парсингу и Python.
👉 Ваш интерес — лучшая мотивация для новых статей!