JSON в CSV на Python — Примеры DictWriter и pandas
Используйте бесплатный 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 плоский — каждое значение находится в ячейке на пересечении строки и столбца. Преобразование работает чисто, когда все объекты имеют одинаковый набор ключей верхнего уровня. Вложенные объекты, массивы и непоследовательные ключи делают задачу интереснее. Исходные данные остаются идентичными; меняется только структура.
[{"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 управляет как порядком столбцов, так и тем, какие ключи включаются.
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) при чтении из файлового дескриптора. Это постоянно сбивает людей с толку — одна функция читает строку, другая — файловый объект.
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 требуют явного форматирования, чтобы избежать научной нотации.
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.
pandas — конвертация JSON в CSV через DataFrames
Если вы уже работаете в кодовой базе, активно использующей pandas, или ваш JSON содержит вложенные объекты, которые нужно выравнивать, подход с pandas требует значительно меньше кода, чем версия на stdlib. Компромисс: pandas — это зависимость ~30 МБ. Для одноразового скрипта это нормально. Для Docker-образа, который вы отправляете в production, подход на stdlib держит вещи легче.
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() обходит вложенные словари и выравнивает их в столбцы с именами через точку.
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.
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 и возвращают количество строк, чтобы вызывающие могли обнаружить нулевой вывод и отправить оповещение.
Файл на диске — чтение, конвертация, сохранение
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 — получение и конвертация
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 для преобразования данных, затем конвертируйте.
# 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
# Сохраните как 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)
" "$@"# Установка 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.csvMiller (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.
pip install pyarrow
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 отображает таблицы с рамками, выравниванием и цветом в терминале — полезно для предпросмотра конвертации в процессе разработки без открытия выходного файла.
pip install 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)
# Отображает цветную таблицу с рамками в терминалеcsv.DictWriter или DataFrame.to_csv(), и используйте rich только для предпросмотра.Работа с большими JSON-файлами
json.load() читает весь файл в память. Для JSON-файла в 200 МБ это означает ~200 МБ сырого текста плюс накладные расходы на Python-объекты — легко 500 МБ+ потребления кучи. Для файлов свыше 100 МБ используйте потоковый ввод через ijson и записывайте CSV-строки по мере обработки.
pip install ijson
Потоковая передача JSON-массива в CSV с ijson
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.
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.load() может потреблять 3–5 ГБ RAM из-за накладных расходов Python-объектов. С ijson потребление памяти остаётся постоянным вне зависимости от размера файла. Если нужно быстро конвертировать небольшой файл, вставьте его в конвертер JSON в CSV вместо этого.Распространённые ошибки
Проблема: Модуль csv записывает окончания строк . Без newline='' текстовый режим Python добавляет ещё один на Windows, что даёт двойные пробелы в выводе.
Решение: Всегда передавайте newline='' при открытии файла для записи CSV. На macOS/Linux это безвредно.
with open("output.csv", "w") as f:
writer = csv.DictWriter(f, fieldnames=columns)
writer.writeheader()
writer.writerows(data)
# Пустые строки между каждой строкой данных на Windowswith open("output.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=columns)
writer.writeheader()
writer.writerows(data)
# Чистый вывод на всех платформахПроблема: Без index=False pandas добавляет автоинкрементный столбец номеров строк (0, 1, 2, ...), загрязняя CSV данными, которых не было в исходном JSON.
Решение: Передавайте index=False в to_csv(). Если столбец индекса действительно нужен, назовите его явно через df.index.name = 'row_num'.
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,...Проблема: Если JSON-объекты имеют разные ключи (некоторые записи содержат необязательные поля), использование ключей первой записи как fieldnames молча отбрасывает столбцы, появляющиеся только в последующих записях.
Решение: Собирайте все уникальные ключи из всех записей перед созданием DictWriter.
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.DictWriter вызывает str() для вложенных словарей, создавая столбцы со значениями вроде "{'city': 'Portland'}"— Python repr, а не реальные данные.
Решение: Сначала выравнивайте вложенные объекты с помощью pd.json_normalize() или пользовательской функции выравнивания.
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_regioncsv.DictWriter против pandas — краткое сравнение
Используйте 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 — иначе часть столбцов будет пропущена.
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 и совместимости формул в электронных таблицах.
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.
# Неправильно — пустые строки на 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 для дозаписи.
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 по файловому дескриптору обрабатывает каждую строку независимо и обеспечивает постоянную память без сторонних библиотек.
# Быстро для файлов, помещающихся в память
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 по умолчанию открывается в текстовом режиме.
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Связанные инструменты
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.
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.