CSV и JSON: две таблицы, два формата
У таблиц два языка: CSV для Excel и плоских выгрузок, JSON для типов и вложенности. Модуль csv переводит с обоих.
Редакция Питоники
JSON — язык API, но не единственный формат данных в жизни. Бухгалтерия присылает таблицы, Excel экспортирует выгрузки, отчёты уходят в файлах, которые открываются двойным щелчком. Всё это — CSV: самый простой табличный формат на свете. Половина данных мира живёт в нём до сих пор, и программист, который умеет читать и писать оба формата, не теряется ни в одном проекте. В этом уроке поставим оба формата рядом, научимся читать каждый и переводить данные с одного языка на другой.
Расшифровка уже в названии: comma-separated values, значения через запятую. Каждая строка файла — запись, внутри записи поля разделены запятой, первая строка обычно шапка с именами столбцов. Никакой магии — сейчас убедимся.
CSV — это просто текст
import os
CSV_TEXT = """name,price
Клавиатура,4990
Мышь,1290
"""
with open("items.csv", "w", encoding="utf-8", newline="") as f:
f.write(CSV_TEXT)
with open("items.csv", encoding="utf-8") as f:
print(f.read())
os.remove("items.csv")
name,price Клавиатура,4990 Мышь,1290
Никаких скобок и кавычек — шапка, две строки данных, и всё. Именно эта простота сделала CSV универсальным: его понимают Excel, Google Таблицы, базы данных и любой язык программирования. Но за простоту платим: формат не знает ни про типы, ни про вложенность — он про текст и запятые, и точка.
csv.reader: строки-списки
Модуль csv входит в стандартную библиотеку и делает единственную полезную работу — аккуратно режет строки файла по полям. csv.reader превращает каждую строку в список значений.
import csv, os
CSV_TEXT = """name,price,rating
Клавиатура,4990,4.7
Мышь,1290,4.3
Наушники,7350,3.9
"""
with open("items.csv", "w", encoding="utf-8", newline="") as f:
f.write(CSV_TEXT)
with open("items.csv", encoding="utf-8", newline="") as f:
rows = list(csv.reader(f))
print(rows[0])
print(rows[1])
print(type(rows[1][1]).__name__, rows[1][1])
os.remove("items.csv")
['name', 'price', 'rating'] ['Клавиатура', '4990', '4.7'] str 4990
Смотри на третью строку вывода: цена приехала строкой, а не числом. CSV не хранит типы — «4990» для него просто текст между запятыми. Считать с ним среднее не выйдет, пока не сделаешь int() сам: это твоя работа при чтении CSV, и забывать её нельзя.
import csv, os
CSV_TEXT = """name,price,rating
Клавиатура,4990,4.7
Мышь,1290,4.3
Наушники,7350,3.9
"""
with open("items.csv", "w", encoding="utf-8", newline="") as f:
f.write(CSV_TEXT)
with open("items.csv", encoding="utf-8", newline="") as f:
reader = csv.reader(f)
header = next(reader)
prices = [int(row[1]) for row in reader]
print(header)
print("Средняя цена:", round(sum(prices) / len(prices)))
os.remove("items.csv")
['name', 'price', 'rating'] Средняя цена: 4543
next(reader) прочитал шапку, а список включения прошёлся по оставшимся строкам, превратив цены в числа. Обрати внимание на index: цена — row[1], то есть второй столбец. Работает, но хрупко: передвинули столбцы в выгрузке — и все индексы поплыли.
csv.DictReader: строки-словари
DictReader решает проблему индексов: он сам читает шапку и делает из каждой строки словарь, где ключи — имена столбцов. Обращение по имени вместо позиции.
import csv, os
CSV_TEXT = """name,price,rating
Клавиатура,4990,4.7
Мышь,1290,4.3
Наушники,7350,3.9
"""
with open("items.csv", "w", encoding="utf-8", newline="") as f:
f.write(CSV_TEXT)
with open("items.csv", encoding="utf-8", newline="") as f:
for row in csv.DictReader(f):
print(row["name"], "-", row["price"], "- рейтинг", row["rating"])
os.remove("items.csv")
Клавиатура - 4990 - рейтинг 4.7 Мышь - 1290 - рейтинг 4.3 Наушники - 7350 - рейтинг 3.9
Порядок столбцов теперь не важен — поле ищется по имени. Записи стали похожи на знакомые по курсу словари из ответа API, с одной разницей: значения здесь всё ещё строки, int() и float() никто не отменял.
import csv, os
CSV_TEXT = """name,price,rating
Клавиатура,4990,4.7
Мышь,1290,4.3
Наушники,7350,3.9
"""
with open("items.csv", "w", encoding="utf-8", newline="") as f:
f.write(CSV_TEXT)
cheap = []
with open("items.csv", encoding="utf-8", newline="") as f:
for row in csv.DictReader(f):
if int(row["price"]) < 5000:
cheap.append(row["name"])
print("Дешевле 5000:", ", ".join(cheap))
os.remove("items.csv")
Дешевле 5000: Клавиатура, Мышь
Всё, что ты умел делать с данными из API, работает и над csv-таблицей — добавился только шаг int() на границе. Фильтр, среднее, топ-N: формат хранения не меняет приёмы обработки.
Точка с запятой: другой разделитель
Один нюанс, о котором узнаешь сразу после первого файла из Excel: в русской локали Excel сохраняет csv с разделителем «точка с запятой», потому что запятая занята десятичной дробью. Модуль csv умеет и такое — разделитель передаётся параметром delimiter.
import csv, os
CSV_SEMI = "name;price\nМышь;1290\nКоврик;890\n"
with open("items.csv", "w", encoding="utf-8", newline="") as f:
f.write(CSV_SEMI)
with open("items.csv", encoding="utf-8", newline="") as f:
rows = list(csv.reader(f, delimiter=";"))
print(rows[0])
print(rows[1][1])
os.remove("items.csv")
['name', 'price'] 1290
Запятая — соглашение по умолчанию, но формат скорее «значения через разделитель». Если файл открылся одной колонкой с мусором внутри — первым делом загляни в него блокнотом и проверь разделитель. И ещё: reader и DictReader — итераторы, они читают файл по строке, а не грузят его целиком; на выгрузке в миллион строк память скажет спасибо. Единственное исключение — list(reader): это осознанное «читаем всё», и берут его только когда данные нужны целиком.
Запятая внутри поля: кавычки
А что если в самом значении есть запятая — примечание «механика, подсветка» разорвётся на два поля? Не разорвётся: writer сам обернёт такое поле в кавычки, а reader сам их раскроет. Это ещё одна причина резать csv именно модулем, а не split-ом по запятой: модуль знает про кавычки, простой split — нет.
import csv, os
with open("quotes.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["name", "note"])
writer.writerow(["Клавиатура", "механика, подсветка"])
with open("quotes.csv", encoding="utf-8") as f:
print(f.read())
os.remove("quotes.csv")
name,note Клавиатура,"механика, подсветка"
Файл получился с кавычками ровно в одном месте — там, где без них было бы не разобраться. То же правило действует в обратную сторону: если в чужой выгрузке поле приехало с кавычками, это не мусор, а след того же самого механизма. Модуль csv снимет их при чтении, и значения вернутся чистыми.
Когда CSV, а когда JSON
| Свойство | CSV | JSON |
|---|---|---|
| структура | плоская таблица: строки и столбцы | любая вложенность: списки в словарях |
| типы | нет, всё текст | числа, bool, null сохраняются |
| кто открывает | Excel, Google Таблицы, 1С | программы и API |
| вес и простота | минимум байт, формат на пальцах | скобки и кавычки, но строгий |
| типичный источник | выгрузки, отчёты, прайсы | ответы API, конфиги |
Правило выбора короткое. CSV хранит только текст и запятые, JSON — типы и вложенность: таблица едет в CSV, структура с глубиной — в JSON. Прайс из тысячи строк для бухгалтерии — CSV; ответ каталога с вложенными категориями и рейтингами — JSON. А когда получатель просит «просто эксельку» — вопросов вовсе не остаётся.
Конвертация: CSV в JSON
Конвертация звучит грозно, а выглядит как всё, что ты уже умеешь: прочитать один формат, привести типы, записать другой. Зачем она нужна — понятно из таблицы: данные приехали csv-файлом, а дальше поедут в API или в программу, которая говорит только JSON. DictReader отдаёт строки-словари — приведём поля к числам и отдадим json.dump.
import csv, json, os
CSV_TEXT = """name,price,rating
Клавиатура,4990,4.7
Мышь,1290,4.3
Наушники,7350,3.9
"""
with open("items.csv", "w", encoding="utf-8", newline="") as f:
f.write(CSV_TEXT)
items = []
with open("items.csv", encoding="utf-8", newline="") as f:
for row in csv.DictReader(f):
items.append({
"name": row["name"],
"price": int(row["price"]),
"rating": float(row["rating"]),
})
with open("items.json", "w", encoding="utf-8") as f:
json.dump(items, f, ensure_ascii=False, indent=2)
with open("items.json", encoding="utf-8") as f:
back = json.load(f)
print(len(back), "товара в json")
print(back[0])
os.remove("items.csv")
os.remove("items.json")
3 товара в json
{'name': 'Клавиатура', 'price': 4990, 'rating': 4.7}В файле items.json теперь настоящие типы: цена — int, рейтинг — float. Именно их CSV потерял при экспорте, и именно их вернул твой код при конвертации — двадцать строк на стыке двух миров, без единой установки. Проверка обратным чтением из прошлого урока здесь как всегда: прочитали, убедились, что json корректен. Такой файл уже можно отдавать любой программе: типы на месте, кириллица читаемая, структура — список словарей.
Конвертация: JSON в CSV
Обратное направление встречается, когда данные из API надо отдать людям в Excel — бухгалтер просит «обычную табличку», и JSON её не устроит. csv.writer пишет строки по одной: сначала шапку, потом по списку значений на каждый товар. writerow принимает список значений любой длины — столбцов в csv может быть сколько нужно, лишь бы в каждой строке их было поровну.
import csv, json, os
API_RESPONSE = """
{
"status": "ok",
"source": "shop-api",
"items": [
{"name": "Клавиатура", "price": 4990, "rating": 4.7, "in_stock": true},
{"name": "Мышь", "price": 1290, "rating": 4.3, "in_stock": true},
{"name": "Монитор", "price": 18400, "rating": 4.8, "in_stock": false},
{"name": "Наушники", "price": 7350, "rating": 3.9, "in_stock": true},
{"name": "Веб-камера", "price": 2450, "rating": 4.1, "in_stock": false}
]
}
"""
items = json.loads(API_RESPONSE)["items"]
with open("items.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["name", "price", "rating"])
for item in items:
writer.writerow([item["name"], item["price"], item["rating"]])
with open("items.csv", encoding="utf-8", newline="") as f:
rows = list(csv.reader(f))
print(len(rows), "строк вместе с шапкой")
print(rows[2])
os.remove("items.csv")
6 строк вместе с шапкой ['Мышь', '1290', '4.3']
Шесть строк: шапка плюс пять товаров. В CSV уехали только три поля — флаг in_stock и служебные ключи ответа остались за бортом: для таблицы они не нужны. Выбрать, какие поля едут в CSV, — часть конвертации, и лучше решать её явно списком, как здесь: новый столбец — это элемент списка в writerow, а не правка половины скрипта. Числа writer превратит в текст сам — обратное преобразование остаётся на стороне читателя.
# на своём компьютере, после pip install pandas
import pandas as pd
table = pd.read_csv("items.csv") # таблица-DataFrame
table["price"] = table["price"].astype(int) # типы вручную и тут
table.to_json("items.json", orient="records", force_ascii=False)
Что дальше
Теперь у тебя оба табличных языка: reader и DictReader на приёме, writer на отправке, приведение типов на границе и ясное правило, какой формат для какой задачи. Осталось собрать всё изученное в один инструмент — следующий урок складывает фильтрацию, разбор ответа и сохранение в мини-клиент API на функциях.
Конвертация форматов — не волшебство, а три шага: прочитать, вернуть типы на место, записать по правилам получателя.
Сначала предскажи ответ в голове — это главный навык программиста.
import csv
row = next(csv.reader(["Мышь,1290,4.3"]))
print(row, len(row))
import csv
rows = list(csv.reader(["name,price", "Мышь,1290"]))
print(rows[1][0], int(rows[1][1]) + 1)
items = [{"name": "Мышь", "price": 1290}]
line = items[0]["name"] + "," + str(items[0]["price"])
print(line)
1. Во что csv.reader превращает строку «Мышь,1290,4.3»?
2. Чем csv.DictReader удобнее csv.reader?
3. Что нужно сделать со значением row["price"] из CSV перед арифметикой?
4. Зачем csv-файлы открывают с newline=""?
5. Какой формат выбрать для прайса, который откроют в Excel?
6. Из каких шагов состоит конвертация CSV в JSON?
Прочитай сохранённую CSV-выгрузку через csv.DictReader, приведи цену к числу и найди самый дорогой товар. Выведи строку вида «Наушники - 7350»: имя, дефис, цена.
Как перевести CSV в JSON на Python?
Прочитать csv.DictReader-ом, привести значения к нужным типам (int, float) и записать json.dump с ensure_ascii=False и indent=2. CSV хранит только строки, поэтому приведение типов — обязательный шаг, иначе в JSON приедут текстовые цены.
Чем csv.DictReader отличается от csv.reader?
reader отдаёт каждую строку списком — доступ по позиции row[1]. DictReader берёт первую строку файла как шапку и отдаёт записи словарями: row["price"]. Для таблиц с понятной шапкой DictReader читаемее и не ломается от перестановки столбцов.
Почему в Python из csv-файла приходят строки вместо чисел?
Потому что в самом формате нет типов: CSV — это текст, где всё между запятыми. «4990» приходит строкой и превращается в число вручную: int(row["price"]). JSON, в отличие от CSV, типы хранит — это одна из главных причин выбирать его для данных, с которыми программа работает.
Когда данные лучше хранить в CSV, а когда в JSON?
Плоская таблица без вложенности, адресованная человеку или Excel, — CSV: просто, компактно, открывается везде. Вложенные структуры, булевы флаги, сохранение типов — JSON. Форматы свободно конвертируются друг в друга силами стандартной библиотеки csv и json.
Почему csv-файл из Excel открывается одной колонкой?
В русской локали Excel разделяет поля точкой с запятой, а не запятой: запятая занята десятичной дробью. Укажи разделитель при чтении: csv.reader(f, delimiter=";") или csv.DictReader(f, delimiter=";") — и колонки встанут на место. Первым делом всегда смотри на файл глазами: разделитель виден сразу.
Что сломается, если открыть csv в Excel и сохранить обратно?
Самое частое — кодировка и разделитель: старые Excel сохраняют в cp1251 и точкой с запятой, а дробные числа могут превратиться в даты. Правило гигиены: csv-файлы для программ правят скриптом, а не руками в Excel; Excel для них — зритель, а не редактор. Если без правки руками не обойтись, при сохранении выбирай явно utf-8 и проверяй результат чтением через DictReader.
Как csv.reader работает с большими файлами?
reader и DictReader — итераторы: строки читаются по одной в цикле, весь файл в память не грузится. Поэтому цикл for row in reader спокойно переваривает выгрузку в миллионы строк. Список целиком нужен только когда данные обязаны лежать в памяти: rows = list(reader).
Понравился урок? Сошлитесь на него
«CSV хранит только текст и запятые, JSON — типы и вложенность: таблица едет в CSV, структура с глубиной — в JSON.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что читать дальше
json · Урок 15
Фильтрация ответа API: цены, рейтинги, флаги
Товары из ответа API приехали — теперь вопросы: что дешевле N, что в наличии, какая средняя цена и сколько процентов подходит под условие.
json · Урок 17
Сохраняем результаты: json.dump итоговых данных
Отфильтровали, отсортировали, посчитали — теперь результат должен пережить скрипт: json.dump пишет итог в файл с кириллицей и лесенкой.
json · Урок 19
Мини-клиент API на функциях
URL, разбор ответа, фильтр, файл — каждый шаг уже умещается в одну функцию. Собираем их в клиент, который читается как сценарий.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
openpyxl · Урок 4
Чтение Excel в openpyxl: load_workbook и обход данных
load_workbook открывает готовый файл, iter_rows обходит строки кортежами, max_row и max_column дают размеры — читаем чужие таблицы циклом.
openpyxl прочитать файлopenpyxl читать данные из таблицы
Pandas · Урок 2
Чтение файлов в Pandas: read_csv, read_excel и знакомство с данными
read_csv со всеми нужными параметрами и примерами: разделители, кодировки с кириллицей, чтение куска большого файла и первые ошибки, которые вы увидите на практике.
как читать csv файл в pandasчитать sql в pandas
json · Урок 4
Файлы JSON: json.dump и json.load
Данные, которые переживают скрипт: json.dump пишет словарь в файл, json.load читает обратно, а round-trip подтверждает — сохранил, прочитал, совпало.
json файл python сохранитьjson.dump python