Форматы JSON и CSV в Python: чтение и запись данных
В прошлой главе мы разобрали файл настроек вручную, резая строки по знаку =. Для данных посложнее так не делают: есть готовые форматы, которые Python читает и пишет готовыми модулями.
Разберём два самых частых:
- JSON (JavaScript Object Notation) — формат обмена данными в вебе и API
- CSV (Comma-Separated Values) — табличный формат; открывается в Excel и Google Таблицах
JSON: JavaScript Object Notation
Что JSON умеет хранить
JSON придумывали как формат обмена между разными языками, поэтому набор типов в нём намеренно скромный, только то, что есть везде:
Соответствие почти один в один: только у числа в Python два варианта — целое становится int, дробное — float. На этом и держится удобство работы с JSON из Python.
Модуль json
В стандартной библиотеке для формата есть модуль json, и вся работа с ним сводится к двум действиям: превратить объект Python в JSON и разобрать JSON обратно в объект.
У модуля четыре функции. dump и load работают с файлом: первая записывает объект Python, вторая читает его обратно. dumps и loads делают то же самое со строкой: буква s на конце как раз от слова string.
Начнём со строк: так виден сам формат. У dumps есть два аргумента, которые почти всегда стоит указывать. ensure_ascii=False оставляет кириллицу кириллицей. Без него «Анна» уедет в строку как \u0410\u043d\u043d\u0430: формально верно, а читать невозможно. А indent=2 расставляет переносы и отступы, чтобы результат можно было разглядывать глазами.
Python 3.13import json person = { "name": "Анна", "age": 28, "city": "Москва", "languages": ["Python", "JavaScript"] } json_string = json.dumps(person, ensure_ascii=False, indent=2) print(json_string){ "name": "Анна", "age": 28, "city": "Москва", "languages": [ "Python", "JavaScript" ] }parsed_data = json.loads(json_string) print(f"Имя: {parsed_data['name']}, языки: {parsed_data['languages']}")Имя: Анна, языки: ['Python', 'JavaScript']
Запись JSON в файл и чтение из файла
Те же данные обычно нужно не просто превратить в строку, а положить на диск. Тут в дело идёт пара без s: dump пишет объект прямо в открытый файл, load читает его оттуда. Промежуточная строка не нужна.
Python 3.13import json students = [ {"id": 1, "name": "Иван", "scores": [85, 90, 78]}, {"id": 2, "name": "Мария", "scores": [92, 88, 95]} ] with open('students.json', 'w', encoding='utf-8') as file: json.dump(students, file, ensure_ascii=False, indent=2) with open('students.json', 'r', encoding='utf-8') as file: loaded_students = json.load(file) for student in loaded_students: print(f" {student['name']}: оценки {student['scores']}")Иван: оценки [85, 90, 78] Мария: оценки [92, 88, 95]
CSV: Comma-Separated Values
Модуль csv
Ниже два файла: people.csv с маленькой таблицей и main.py, который её читает. Начнём с вкладки people.csv. Это основа формата: первая строка — заголовки, каждая следующая — строка таблицы, столбцы разделены запятыми.
Глядя на такой файл, легко решить, что никакой модуль не нужен: режем строку через split(","), и готово. Так и работает, но только до первой запятой внутри самого значения: она рано или поздно встретится в адресе или описании товара. Модуль csv знает правила формата и берёт эти хлопоты на себя. В main.py файл читает csv.reader:
1Имя,Возраст,Город2Анна,28,Москва3Иван,35,Санкт-Петербург4Мария,22,Казань5Запустим main.py — в выводе:
Анна, 28 лет, город Москва Иван, 35 лет, город Санкт-Петербург Мария, 22 лет, город Казань
Запятая внутри значения
Вернёмся к обещанной проблеме. Что произойдёт, если запятая окажется в самом значении, например в описании «лёгкий, тонкий»? Наивный split(",") разрезал бы такое значение надвое: один столбец превратился бы в два, и вся строка поехала бы вправо.
Формат решает это кавычками: значение с запятой берётся в кавычки, и разделителем считается только запятая снаружи них. Расставлять их руками не нужно: запись таблицы в файл — работа csv.writer, и кавычки он добавит сам, а csv.reader при чтении снимет.
Файл для записи открывают с параметром newline='': так переводами строк управляет сам модуль csv, иначе на Windows между строками таблицы появились бы пустые.
Посмотрим, как это выглядит в файле:
Python 3.13import csv rows = [ ['Товар', 'Описание', 'Цена'], ['Ноутбук', 'лёгкий, тонкий', '45000'], ] with open('quoted.csv', 'w', newline='', encoding='utf-8') as file: csv.writer(file).writerows(rows) with open('quoted.csv', 'r', encoding='utf-8') as file: print(file.read(), end="")Товар,Описание,Цена Ноутбук,"лёгкий, тонкий",45000
Кавычки вокруг «лёгкий, тонкий» появились сами, и столбцов в строке осталось три.
Кавычки — не единственная тонкость формата: несмотря на название, разделителем не всегда служит запятая. В выгрузках из русской версии Excel это чаще всего ;, потому что запятая там занята под десятичный разделитель. Разделитель задают параметром delimiter, причём одинаково при записи и при чтении:
Python 3.13writer = csv.writer(file, delimiter=';') reader = csv.reader(file, delimiter=';')
Иначе вся строка вернётся одним куском вместо отдельных столбцов.
Обращение к столбцам по имени
До сих пор строки приходили к нам списками, и до нужного значения мы добирались по номеру: row[0], row[1], row[2]. Пока столбцов три и файл ваш собственный, это терпимо. Но стоит кому-то добавить в выгрузку новый столбец в середину — и все номера съедут. Программа при этом не упадёт: она молча начнёт печатать город там, где ожидался возраст. Неправильный результат без ошибки найти труднее, чем падение.
От номеров избавляет DictReader. Он смотрит на строку заголовков и отдаёт каждую строку словарём, где ключ — имя столбца. Прочитаем им тот же people.csv:
1import csv2 3with open('people.csv', 'r', encoding='utf-8') as file:4 for row in csv.DictReader(file):5 print(f" {row['Имя']}, {row['Возраст']} лет, город {row['Город']}")6 Запустим main.py — в выводе:
Анна, 28 лет, город Москва Иван, 35 лет, город Санкт-Петербург Мария, 22 лет, город Казань
Результат тот же, но в коде вместо номеров стоят имена столбцов, и новый столбец в файле ничего не сдвинет. Для записи словарей есть парный DictWriter: он устроен так же, только заранее просит список имён столбцов.
Проверка понимания
Чем json.dumps отличается от json.dump?
