JSON в CSV на Python — Примеры DictWriter и pandas

·Backend Developer·ПровереноPriya Sharma·Опубликовано

Используйте бесплатный JSON to CSV прямо в браузере — установка не требуется.

Попробовать JSON to CSV онлайн →

Почти каждый конвейер обработки данных рано или поздно упирается в один и тот же шаг: API возвращает JSON, а следующий потребитель — таблица, скрипт импорта, команда Redshift COPY — ожидает CSV. Конвертация JSON в CSV на Python кажется тривиальной, пока не столкнёшься с вложенными объектами, непоследовательными ключами или значениями datetime, требующими особой обработки. Python предлагает два надёжных пути: встроенные модули json + csv для скриптов без зависимостей и pandas для выравнивания вложенных структур и работы с большими наборами данных — или онлайн-конвертер JSON в CSV для быстрых разовых преобразований без написания кода. Это руководство охватывает оба подхода от начала до конца с готовыми к запуску примерами на Python 3.8+.

  • csv.DictWriter конвертирует список словарей в CSV без зависимостей — используйте json.load() для разбора, затем writeheader() + writerows().
  • Всегда открывайте CSV-файлы с newline="" на Windows, чтобы предотвратить появление пустых строк между строками данных.
  • pd.json_normalize() выравнивает вложенный JSON в плоский DataFrame перед вызовом to_csv() — автоматически обрабатывает многоуровневое вложение.
  • Передавайте index=False в DataFrame.to_csv() — без этого pandas записывает лишний столбец с номерами строк.
  • Для файлов свыше 500 МБ используйте ijson для потоковой обработки JSON совместно с csv.DictWriter для постоянного использования памяти.

Что такое конвертация JSON в CSV?

Конвертация JSON в CSV преобразует массив JSON-объектов в табличный формат, где каждый объект становится строкой, а каждый ключ — заголовком столбца. JSON иерархичен — объекты могут быть произвольно глубоко вложены. CSV плоский — каждое значение находится в ячейке на пересечении строки и столбца. Преобразование работает чисто, когда все объекты имеют одинаковый набор ключей верхнего уровня. Вложенные объекты, массивы и непоследовательные ключи делают задачу интереснее. Исходные данные остаются идентичными; меняется только структура.

Before · json
After · json
[{"order_id":"ord_91a3","total":149.99,"status":"shipped"},
 {"order_id":"ord_b7f2","total":34.50,"status":"pending"}]
order_id,total,status
ord_91a3,149.99,shipped
ord_b7f2,34.50,pending

csv.DictWriter — конвертация JSON в CSV без pandas

Модуль csv входит в комплект каждой установки Python. Никакого pip install, никаких виртуальных окружений. csv.DictWriter принимает список словарей и записывает каждый из них как строку CSV, отображая ключи словаря на заголовки столбцов. Параметр fieldnames управляет как порядком столбцов, так и тем, какие ключи включаются.

Python 3.8+ — минимальный пример json в csv
import json
import csv

# Пример JSON-данных — массив объектов-заказов
json_string = """
[
  {"order_id": "ord_91a3", "product": "Wireless Keyboard", "quantity": 2, "unit_price": 74.99},
  {"order_id": "ord_b7f2", "product": "USB-C Hub", "quantity": 1, "unit_price": 34.50},
  {"order_id": "ord_c4e8", "product": "Monitor Stand", "quantity": 3, "unit_price": 29.95}
]
"""

records = json.loads(json_string)

with open("orders.csv", "w", newline="", encoding="utf-8") as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=records[0].keys())
    writer.writeheader()
    writer.writerows(records)

# orders.csv:
# order_id,product,quantity,unit_price
# ord_91a3,Wireless Keyboard,2,74.99
# ord_b7f2,USB-C Hub,1,34.50
# ord_c4e8,Monitor Stand,3,29.95

Аргумент newline="" в open() не является необязательным на Windows. Без него вы получите двойные символы возврата каретки — что отображается как пустые строки между каждой строкой данных в Excel. На macOS и Linux это безвредно, поэтому просто всегда включайте его.

Код выше использует json.loads() для строки. Используйте json.load() (без буквы s) при чтении из файлового дескриптора. Это постоянно сбивает людей с толку — одна функция читает строку, другая — файловый объект.

Python 3.8+ — читаем JSON-файл, записываем CSV-файл
import json
import csv

with open("server_metrics.json", encoding="utf-8") as jf:
    metrics = json.load(jf)  # json.load() для файловых объектов

# Явные fieldnames управляют порядком столбцов
columns = ["timestamp", "hostname", "cpu_percent", "memory_mb", "disk_io_ops"]

with open("server_metrics.csv", "w", newline="", encoding="utf-8") as cf:
    writer = csv.DictWriter(cf, fieldnames=columns, extrasaction="ignore")
    writer.writeheader()
    writer.writerows(metrics)

# В выводе только пять указанных столбцов, именно в таком порядке

