Как сохранить полученный от сервера ответ в файл
Сохранение ответа в файл в Python
Как эффективно сохранить ответ HTTP-запроса в файл?
Наиболее распространённая задача - сохранение данных, полученных от сервера через библиотеку requests. Основное решение использует атрибут content (для бинарных данных) или text (для текста) в сочетании с контекстным менеджером open.
import requests
url = 'https://httpbin.org/image/png'
response = requests.get(url, stream=True)
with open('image.png', 'wb') as f:
f.write(response.content)
print('Файл сохранён')ввод программ на python (ввод данных в программе python)
При использовании stream=True ответ загружается небольшими порциями, что позволяет экономить память при больших файлах. Без этого флага content загружается целиком.
Типичные проблемы:
- Ошибка соединения - requests.exceptions.ConnectionError. Решение: добавить таймаут и обработку исключений.
- Недостаточно места на диске - проверить доступное место перед записью.
- Отсутствие папки для файла - создать директорию через os.makedirs.
- Повреждение файла при бинарной записи - проверить тип содержимого (image/jpeg, application/pdf).
Как сохранить ответ в файл с поблочной записью (chunked download)?
Для больших файлов (гигабайты) не следует использовать content, лучше записывать поток по частям:
import requests
url = 'https://example.com/largefile.zip'
response = requests.get(url, stream=True)
response.raise_for_status()
with open('largefile.zip', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print('Большой файл сохранён')Python file io (ввод-вывод файлов в python)
Этот вариант снижает потребление памяти до минимума и позволяет контролировать процесс.
Возможные ошибки:
- Прерывание соединения во время загрузки - необходимо реализовать возобновление (контролировать заголовок Range).
- Некорректный размер chunk_size - слишком маленькое значение замедляет запись, слишком большое - увеличивает память на один chunk.
Как сохранить ответ в формате JSON с читаемой структурой?
Если сервер возвращает JSON, его можно сразу сохранить в файл с красивым форматированием:
import requests
import json
url = 'https://api.github.com/users/python'
response = requests.get(url)
data = response.json()
with open('user_python.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print('JSON сохранён')
Python temp files (временные файлы в python)
Опция ensure_ascii=False сохраняет кириллицу и другие символы без экранирования.
Проблемы:
- Ошибка декодирования JSON - проверить кодировку (response.encoding).
- Огромный объект - не загружать в память (использовать ijson для потоковой обработки).
Как сохранить ответ с автоподстановкой имени файла из заголовков?
Многие серверы передают имя файла в заголовке Content-Disposition. Извлечение имени делает код универсальным:
import requests
import re
url = 'https://example.com/download'
response = requests.get(url, stream=True)
content_disp = response.headers.get('content-disposition', '')
filename = re.findall('filename="(.+)"', content_disp)
if not filename:
filename = 'downloaded_file'
else:
filename = filename[0]
with open(filename, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f'Файл сохранён как {filename}')Python index files (индексация файлов в python)
Этот подход автоматизирует сохранение при работе с динамическими ссылками.
Нюансы:
- Содержимое заголовка может быть закодировано (RFC 5987) - требуется дополнительная обработка.
- Безопасность: не доверять имени файла, ограничить длину и символы.
Как сохранить ответ из urllib без requests?
Встроенная библиотека urllib.request также подходит для загрузки:
import urllib.request
url = 'https://httpbin.org/image/jpeg'
urllib.request.urlretrieve(url, 'image.jpg')
print('Файл сохранён через urllib')File python class (класс для работы с файлами в python)
Функция urlretrieve автоматически записывает ответ в файл, но не поддерживает потоковую запись и перехват ошибок без обёртки.
Недостатки:
- Нельзя получить прогресс без замены обработчика.
- Медленнее при больших файлах из-за копирования в память.
Как сохранить ответ асинхронно с aiohttp?
В асинхронных приложениях (например, веб-скрейпинг) используется aiohttp:
import aiohttp
import asyncio
async def fetch_and_save(url, filename):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
with open(filename, 'wb') as f:
f.write(await resp.read())
print(f'Сохранён {filename}')
asyncio.run(fetch_and_save('https://httpbin.org/image/png', 'async_image.png'))Python file utf 8 (кодировка utf-8 для файлов в python)
Для больших файлов используют resp.content.iter_chunked.
Ошибки:
- Event loop конфликтует - использовать asyncio.run только один раз.
- Не закрытая сессия - обязательно использовать async with.
Как сохранить ответ с отображением прогресса (tqdm)?
Чтобы визуализировать загрузку, оборачивают итератор в tqdm:
import requests
from tqdm import tqdm
url = 'https://example.com/bigfile.iso'
response = requests.get(url, stream=True)
total = int(response.headers.get('content-length', 0))
with open('bigfile.iso', 'wb') as f:
with tqdm(total=total, unit='B', unit_scale=True) as pbar:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
pbar.update(len(chunk))
print('Загрузка завершена')
Если сервер не указывает Content-Length, прогресс не отображается.
Проблемы:
- Отсутствие заголовка длины - индикатор не отображается.
- Установка tqdm - дополнительная зависимость.
Дополнительные примеры сохранения ответа в файл
Сохранение с проверкой целостности (хэш SHA256)
После загрузки полезно убедиться, что файл не повреждён. Пример сохраняет ответ и вычисляет хэш, сравнивая его с ожидаемым:
import requests
import hashlib
url = 'https://example.com/firmware.bin'
expected_hash = 'a1b2c3...' # заранее известный хэш
response = requests.get(url, stream=True)
sha256 = hashlib.sha256()
with open('firmware.bin', 'wb') as f:
for chunk in response.iter_content(chunk_size=4096):
f.write(chunk)
sha256.update(chunk)
actual_hash = sha256.hexdigest()
if actual_hash == expected_hash:
print('Целостность подтверждена')
else:
print('Хэш не совпадает - файл повреждён')
Целостность подтверждена
Такой подход применяется для загрузки дистрибутивов, прошивок и других критичных данных.
Параллельная загрузка нескольких файлов с использованием concurrent.futures
Для ускорения можно загружать несколько файлов одновременно:
import requests
from concurrent.futures import ThreadPoolExecutor
def download_file(url, filename):
with requests.get(url, stream=True) as r:
r.raise_for_status()
with open(filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
return f'{filename} загружен'
urls = [
('https://httpbin.org/image/png', '1.png'),
('https://httpbin.org/image/jpeg', '1.jpg'),
('https://httpbin.org/image/svg', '1.svg')
]
with ThreadPoolExecutor(max_workers=3) as executor:
results = list(executor.map(lambda urls: download_file(*urls), urls))
for res in results:
print(res)
1.png загружен 1.jpg загружен 1.svg загружен
Ограничение: не следует открывать слишком много потоков, чтобы не перегрузить сетевое соединение.
Сохранение ответа с автоповтором при ошибках (retry)
Используя requests.adapters.HTTPAdapter с политикой повторных попыток:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry_policy = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
adapter = HTTPAdapter(max_retries=retry_policy)
session.mount('https://', adapter)
session.mount('http://', adapter)
url = 'https://example.com/unstable-server/document.pdf'
response = session.get(url, stream=True)
response.raise_for_status()
with open('document.pdf', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print('Документ сохранён после возможных повторов')
Повторные попытки увеличивают надёжность при нестабильной сети.
Сохранение ответа с использованием shutil.copyfileobj
Библиотека shutil предоставляет удобный способ копирования потока:
import requests
import shutil
url = 'https://httpbin.org/image/png'
response = requests.get(url, stream=True)
response.raw.decode_content = True # обрабатывает сжатие gzip
with open('image_shutil.png', 'wb') as f:
shutil.copyfileobj(response.raw, f)
print('Файл сохранён через shutil')
Этот метод компактен, но требует осторожности с кодировкой (response.raw может содержать несжатые данные).
Сохранение ответа в файл в temp-директорию
Для временных файлов используют tempfile:
import requests
import tempfile
import os
url = 'https://httpbin.org/image/png'
response = requests.get(url, stream=True)
with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmp:
for chunk in response.iter_content(chunk_size=8192):
tmp.write(chunk)
tmp_path = tmp.name
print(f'Временный файл: {tmp_path}')
# Позже можно удалить вручную
os.unlink(tmp_path)
Удобно для кэширования или промежуточной обработки.