JSON в Python — парсинг, создание и работа с файлами
Коротко
JSON (JavaScript Object Notation) — текстовый формат обмена данными. В Python модуль json из стандартной библиотеки конвертирует JSON в словари и обратно. Аналитику JSON нужен постоянно: API-ответы, конфиги, логи, выгрузки из систем аналитики. На собеседованиях спрашивают про парсинг JSON, обработку вложенных структур и разницу между loads/dumps и load/dump.
Базовые операции
import json
# JSON-строка → Python-объект
data = json.loads('{"name": "Иван", "age": 28}')
print(data) # {'name': 'Иван', 'age': 28}
print(type(data)) # <class 'dict'>
print(data['name']) # Иван
# Python-объект → JSON-строка
json_str = json.dumps(data)
print(json_str) # {"name": "\u0418\u0432\u0430\u043d", "age": 28}
# С кириллицей и отступами
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)
# {
# "name": "Иван",
# "age": 28
# }json.loads(string)— parse JSON-строки (load string)json.dumps(object)— создать JSON-строку (dump string)ensure_ascii=False— не экранировать кириллицуindent=2— форматирование с отступами
Чтение и запись файлов
import json
# Чтение JSON-файла
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
# Запись в JSON-файл
with open('output.json', 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)json.load(file)— читает из файлового объектаjson.dump(object, file)— пишет в файловый объект
Запомните: с s на конце (loads, dumps) — работа со строками. Без s (load, dump) — работа с файлами.
Типы данных: JSON ↔ Python
| JSON | Python |
|---|---|
object {} |
dict |
array [] |
list |
string "" |
str |
| number (int) | int |
| number (float) | float |
| true / false | True / False |
| null | None |
data = json.loads('''
{
"users": [
{"id": 1, "active": true, "score": null},
{"id": 2, "active": false, "score": 95.5}
],
"total": 2
}
''')
print(data['users'][0]['active']) # True (bool)
print(data['users'][0]['score']) # None
print(data['total']) # 2 (int)JSON true → Python True, null → None. Обратное преобразование работает автоматически.
Вложенные структуры
API часто возвращают глубоко вложенные JSON. Навигация по ним — базовый навык аналитика.
response = {
"data": {
"users": [
{
"id": 1,
"profile": {
"name": "Анна",
"contacts": {"email": "anna@example.com"}
},
"orders": [
{"id": 101, "amount": 5000},
{"id": 102, "amount": 3200}
]
}
]
},
"meta": {"page": 1, "total": 100}
}
# Навигация по вложенным ключам
user = response['data']['users'][0]
email = user['profile']['contacts']['email']
first_order = user['orders'][0]['amount']
# Безопасный доступ через .get()
phone = user['profile']['contacts'].get('phone', 'не указан').get(key, default) — не выбросит KeyError, если ключа нет. Для глубоко вложенных структур это безопаснее прямого доступа.
Работа с API-ответами
import json
import urllib.request
url = 'https://api.example.com/users?page=1'
req = urllib.request.Request(url)
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode('utf-8'))
# Или с библиотекой requests (рекомендуется)
import requests
response = requests.get('https://api.example.com/users', params={'page': 1})
data = response.json() # автоматический парсингresponse.json() в requests — шорткат для json.loads(response.text).
JSON в pandas
import pandas as pd
# Из JSON-файла
df = pd.read_json('data.json')
# Из JSON-строки
json_str = '[{"name": "Иван", "age": 28}, {"name": "Анна", "age": 32}]'
df = pd.read_json(json_str)
# Из списка словарей (после парсинга API)
data = [
{'user_id': 1, 'revenue': 5000},
{'user_id': 2, 'revenue': 3200}
]
df = pd.DataFrame(data)
# DataFrame → JSON
df.to_json('output.json', orient='records', force_ascii=False)Для вложенных JSON используйте pd.json_normalize():
data = [
{'id': 1, 'profile': {'name': 'Иван', 'city': 'Москва'}, 'score': 85},
{'id': 2, 'profile': {'name': 'Анна', 'city': 'Питер'}, 'score': 92}
]
df = pd.json_normalize(data)
print(df.columns)
# Index(['id', 'score', 'profile.name', 'profile.city'])json_normalize «разворачивает» вложенные словари в плоские столбцы — то, что нужно для анализа.
JSON Lines (JSONL)
Формат, где каждая строка файла — отдельный JSON-объект. Часто используется для логов и стриминга данных.
# Чтение JSONL
events = []
with open('events.jsonl', 'r') as f:
for line in f:
events.append(json.loads(line))
# Запись JSONL
with open('output.jsonl', 'w') as f:
for event in events:
f.write(json.dumps(event, ensure_ascii=False) + '\n')
# Через pandas
df = pd.read_json('events.jsonl', lines=True)
df.to_json('output.jsonl', orient='records', lines=True)Параметр lines=True в pandas — для чтения и записи JSONL.
Обработка ошибок
import json
# Невалидный JSON
try:
data = json.loads('{"name": "broken}')
except json.JSONDecodeError as e:
print(f'Ошибка парсинга: {e}')
# Ошибка парсинга: Unterminated string starting at: line 1 column 10
# Несериализуемый объект
from datetime import datetime
data = {'created_at': datetime.now()}
try:
json.dumps(data)
except TypeError as e:
print(f'Ошибка: {e}')
# Object of type datetime is not JSON serializabledatetime, set, bytes — не сериализуются в JSON. Решение — кастомный сериализатор:
def custom_serializer(obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, set):
return list(obj)
raise TypeError(f'Not serializable: {type(obj)}')
data = {'created_at': datetime.now(), 'tags': {'sql', 'python'}}
json_str = json.dumps(data, default=custom_serializer, ensure_ascii=False)Типичные ошибки
Путают loads/dumps и load/dump. С s — строки, без s — файлы. json.load(string) выбросит ошибку — нужен файловый объект.
Забывают ensure_ascii=False. По умолчанию кириллица экранируется в \uXXXX. Для читаемого вывода — ensure_ascii=False.
Одинарные кавычки. JSON требует двойных кавычек. {'key': 'value'} — невалидный JSON. {"key": "value"} — валидный. Python словари используют любые кавычки, JSON — только двойные.
Trailing comma. {"a": 1, "b": 2,} — невалидный JSON (висячая запятая). В Python dict это допустимо, в JSON — нет.
Вопросы с собеседований
-- Чем отличается json.loads от json.load?
-- loads (load string) принимает строку, load принимает файловый объект. Аналогично dumps и dump для сериализации.
-- Как обработать вложенный JSON в pandas?
-- pd.json_normalize(data) разворачивает вложенные словари в плоские столбцы. Для массивов внутри объектов можно указать record_path и meta.
-- Что делать, если в JSON есть datetime?
-- JSON не поддерживает datetime нативно. При сериализации передать default-функцию, которая конвертирует в ISO-формат. При десериализации — парсить строку обратно в datetime.
-- Как прочитать JSONL-файл?
-- Построчно: for line in f: json.loads(line). Через pandas: pd.read_json('file.jsonl', lines=True). JSONL — каждая строка файла — отдельный JSON-объект.
Потренируйтесь решать задачи — откройте тренажёр с 1500+ вопросами для подготовки к собеседованиям аналитиков.
FAQ
JSON vs CSV — когда что использовать?
CSV — для табличных данных с фиксированной структурой. JSON — для вложенных, иерархических данных и API-ответов. CSV компактнее и быстрее читается pandas. JSON гибче и поддерживает любую вложенность.
Как работать с большими JSON-файлами?
Для файлов, не помещающихся в память, используйте потоковый парсинг (ijson библиотека) или JSONL-формат с построчным чтением. В pandas — pd.read_json('file.jsonl', lines=True, chunksize=1000).
JSON в SQL — это возможно?
Да. PostgreSQL, MySQL 5.7+, ClickHouse поддерживают тип JSONB/JSON. Можно хранить и запрашивать JSON прямо в БД: SELECT data->>'name' FROM users.
Как тренироваться
Задачи на работу с JSON, API и структурами данных — в тренажёре Карьерник. Больше вопросов по Python — в разделе с примерами.