Как сохранить полученный от сервера ответ в файл

Раздел: Python -> Файловый ввод-вывод

Сохранение ответа в файл в 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 - дополнительная зависимость.
- Python log file (логирование в файл в python)
- Python file methods (методы работы с файлами в python)
- File models in python (модели файлов в python)

Дополнительные примеры сохранения ответа в файл

Сохранение с проверкой целостности (хэш 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)

Удобно для кэширования или промежуточной обработки.

Сохранение ответа в файл в Python - comments

En
Python response file (python)