Установка extrasaction="ignore" молча отбрасывает ключи в словарях, не входящие в список fieldnames. По умолчанию используется "raise", что вызывает ValueError при наличии неожиданного ключа в любом словаре. Выбирайте тот вариант, который соответствует вашей толерантности к сюрпризам.

Примечание:csv.DictWriter vs csv.writer: DictWriter автоматически отображает ключи словаря на позиции столбцов. csv.writer записывает сырые списки как строки — порядок столбцов вы контролируете самостоятельно. DictWriter почти всегда предпочтительнее для JSON-в-CSV, поскольку JSON-записи уже являются словарями.

Модуль csv Python поставляется с тремя именованными диалектами: excel (разделитель-запятая, окончания строк CRLF — по умолчанию), excel-tab (разделитель-табуляция, окончания CRLF) и unix (окончания строк LF, все нечисловые поля заключаются в кавычки). Передайте имя диалекта как аргумент dialect в csv.DictWriter. Также можно определить пользовательский диалект через csv.register_dialect() для систем с нестандартными правилами кавычек или разделителей. Для большинства задач JSON-в-CSV диалект excel подходит, но переключайтесь на unix при записи файлов, которые будут обрабатываться POSIX-утилитами вроде awk или sort.

Нестандартные типы: datetime, UUID и Decimal

JSON из API часто содержит даты как ISO-строки, UUID как строки с дефисами и денежные значения как числа с плавающей точкой. Когда вы разбираете их в Python-объекты для обработки перед записью CSV, нужно снова конвертировать их в строки. Модуль csv вызывает str() для каждого значения, поэтому большинство типов работает «из коробки». Но объекты datetime дают неудобное стандартное строковое представление, а значения Decimal требуют явного форматирования, чтобы избежать научной нотации.

Python 3.8+ — предобработка datetime и Decimal перед записью CSV
import json
import csv
from datetime import datetime, timezone
from decimal import Decimal
from uuid import UUID

