Как прочитать JSON в pandas

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем это нужно

JSON — формат номер один для данных от API, event logs, NoSQL баз. Выгружаете из Amplitude / Mixpanel / Yandex Metrika → получаете JSON. API партнёра → JSON. Webhook-лог → JSON. Чтобы работать с этим в pandas — нужно уметь правильно парсить, учитывая разные orient-форматы и вложенные структуры.

Вложенный JSON — отдельная история. В колонке payload лежит структура {"user": {"name": ..., "email": ...}, "items": [...]}. Обычный read_json оставит это как dict в колонке — неудобно для анализа. json_normalize разворачивает вложенность в плоский DataFrame с колонками user.name, user.email и т.д.

В статье — все частые случаи:

  • read_json с разными orient (records, columns, index, values)
  • NDJSON (JSON Lines) — формат logs
  • json_normalize для вложенных структур и массивов
  • Парсинг JSON-колонки в существующем DataFrame
  • Из API через requests.get().json()
  • Чтение больших файлов чанками
  • Работа с сложными вложенными структурами через json.load

Базовое чтение

import pandas as pd

# из файла
df = pd.read_json('data.json')

# из строки
df = pd.read_json('{"a": [1, 2, 3], "b": [4, 5, 6]}')

1. Orient (ориентация JSON)

JSON может быть по-разному структурирован. pandas понимает разные варианты через параметр orient.

records (самое частое)

[
    {"name": "Alice", "age": 30},
    {"name": "Bob",   "age": 25}
]
df = pd.read_json('data.json', orient='records')

Это default, если JSON — массив объектов.

columns

{
    "name": ["Alice", "Bob"],
    "age":  [30, 25]
}
df = pd.read_json('data.json', orient='columns')

index

{
    "0": {"name": "Alice", "age": 30},
    "1": {"name": "Bob",   "age": 25}
}
df = pd.read_json('data.json', orient='index')

values

[[1, 2], [3, 4]]
df = pd.read_json('data.json', orient='values')

2. NDJSON / JSON Lines

Когда каждая строка файла — отдельный JSON-объект:

{"name": "Alice", "age": 30}
{"name": "Bob",   "age": 25}
{"name": "Carol", "age": 35}

Читается так:

df = pd.read_json('data.ndjson', lines=True)

Популярный формат для логов и big data (каждая строка можно обработать отдельно).

3. Вложенный JSON — json_normalize

Для структур с вложенностью:

{
    "data": [
        {
            "id": 1,
            "user": {
                "name": "Alice",
                "email": "alice@example.com"
            },
            "amount": 100
        },
        ...
    ]
}
import json

with open('data.json') as f:
    raw = json.load(f)

df = pd.json_normalize(raw['data'])
# колонки: id, user.name, user.email, amount

4. json_normalize с record_path

Если вложенные массивы нужно «развернуть»:

[
    {
        "order_id": 1,
        "items": [
            {"product": "A", "qty": 2},
            {"product": "B", "qty": 1}
        ]
    }
]
df = pd.json_normalize(
    data,
    record_path='items',
    meta=['order_id']
)
# колонки: product, qty, order_id

5. Из API

import requests

r = requests.get('https://api.example.com/users')
data = r.json()

df = pd.DataFrame(data)
# или
df = pd.json_normalize(data)

6. Из столбца с JSON в DataFrame

Если у вас DataFrame, где одна колонка — JSON-строка:

import json

df['parsed'] = df['json_col'].apply(json.loads)

# извлечь поле
df['user_name'] = df['parsed'].apply(lambda d: d.get('user', {}).get('name'))

# или через json_normalize
parsed = pd.json_normalize(df['parsed'])
df = pd.concat([df, parsed], axis=1)

7. Из больших файлов — чанками

# chunksize работает только с lines=True
chunks = pd.read_json('big.ndjson', lines=True, chunksize=10000)

for chunk in chunks:
    process(chunk)

8. Обработка ошибок

try:
    df = pd.read_json('data.json')
except ValueError as e:
    print(f"JSON parse error: {e}")

Частые причины:

  • Плохой JSON (неверные кавычки, trailing comma)
  • Разная структура в строках
  • Encoding (не UTF-8)

9. Гибкость: сначала json.load, потом DataFrame

Когда структура сложная — парсить отдельно:

import json

with open('complex.json') as f:
    data = json.load(f)

# своя логика извлечения
rows = []
for item in data:
    rows.append({
        'id': item['id'],
        'user_name': item['user']['name'],
        'total': sum(i['price'] for i in item['items'])
    })

df = pd.DataFrame(rows)

10. Сохранение обратно

df.to_json('output.json', orient='records', force_ascii=False)

# с красивым форматированием
df.to_json('output.json', orient='records', indent=2, force_ascii=False)

# NDJSON
df.to_json('output.ndjson', orient='records', lines=True, force_ascii=False)

force_ascii=False для корректных русских букв.

Частые ошибки

1. Wrong orient

# падает с ошибкой
pd.read_json('records-style.json')  # default orient='columns'

Правильно:

pd.read_json('records-style.json', orient='records')

2. Вложенные данные не развернулись

read_json может оставить dict'ы в колонках. Используйте json_normalize.

3. lines=True для обычного JSON

# падает
pd.read_json('normal.json', lines=True)  # ожидает NDJSON

4. Кодировка

JSON должен быть UTF-8. Если ломается:

with open('data.json', encoding='cp1251') as f:
    data = json.load(f)
df = pd.DataFrame(data)

Связанные темы

FAQ

read_json или json.load?

read_json для плоских структур. json.load + DataFrame для сложной обработки.

json_normalize vs apply?

json_normalize быстрее и чище. apply — для custom логики.

Что такое NDJSON?

Newline-delimited JSON. Каждая строка = отдельный JSON. Популярно для логов и big data.

Как обработать массив вложенных объектов?

pd.json_normalize(data, record_path='array_field', meta=['parent_field']).


Тренируйте pandas — откройте тренажёр с 1500+ вопросами для собесов.