# Имитация разобранного ответа API с Python-типами
transactions = [
    {
        "txn_id": UUID("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
        "created_at": datetime(2026, 3, 15, 9, 30, 0, tzinfo=timezone.utc),
        "amount": Decimal("1249.99"),
        "currency": "USD",
        "merchant": "CloudHost Inc.",
    },
    {
        "txn_id": UUID("b2c3d4e5-f6a7-8901-bcde-f12345678901"),
        "created_at": datetime(2026, 3, 15, 14, 12, 0, tzinfo=timezone.utc),
        "amount": Decimal("87.50"),
        "currency": "EUR",
        "merchant": "DataSync GmbH",
    },
]

def prepare_row(record: dict) -> dict:
    """Конвертирует нестроковые типы в строки для CSV."""
    return {
        "txn_id": str(record["txn_id"]),
        "created_at": record["created_at"].isoformat(),
        "amount": f"{record['amount']:.2f}",
        "currency": record["currency"],
        "merchant": record["merchant"],
    }

with open("transactions.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["txn_id", "created_at", "amount", "currency", "merchant"])
    writer.writeheader()
    for txn in transactions:
        writer.writerow(prepare_row(txn))

# transactions.csv:
# txn_id,created_at,amount,currency,merchant
# a1b2c3d4-e5f6-7890-abcd-ef1234567890,2026-03-15T09:30:00+00:00,1249.99,USD,CloudHost Inc.
# b2c3d4e5-f6a7-8901-bcde-f12345678901,2026-03-15T14:12:00+00:00,87.50,EUR,DataSync GmbH

Функция prepare_row() — правильный подход здесь. Вместо того чтобы учить csv.DictWriter работать с пользовательскими типами, вы нормализуете каждую запись до строк перед записью. Я предпочитаю явно вызывать .isoformat() для объектов datetime, а не полагаться на str() — формат вывода более предсказуем, и последующие парсеры надёжно обрабатывают ISO 8601.

Предупреждение:Если пропустить значения Decimal без форматирования, очень маленькие или очень большие числа могут отобразиться в научной нотации (например, 1.5E+7). Всегда форматируйте Decimal с явным f-строкой вроде f"{value:.2f}" при записи финансовых данных в CSV.

Альтернативный шаблон для конвейеров со многими пользовательскими типами — расширить json.JSONEncoder. Создайте подкласс, переопределите метод default() для возврата JSON-совместимого значения для каждого пользовательского типа, затем передайте подкласс как аргумент cls в json.dumps(). Повторное кодирование через пользовательский энкодер перед записью в CSV нормализует все типы за один шаг без вызова prepare_row() для каждой строки. Шаблон prepare_row() проще для разовых скриптов; подход с подклассом JSONEncoder лучше масштабируется, когда одна и та же доменная модель с пользовательскими типами используется в нескольких стадиях конвейера или микросервисах.

Справочник параметров csv.DictWriter

Полная сигнатура конструктора: csv.DictWriter(f, fieldnames, restval="", extrasaction="raise", dialect="excel", **fmtparams). У большинства параметров разумные значения по умолчанию. Те, которые вы будете менять: fieldnames, delimiter и extrasaction.

Параметр
Тип
По умолчанию
Описание
f
file object
(обязательный)
Любой объект с методом write() — как правило, результат open()
fieldnames
sequence
(обязательный)
Список ключей, определяющий порядок столбцов в CSV-выводе
restval
str
""
Значение, записываемое когда в словаре отсутствует ключ из fieldnames
extrasaction
str
"raise"
"raise" вызывает ValueError для лишних ключей; "ignore" молча их игнорирует
dialect
str / Dialect
"excel"
Предопределённые правила форматирования — "excel", "excel-tab" или "unix"
delimiter
str
","
Одиночный символ-разделитель полей — используйте "\t" для TSV-вывода
quotechar
str
"
Символ для заключения в кавычки полей, содержащих разделитель
quoting
int
csv.QUOTE_MINIMAL
Управляет применением кавычек — MINIMAL, ALL, NONNUMERIC, NONE
lineterminator
str
"\r\n"
Строка, добавляемая после каждой строки — замените на "\n" для Unix-стиля

pandas — конвертация JSON в CSV через DataFrames

Если вы уже работаете в кодовой базе, активно использующей pandas, или ваш JSON содержит вложенные объекты, которые нужно выравнивать, подход с pandas требует значительно меньше кода, чем версия на stdlib. Компромисс: pandas — это зависимость ~30 МБ. Для одноразового скрипта это нормально. Для Docker-образа, который вы отправляете в production, подход на stdlib держит вещи легче.

Python 3.8+ — pandas read_json затем to_csv
import pandas as pd

# Читаем JSON-массив напрямую в DataFrame
df = pd.read_json("warehouse_inventory.json")

# Записываем в CSV — index=False предотвращает автогенерацию номеров строк
df.to_csv("warehouse_inventory.csv", index=False)

# Вот и всё. Две строки. pandas автоматически определяет типы столбцов.

Флаг index=False — одна из тех вещей, которую ищешь каждый раз. Без него pandas записывает столбец 0, 1, 2, ... как первый столбец CSV. Никому это не нужно.

Выравнивание вложенного JSON с json_normalize

Реальные ответы API редко бывают плоскими. Заказы содержат адреса доставки, пользователи — вложенные настройки, события телеметрии — вложенные метаданные. pd.json_normalize() обходит вложенные словари и выравнивает их в столбцы с именами через точку.

Python 3.8+ — выравнивание вложенного JSON с json_normalize
import json
import pandas as pd

api_response = """
[
  {
    "order_id": "ord_91a3",
    "placed_at": "2026-03-15T09:30:00Z",
    "customer": {
      "name": "Sarah Chen",
      "email": "s.chen@example.com",
      "tier": "premium"
    },
    "shipping": {
      "method": "express",
      "address": {
        "city": "Portland",
        "state": "OR",
        "zip": "97201"
      }
    },
    "total": 299.95
  },
  {
    "order_id": "ord_b7f2",
    "placed_at": "2026-03-15T14:12:00Z",
    "customer": {
      "name": "James Park",
      "email": "j.park@example.com",
      "tier": "standard"
    },
    "shipping": {
      "method": "standard",
      "address": {
        "city": "Austin",
        "state": "TX",
        "zip": "73301"
      }
    },
    "total": 87.50
  }
]
"""

orders = json.loads(api_response)

# json_normalize выравнивает вложенные словари — sep управляет разделителем
df = pd.json_normalize(orders, sep="_")
df.to_csv("flat_orders.csv", index=False)

# Результирующие столбцы:
# order_id, placed_at, customer_name, customer_email, customer_tier,
# shipping_method, shipping_address_city, shipping_address_state,
# shipping_address_zip, total

Параметр sep="_" управляет тем, как объединяются вложенные имена ключей. По умолчанию используется ".", что даёт столбцы вроде customer.name. Я предпочитаю нижнее подчёркивание, потому что точки в именах столбцов вызывают проблемы при импорте в SQL и в некоторых формулах электронных таблиц.

Для ответов API, оборачивающих массив записей под вложенным ключом, используйте параметр record_path. Если ответ выглядит как {"data": {"orders": [...]}}, передайте record_path=["data", "orders"] для навигации к нужному списку. Необязательный параметр meta позволяет извлекать поля родительского уровня рядом с вложенными записями — полезно, когда ответ включает информацию о пагинации верхнего уровня (номер страницы, общее количество), которую нужно добавить как столбец в каждую строку. Вместе record_path и meta обрабатывают большинство реальных форм вложенных ответов API без предварительной обработки.

Справочник параметров DataFrame.to_csv()

DataFrame.to_csv() имеет более 20 параметров. Вот те, что важны для задач JSON-в-CSV.

Параметр
Тип
По умолчанию
Описание
path_or_buf
str / Path / None
None
Путь к файлу или буфер — None возвращает CSV как строку
sep
str
","
Разделитель полей — используйте "\t" для TSV
index
bool
True
Записывать индекс строки как первый столбец — почти всегда устанавливают False
columns
list
None
Выбор и переупорядочивание столбцов в выводе
header
bool / list
True
Записывать имена столбцов — установите False при дозаписи в существующий файл
encoding
str
"utf-8"
Кодировка вывода — используйте "utf-8-sig" для совместимости с Excel на Windows
na_rep
str
""
Строковое представление пропущенных значений (NaN, None)
quoting
int
csv.QUOTE_MINIMAL
Управляет заключением полей в кавычки
Python 3.8+ — to_csv с часто используемыми параметрами
import pandas as pd

df = pd.read_json("telemetry_events.json")

# TSV-вывод с явной кодировкой и обработкой пропущенных значений
df.to_csv(
    "telemetry_events.tsv",
    sep="\t",
    index=False,
    encoding="utf-8",
    na_rep="NULL",
    columns=["event_id", "timestamp", "source", "severity", "message"],
)

# Вывод в stdout для перенаправления в shell-скриптах
print(df.to_csv(index=False))

# Возврат как строки (файл не записывается)
csv_string = df.to_csv(index=False)
print(len(csv_string), "символов")

Конвертация JSON из файла и ответа API

Два наиболее распространённых реальных сценария: чтение JSON из файла на диске и конвертация, или получение JSON из HTTP API и сохранение результата как CSV. В разработке можно обойтись без обработки ошибок. В production этот выбор превращается в оповещение в 2 часа ночи. Файлы могут не существовать, API могут возвращать коды 4xx или 5xx вместо JSON, тело ответа может быть объектом ошибки, а не массивом, или JSON может быть усечён из-за таймаута сети. Приведённые ниже шаблоны явно обрабатывают все эти случаи, логируют ошибки в stderr и возвращают количество строк, чтобы вызывающие могли обнаружить нулевой вывод и отправить оповещение.

Файл на диске — чтение, конвертация, сохранение

Python 3.8+ — конвертация JSON-файла в CSV с обработкой ошибок
import json
import csv
import sys

def json_file_to_csv(input_path: str, output_path: str) -> int:
    """Конвертирует JSON-файл с массивом объектов в CSV.
    Возвращает количество записанных строк.
    """
    try:
        with open(input_path, encoding="utf-8") as jf:
            data = json.load(jf)
    except FileNotFoundError:
        print(f"Error: {input_path} not found", file=sys.stderr)
        return 0
    except json.JSONDecodeError as exc:
        print(f"Error: invalid JSON in {input_path}: {exc.msg} at line {exc.lineno}", file=sys.stderr)
        return 0

    if not isinstance(data, list) or not data:
        print(f"Error: expected a non-empty JSON array in {input_path}", file=sys.stderr)
        return 0

    # Собираем все уникальные ключи из всех записей — для непоследовательных схем
    all_keys: list[str] = []
    seen: set[str] = set()
    for record in data:
        for key in record:
            if key not in seen:
                all_keys.append(key)
                seen.add(key)

    with open(output_path, "w", newline="", encoding="utf-8") as cf:
        writer = csv.DictWriter(cf, fieldnames=all_keys, restval="", extrasaction="ignore")
        writer.writeheader()
        writer.writerows(data)

    return len(data)

rows = json_file_to_csv("deploy_logs.json", "deploy_logs.csv")
print(f"Wrote {rows} rows to deploy_logs.csv")

Ответ HTTP API — получение и конвертация

Python 3.8+ — получение JSON из API и сохранение как CSV
import json
import csv
import urllib.request
import urllib.error

def api_response_to_csv(url: str, output_path: str) -> int:
    """Получает JSON из REST API и записывает его как CSV."""
    try:
        req = urllib.request.Request(url, headers={"Accept": "application/json"})
        with urllib.request.urlopen(req, timeout=30) as resp:
            if resp.status != 200:
                print(f"Error: API returned status {resp.status}")
                return 0
            body = resp.read().decode("utf-8")
    except urllib.error.URLError as exc:
        print(f"Error: could not reach {url}: {exc.reason}")
        return 0

    try:
        records = json.loads(body)
    except json.JSONDecodeError as exc:
        print(f"Error: API returned invalid JSON: {exc.msg}")
        return 0

    if not isinstance(records, list) or not records:
        print("Error: expected a non-empty JSON array from the API")
        return 0

    with open(output_path, "w", newline="", encoding="utf-8") as cf:
        writer = csv.DictWriter(cf, fieldnames=records[0].keys())
        writer.writeheader()
        writer.writerows(records)

    return len(records)

rows = api_response_to_csv(
    "https://api.internal.example.com/v2/deployments?status=completed",
    "completed_deployments.csv",
)
print(f"Exported {rows} deployments to CSV")
Примечание:Пример выше использует urllib из стандартной библиотеки, чтобы скрипт не зависел от внешних пакетов. Если у вас установлен requests, замените раздел с urllib на resp = requests.get(url, timeout=30); records = resp.json() — остальной код записи CSV остаётся идентичным.

Конвертация JSON в CSV из командной строки

Иногда нужен просто однострочник в терминале. Флаг -c Python позволяет выполнить быструю конвертацию без создания файла скрипта. Для более сложных преобразований сначала используйте jq для преобразования данных, затем конвертируйте.

bash — однострочная конвертация json в csv
# Python однострочник: читает JSON из stdin, записывает CSV в stdout
cat orders.json | python3 -c "
import json, csv, sys
data = json.load(sys.stdin)
w = csv.DictWriter(sys.stdout, fieldnames=data[0].keys())
w.writeheader()
w.writerows(data)
"

# Сохранение вывода в файл
cat orders.json | python3 -c "
import json, csv, sys
data = json.load(sys.stdin)
w = csv.DictWriter(sys.stdout, fieldnames=data[0].keys())
w.writeheader()
w.writerows(data)
" > orders.csv
bash — самодостаточный CLI-скрипт с argparse
# Сохраните как json2csv.py и запустите: python3 json2csv.py input.json -o output.csv
python3 -c "
import json, csv, argparse, sys

parser = argparse.ArgumentParser(description='Convert JSON array to CSV')
parser.add_argument('input', help='Path to JSON file')
parser.add_argument('-o', '--output', default=None, help='Output CSV path (default: stdout)')
parser.add_argument('-d', '--delimiter', default=',', help='CSV delimiter')
args = parser.parse_args()

with open(args.input) as f:
    data = json.load(f)

out = open(args.output, 'w', newline='') if args.output else sys.stdout
writer = csv.DictWriter(out, fieldnames=data[0].keys(), delimiter=args.delimiter)
writer.writeheader()
writer.writerows(data)
if args.output:
    out.close()
    print(f'Wrote {len(data)} rows to {args.output}', file=sys.stderr)
" "$@"
bash — использование jq + csvkit для сложных преобразований
# Установка csvkit: pip install csvkit

# jq выбирает поля, in2csv обрабатывает форматирование CSV
cat api_response.json | jq '[.[] | {id: .order_id, customer: .customer.name, total}]' | in2csv -f json > orders.csv

# Miller (mlr) — ещё один вариант для JSON-в-CSV
mlr --json2csv cat orders.json > orders.csv

Miller (mlr) — отдельный бинарник, который обрабатывает JSON, CSV и TSV как форматы первого класса без необходимости в Python. Флаг --json2csv конвертирует JSON-ввод в CSV за один проход, и можно объединять команды Miller для фильтрации, сортировки или переименования столбцов в той же команде. Устанавливается через Homebrew на macOS (brew install miller) или через менеджер пакетов Linux. Особенно удобен в CI-конвейерах, где нужна быстрая конвертация JSON в CSV без запуска Python-окружения.

Высокопроизводительная альтернатива — pandas с pyarrow

Для наборов данных в десятки миллионов строк pandas с бэкендом pyarrow читает и записывает значительно быстрее, чем по умолчанию. Движок Arrow на основе C обрабатывает столбчатые данные эффективнее, чем построчный модуль csv Python. API остаётся тем же — нужно просто задать параметр engine.

bash — установка pyarrow
pip install pyarrow
Python 3.8+ — pandas с pyarrow для более быстрой записи CSV
import pandas as pd

# Чтение JSON с движком pyarrow (более быстрый разбор для больших файлов)
df = pd.read_json("sensor_readings.json", engine="pyarrow")

# to_csv не имеет параметра engine, но операции с DataFrame
# между чтением и записью выигрывают от столбчатой структуры pyarrow
df.to_csv("sensor_readings.csv", index=False)

# Для по-настоящему больших экспортов рассмотрите запись в Parquet вместо CSV
# — бинарный формат, в 5-10 раз меньше, сохраняет типы
df.to_parquet("sensor_readings.parquet", engine="pyarrow")

Если вы обрабатываете более нескольких сотен МБ JSON и конечный потребитель принимает Parquet, пропустите CSV полностью. Parquet меньше по размеру, сохраняет типы столбцов, и Redshift, и BigQuery загружают его нативно. CSV — формат с потерями: каждое значение становится строкой.

Вывод в терминал с подсветкой синтаксиса

Библиотека rich отображает таблицы с рамками, выравниванием и цветом в терминале — полезно для предпросмотра конвертации в процессе разработки без открытия выходного файла.

bash — установка rich
pip install rich
Python 3.8+ — предпросмотр CSV в терминале с rich
import json
from rich.console import Console
from rich.table import Table

json_string = """
[
  {"hostname": "web-prod-1", "cpu_percent": 72.3, "memory_mb": 3840, "uptime_hours": 720},
  {"hostname": "web-prod-2", "cpu_percent": 45.1, "memory_mb": 2560, "uptime_hours": 168},
  {"hostname": "db-replica-1", "cpu_percent": 91.7, "memory_mb": 7680, "uptime_hours": 2160}
]
"""

records = json.loads(json_string)
console = Console()

table = Table(title="Server Metrics Preview", show_lines=True)
for key in records[0]:
    table.add_column(key, style="cyan" if key == "hostname" else "white")

for row in records:
    table.add_row(*[str(v) for v in row.values()])

console.print(table)
# Отображает цветную таблицу с рамками в терминале
Предупреждение:Rich предназначен только для отображения в терминале. Не используйте его для генерации CSV-файлов — он добавляет ANSI escape-коды, которые повредят вывод. Записывайте в файлы через csv.DictWriter или DataFrame.to_csv(), и используйте rich только для предпросмотра.

Работа с большими JSON-файлами

json.load() читает весь файл в память. Для JSON-файла в 200 МБ это означает ~200 МБ сырого текста плюс накладные расходы на Python-объекты — легко 500 МБ+ потребления кучи. Для файлов свыше 100 МБ используйте потоковый ввод через ijson и записывайте CSV-строки по мере обработки.

bash — установка ijson
pip install ijson

Потоковая передача JSON-массива в CSV с ijson

Python 3.8+ — потоковая конвертация большого JSON-массива в CSV с постоянной памятью
import ijson
import csv

def stream_json_to_csv(json_path: str, csv_path: str) -> int:
    """Конвертирует большой JSON-массив в CSV без загрузки всего файла в память."""
    with open(json_path, "rb") as jf, open(csv_path, "w", newline="", encoding="utf-8") as cf:
        # ijson.items возвращает каждый элемент массива верхнего уровня по одному
        records = ijson.items(jf, "item")

        first_record = next(records)
        fieldnames = list(first_record.keys())

        writer = csv.DictWriter(cf, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerow(first_record)

        count = 1
        for record in records:
            writer.writerow(record)
            count += 1

    return count

rows = stream_json_to_csv("clickstream_2026_03.json", "clickstream_2026_03.csv")
print(f"Streamed {rows} records to CSV")

NDJSON / JSON Lines — по одному объекту на строку

NDJSON (Newline-Delimited JSON), также называемый JSON Lines или .jsonl, хранит один корректный JSON-объект на строку без оборачивающего массива. Этот формат распространён в конвейерах логов, потоках событий (Kafka, Kinesis) и массовых экспортах из сервисов вроде Elasticsearch и BigQuery. Поскольку каждая строка — самодостаточный JSON-объект, NDJSON-файл можно обрабатывать обычным циклом Python for по файловому дескриптору — библиотека ijson не нужна. Потребление памяти остаётся постоянным вне зависимости от размера файла — это простейший потоковый подход, когда исходные данные уже в формате JSON Lines.

Python 3.8+ — конвертация NDJSON в CSV построчно
import json
import csv

def ndjson_to_csv(ndjson_path: str, csv_path: str) -> int:
    """Конвертирует файл в формате newline-delimited JSON в CSV построчно."""
    with open(ndjson_path, encoding="utf-8") as nf:
        first_line = nf.readline()
        first_record = json.loads(first_line)
        fieldnames = list(first_record.keys())

        with open(csv_path, "w", newline="", encoding="utf-8") as cf:
            writer = csv.DictWriter(cf, fieldnames=fieldnames)
            writer.writeheader()
            writer.writerow(first_record)

            count = 1
            for line in nf:
                line = line.strip()
                if not line:
                    continue
                try:
                    record = json.loads(line)
                    writer.writerow(record)
                    count += 1
                except json.JSONDecodeError:
                    continue  # пропускаем некорректные строки

    return count

rows = ndjson_to_csv("access_log.ndjson", "access_log.csv")
print(f"Converted {rows} log entries to CSV")
Примечание:Переключайтесь на потоковую обработку, когда JSON-файл превышает 100 МБ. Загрузка 1 ГБ JSON-массива через json.load() может потреблять 3–5 ГБ RAM из-за накладных расходов Python-объектов. С ijson потребление памяти остаётся постоянным вне зависимости от размера файла. Если нужно быстро конвертировать небольшой файл, вставьте его в конвертер JSON в CSV вместо этого.

Распространённые ошибки

Отсутствие newline='' в open() — пустые строки на Windows

Проблема: Модуль csv записывает окончания строк . Без newline='' текстовый режим Python добавляет ещё один на Windows, что даёт двойные пробелы в выводе.

Решение: Всегда передавайте newline='' при открытии файла для записи CSV. На macOS/Linux это безвредно.

Before · Python
After · Python
with open("output.csv", "w") as f:
    writer = csv.DictWriter(f, fieldnames=columns)
    writer.writeheader()
    writer.writerows(data)
# Пустые строки между каждой строкой данных на Windows
with open("output.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=columns)
    writer.writeheader()
    writer.writerows(data)
# Чистый вывод на всех платформах
Забытый index=False в pandas to_csv()

Проблема: Без index=False pandas добавляет автоинкрементный столбец номеров строк (0, 1, 2, ...), загрязняя CSV данными, которых не было в исходном JSON.

Решение: Передавайте index=False в to_csv(). Если столбец индекса действительно нужен, назовите его явно через df.index.name = 'row_num'.

Before · Python
After · Python
df = pd.read_json("events.json")
df.to_csv("events.csv")
# CSV получает лишний безымянный столбец: ,event_id,timestamp,...
# Ведущая запятая ломает многие CSV-парсеры
df = pd.read_json("events.json")
df.to_csv("events.csv", index=False)
# Чистый CSV: event_id,timestamp,...
Использование records[0].keys() при непоследовательных ключах

Проблема: Если JSON-объекты имеют разные ключи (некоторые записи содержат необязательные поля), использование ключей первой записи как fieldnames молча отбрасывает столбцы, появляющиеся только в последующих записях.

Решение: Собирайте все уникальные ключи из всех записей перед созданием DictWriter.

Before · Python
After · Python
records = json.load(f)
writer = csv.DictWriter(out, fieldnames=records[0].keys())
# Пропускает поле "discount", которое появляется только в records[2]
records = json.load(f)
all_keys = list(dict.fromkeys(k for r in records for k in r))
writer = csv.DictWriter(out, fieldnames=all_keys, restval="")
# Каждый ключ из каждой записи включается как столбец
Запись вложенных словарей напрямую в CSV без выравнивания

Проблема: csv.DictWriter вызывает str() для вложенных словарей, создавая столбцы со значениями вроде "{'city': 'Portland'}"— Python repr, а не реальные данные.

Решение: Сначала выравнивайте вложенные объекты с помощью pd.json_normalize() или пользовательской функции выравнивания.

Before · Python
After · Python
records = [{"id": "evt_1", "meta": {"source": "web", "region": "us-west"}}]
writer = csv.DictWriter(f, fieldnames=["id", "meta"])
writer.writerows(records)
# столбец meta содержит: {'source': 'web', 'region': 'us-west'}
import pandas as pd
records = [{"id": "evt_1", "meta": {"source": "web", "region": "us-west"}}]
df = pd.json_normalize(records, sep="_")
df.to_csv("events.csv", index=False)
# Столбцы: id, meta_source, meta_region

csv.DictWriter против pandas — краткое сравнение

Метод
Вложенный JSON
Пользовательские типы
Потоковая обработка
Зависимости
Требует установки
csv.DictWriter
✗ (ручное выравнивание)
✓ (построчно)
Нет
Нет (stdlib)
csv.writer
✓ (построчно)
Нет
Нет (stdlib)
pd.DataFrame.to_csv()
✗ (только плоский)
✓ (через dtypes)
pandas + numpy
pip install
pd.json_normalize() + to_csv()
✓ (через dtypes)
pandas + numpy
pip install
csv.writer + json_flatten
flatten_json
pip install
jq + csvkit (CLI)
✓ (через jq)
N/A
jq, csvkit
Системная установка

Используйте csv.DictWriter когда нужны нулевые зависимости, JSON плоский, и скрипт работает в ограниченном окружении (CI-контейнеры, Lambda-функции, встроенный Python). Используйте pd.json_normalize() + to_csv() когда JSON вложенный, нужно преобразовать или отфильтровать данные перед экспортом, или вы уже работаете в pandas-окружении. Для файлов, не помещающихся в память, комбинируйте ijson с csv.DictWriter для потоковой обработки с постоянным потреблением памяти.

Для быстрых конвертаций без написания кода конвертер JSON в CSV на ToolDeck справится без настройки Python.

Часто задаваемые вопросы

Как конвертировать JSON в CSV на Python без pandas?

Используйте встроенные модули json и csv. Вызовите json.load() для разбора JSON-файла в список словарей, извлеките fieldnames из ключей первого словаря, создайте csv.DictWriter, вызовите writeheader(), затем writerows(). Этот подход не требует внешних зависимостей и работает в любом окружении Python 3.x. Также он быстрее pandas для небольших файлов, поскольку отсутствуют накладные расходы на создание DataFrame. Если JSON-объекты имеют непоследовательные ключи в разных записях, сначала соберите все уникальные ключи с помощью dict.fromkeys(k for r in records for k in r), прежде чем передавать их как fieldnames — иначе часть столбцов будет пропущена.

Python
import json
import csv

with open("orders.json") as f:
    records = json.load(f)

with open("orders.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=records[0].keys())
    writer.writeheader()
    writer.writerows(records)

Как обрабатывать вложенный JSON при конвертации в CSV?

Плоские JSON-массивы напрямую преобразуются в строки CSV, но вложенные объекты сначала нужно выравнивать. С pandas pd.json_normalize() делает это автоматически — он объединяет вложенные ключи через разделитель-точку (например, "address.city"). Без pandas напишите рекурсивную функцию, которая обходит словарь и конкатенирует ключи через разделитель. Для глубоко вложенных структур с несколькими уровнями json_normalize справляется за один проход. Параметр sep управляет символом-соединителем между сегментами ключей — нижнее подчёркивание обычно безопаснее стандартной точки для импорта в SQL и совместимости формул в электронных таблицах.

Python
import pandas as pd

nested_data = [
    {"id": "ord_91a3", "customer": {"name": "Анна Иванова", "email": "a.ivanova@primer.ru"}},
]
df = pd.json_normalize(nested_data, sep="_")
# Столбцы: id, customer_name, customer_email
df.to_csv("flat_orders.csv", index=False)

Почему в моём CSV появляются пустые строки между строками данных на Windows?

Модуль csv по умолчанию записывает окончания строк \r\n. На Windows открытие файла в текстовом режиме добавляет ещё один \r, что даёт \r\r\n — это отображается как пустая строка. Исправление: всегда передавайте newline="" в open(). Это указывает Python не преобразовывать окончания строк, позволяя модулю csv обрабатывать их самостоятельно. Этот шаблон обязателен независимо от операционной системы — на macOS и Linux он безвреден, а на Windows критически важен. Документация Python явно указывает на это в разделе модуля csv как правильный способ открытия файлов для записи CSV.

Python
# Неправильно — пустые строки на Windows
with open("output.csv", "w") as f:
    writer = csv.writer(f)

# Правильно — newline="" предотвращает двойной \r
with open("output.csv", "w", newline="") as f:
    writer = csv.writer(f)

Как дозаписать JSON-записи в существующий CSV-файл?

Откройте файл в режиме дозаписи ("a") и создайте DictWriter с теми же fieldnames. Пропустите writeheader(), поскольку строка заголовка уже существует. В pandas используйте to_csv(mode="a", header=False). Убедитесь, что порядок столбцов совпадает с существующим файлом, иначе данные окажутся не в тех столбцах. Если вы не уверены в порядке столбцов в существующем файле, сначала откройте его с помощью csv.DictReader и прочитайте fieldnames из его атрибута fieldnames, прежде чем создавать writer для дозаписи.

Python
import csv

new_records = [
    {"order_id": "ord_f4c1", "total": 89.50, "status": "shipped"},
]

with open("orders.csv", "a", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["order_id", "total", "status"])
    writer.writerows(new_records)

Какой самый быстрый способ конвертировать большой JSON-файл в CSV на Python?

Для файлов до 500 МБ pd.read_json() с последующим to_csv() — самый быстрый однострочный подход: pandas использует оптимизированный код на C внутри. Для файлов свыше 500 МБ используйте ijson для потоковой обработки JSON-записей и записывайте их в CSV с помощью csv.DictWriter построчно. Это поддерживает постоянное использование памяти вне зависимости от размера файла. Для NDJSON-файлов (по одному JSON-объекту на строку) ijson вообще не нужен — обычный цикл Python for по файловому дескриптору обрабатывает каждую строку независимо и обеспечивает постоянную память без сторонних библиотек.

Python
# Быстро для файлов, помещающихся в память
import pandas as pd
df = pd.read_json("large_dataset.json")
df.to_csv("large_dataset.csv", index=False)

# Потоковая обработка для файлов, не помещающихся в память
import ijson, csv
with open("huge.json", "rb") as jf, open("huge.csv", "w", newline="") as cf:
    records = ijson.items(jf, "item")
    first = next(records)
    writer = csv.DictWriter(cf, fieldnames=first.keys())
    writer.writeheader()
    writer.writerow(first)
    for record in records:
        writer.writerow(record)

Можно ли записывать вывод CSV в stdout вместо файла на Python?

Да. Передайте sys.stdout как файловый объект в csv.writer() или csv.DictWriter(). Это полезно для перенаправления вывода в shell-скриптах или быстрой отладки. В pandas вызовите to_csv(sys.stdout, index=False) или to_csv(None), чтобы получить строку для вывода. Временный файл не нужен. При записи в stdout на Windows сначала вызовите sys.stdout.reconfigure(newline=""), чтобы избежать проблемы двойного возврата каретки, поскольку stdout по умолчанию открывается в текстовом режиме.

Python
import csv
import sys
import json

data = json.loads('[{"host":"web-1","cpu":72.3},{"host":"web-2","cpu":45.1}]')
writer = csv.DictWriter(sys.stdout, fieldnames=data[0].keys())
writer.writeheader()
writer.writerows(data)
# host,cpu
# web-1,72.3
# web-2,45.1

Связанные инструменты

MS
Maria SantosBackend Developer

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.

PS
Priya SharmaТехнический рецензент

Priya is a data scientist and machine learning engineer who has worked across the full Python data stack — from raw data ingestion and cleaning to model deployment and monitoring. She is passionate about reproducible research, Jupyter-based workflows, and the practical engineering side of ML. She writes about NumPy, Pandas, data serialisation, and the Python patterns that make data pipelines reliable at scale.