JSON do CSV w Pythonie — Przykłady DictWriter + pandas
Użyj darmowego JSON do CSV bezpośrednio w przeglądarce — bez instalacji.
Wypróbuj JSON do CSV online →Niemal każdy potok danych w końcu napotyka ten sam krok: API zwraca JSON, ale kolejny konsument — arkusz kalkulacyjny, skrypt importu, polecenie Redshift COPY — potrzebuje CSV. Konwersja JSON do CSV w Pythonie brzmi trywialnie, dopóki nie natkniesz się na zagnieżdżone obiekty, niespójne klucze lub wartości datetime wymagające specjalnej obsługi. Python oferuje dwie solidne ścieżki: wbudowane moduły json + csv dla skryptów bez zewnętrznych zależności, oraz pandas do spłaszczania zagnieżdżonych struktur i większych zbiorów danych — albo konwerter JSON do CSV online dla szybkich jednorazowych konwersji bez żadnego kodu. Ten przewodnik omawia oba podejścia od początku do końca, z działającymi przykładami Python 3.8+.
- ✓csv.DictWriter konwertuje listę słowników do CSV bez żadnych zależności — użyj json.load() do parsowania, a następnie writeheader() + writerows().
- ✓Zawsze otwieraj pliki CSV z newline="" w Windows, aby zapobiec pustym wierszom między wierszami danych.
- ✓pd.json_normalize() spłaszcza zagnieżdżony JSON do płaskiego DataFrame przed wywołaniem to_csv() — obsługuje automatycznie wielopoziomowe zagnieżdżenia.
- ✓Przekaż index=False do DataFrame.to_csv() — bez tego pandas zapisuje niechcianą kolumnę z numerami wierszy.
- ✓Dla plików powyżej 500 MB użyj ijson do strumieniowego parsowania JSON w połączeniu z csv.DictWriter dla stałego użycia pamięci.
Czym jest konwersja JSON do CSV?
Konwersja JSON do CSV przekształca tablicę obiektów JSON w format tabelaryczny, gdzie każdy obiekt staje się wierszem, a każdy klucz staje się nagłówkiem kolumny. JSON jest hierarchiczny — obiekty mogą być zagnieżdżone dowolnie głęboko. CSV jest płaskie — każda wartość siedzi w siatce wiersz-kolumna. Konwersja działa sprawnie, gdy każdy obiekt posiada ten sam zestaw kluczy najwyższego poziomu. Zagnieżdżone obiekty, tablice i niespójne klucze sprawiają, że robi się ciekawie. Surowe dane pozostają identyczne; zmienia się tylko struktura.
[{"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 — Konwersja JSON do CSV bez pandas
Moduł csv jest dostarczany z każdą instalacją Pythona. Żadnego pip install, żadnych komplikacji ze środowiskami wirtualnymi. csv.DictWriter przyjmuje listę słowników i zapisuje każdy jako wiersz CSV, mapując klucze słownika na nagłówki kolumn. Parametr fieldnames kontroluje zarówno kolejność kolumn, jak i to, które klucze są uwzględniane.
import json
import csv
# Przykładowe dane JSON — tablica obiektów zamówień
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.95Argument newline="" w open() nie jest opcjonalny w Windows. Bez niego otrzymujesz podwójne powroty karetki — które wyświetlają się jako puste wiersze między każdym wierszem danych w Excelu. Na macOS i Linux jest nieszkodliwy, więc po prostu zawsze go uwzględniaj.
Powyższy kod używa json.loads() dla ciągu znaków. Użyj json.load() (bez końcowego s) przy odczycie z uchwytu pliku. To ciągle myli ludzi — jedna czyta ciąg znaków, druga czyta obiekt pliku.
import json
import csv
with open("server_metrics.json", encoding="utf-8") as jf:
metrics = json.load(jf) # json.load() dla obiektów pliku
# Jawne fieldnames kontrolują kolejność kolumn
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)
# Pojawia się tylko pięć określonych kolumn, dokładnie w tej kolejnościUstawienie extrasaction="ignore" cicho pomija klucze w słownikach, które nie znajdują się na liście fieldnames. Domyślne jest "raise", które rzuca ValueError gdy jakiś słownik ma nieoczekiwany klucz. Wybierz to, co odpowiada twojej tolerancji na niespodzianki.
csv.DictWriter vs csv.writer: DictWriter automatycznie mapuje klucze słownika na pozycje kolumn. csv.writer zapisuje surowe listy jako wiersze — sam obsługujesz kolejność kolumn. DictWriter jest prawie zawsze właściwym wyborem do konwersji JSON na CSV, ponieważ rekordy JSON są już słownikami.Moduł csv Pythona zawiera trzy nazwane dialekty: excel (ogranicznik przecinek, zakończenia linii CRLF — domyślny), excel-tab (ogranicznik tabulator, zakończenia CRLF) oraz unix (zakończenia LF, cytuje wszystkie pola nienumeryczne). Przekaż nazwę dialektu jako argument dialect do csv.DictWriter. Możesz też zdefiniować niestandardowy dialekt za pomocą csv.register_dialect() gdy system docelowy ma nietypowe reguły cytowania lub ograniczników. W większości przepływów pracy JSON do CSV dialekt excel jest prawidłowy, ale przełącz się na unix przy zapisywaniu plików przetwarzanych przez narzędzia POSIX takie jak awk lub sort.
Obsługa niestandardowych typów: datetime, UUID i Decimal
JSON z API często zawiera daty jako ciągi ISO, UUID jako ciągi z myślnikami i wartości monetarne jako liczby zmiennoprzecinkowe. Gdy parsujemy je do obiektów Pythona do przetwarzania przed zapisem CSV, trzeba je z powrotem przekonwertować na ciągi znaków. Moduł csv wywołuje str() na każdej wartości, więc większość typów po prostu działa. Ale obiekty datetime dają nieporządne domyślne reprezentacje ciągów, a wartości Decimal wymagają jawnego formatowania, aby uniknąć notacji naukowej.
import json
import csv
from datetime import datetime, timezone
from decimal import Decimal
from uuid import UUID
# Symulacja sparsowanej odpowiedzi API z typami Pythona
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:
"""Konwertuj typy niebędące ciągami na ciągi przyjazne dla 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 GmbHFunkcja prepare_row() jest tutaj właściwym podejściem. Zamiast próbować nauczyć csv.DictWriter o niestandardowych typach, normalizujesz każdy rekord do ciągów znaków przed zapisem. Preferuję jawne wywołanie .isoformat() na obiektach datetime zamiast polegania na str() — format wyjściowy jest bardziej przewidywalny, a parsery downstream niezawodnie obsługują ISO 8601.
Decimal przejść bez formatowania, bardzo małe lub bardzo duże liczby mogą być renderowane w notacji naukowej (np. 1.5E+7). Zawsze formatuj Decimal jawnym f-stringiem jak f"{value:.2f}" przy zapisywaniu danych finansowych do CSV.Alternatywnym wzorcem dla potoków z wieloma niestandardowymi typami jest rozszerzenie json.JSONEncoder. Utwórz podklasę, przesłoń metodę default() aby zwracała wartość serializowalną do JSON dla każdego niestandardowego typu, a następnie przekaż podklasę jako argument cls do json.dumps(). Ponowne kodowanie przez niestandardowy enkoder przed zapisem do CSV normalizuje wszystkie typy w jednym kroku bez wywołania prepare_row() dla każdego wiersza. Wzorzec prepare_row() pokazany powyżej jest prostszy dla jednorazowych skryptów; podejście z podklasą JSONEncoder skaluje się lepiej, gdy ten sam model domeny z niestandardowymi typami jest współdzielony przez wiele etapów potoku lub mikroserwisów.
Dokumentacja parametrów csv.DictWriter
Pełna sygnatura konstruktora to csv.DictWriter(f, fieldnames, restval="", extrasaction="raise", dialect="excel", **fmtparams). Większość z nich ma sensowne wartości domyślne. Te, które faktycznie będziesz zmieniać, to fieldnames, delimiter oraz extrasaction.
pandas — Konwersja JSON do CSV z DataFrame
Jeśli pracujesz już w kodzie intensywnie używającym pandas, lub twój JSON zawiera zagnieżdżone obiekty wymagające spłaszczenia, podejście z pandas wymaga znacznie mniej kodu niż wersja ze standardową biblioteką. Kompromis: pandas to zależność ~30 MB. Dla jednorazowego skryptu to w porządku. Dla obrazu Docker wysyłanego na produkcję, podejście stdlib utrzymuje rzeczy lżejsze.
import pandas as pd
# Wczytaj tablicę JSON bezpośrednio do DataFrame
df = pd.read_json("warehouse_inventory.json")
# Zapisz do CSV — index=False zapobiega automatycznie generowanym numerom wierszy
df.to_csv("warehouse_inventory.csv", index=False)
# To wszystko. Dwie linie. pandas automatycznie wnioskuje typy kolumn.Flaga index=False to jedna z tych rzeczy, które sprawdzasz za każdym razem. Bez niej pandas zapisuje kolumnę 0, 1, 2, ... jako pierwszą kolumnę pliku CSV. Nikt tego nie chce.
Spłaszczanie zagnieżdżonego JSON z json_normalize
Prawdziwe odpowiedzi API rzadko są płaskie. Zamówienia zawierają adresy wysyłki, użytkownicy zawierają zagnieżdżone preferencje, zdarzenia telemetryczne zawierają zagnieżdżone metadane. pd.json_normalize() przechodzi przez zagnieżdżone słowniki i spłaszcza je do kolumn z nazwami rozdzielonymi kropkami.
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 spłaszcza zagnieżdżone słowniki — sep kontroluje ogranicznik
df = pd.json_normalize(orders, sep="_")
df.to_csv("flat_orders.csv", index=False)
# Wynikowe kolumny:
# order_id, placed_at, customer_name, customer_email, customer_tier,
# shipping_method, shipping_address_city, shipping_address_state,
# shipping_address_zip, totalParametr sep="_" kontroluje jak nazwy zagnieżdżonych kluczy są łączone. Domyślne to ".", co generuje kolumny jak customer.name. Preferuję podkreślniki, ponieważ kropki w nazwach kolumn sprawiają problemy przy importach SQL i niektórych formułach arkuszy kalkulacyjnych.
Dla odpowiedzi API owijających tablicę rekordów pod zagnieżdżonym kluczem, użyj parametru record_path. Jeśli odpowiedź wygląda jak {"data": {"orders": [...]}}, przekaż record_path=["data", "orders"] aby nawigować do właściwej listy. Opcjonalny parametr meta pozwala pobierać pola z poziomu nadrzędnego razem z zagnieżdżonymi rekordami — przydatne gdy odpowiedź zawiera informacje o paginacji najwyższego poziomu (numer strony, łączna liczba), które chcesz mieć jako kolumnę w każdym wierszu. Razem, record_path i meta obsługują większość rzeczywistych kształtów zagnieżdżonych odpowiedzi API bez niestandardowego przetwarzania wstępnego.
Dokumentacja parametrów DataFrame.to_csv()
DataFrame.to_csv() ma ponad 20 parametrów. Oto te, które mają znaczenie w przepływach pracy JSON do CSV.
import pandas as pd
df = pd.read_json("telemetry_events.json")
# Wyjście TSV z jawnym kodowaniem i obsługą brakujących wartości
df.to_csv(
"telemetry_events.tsv",
sep="\t",
index=False,
encoding="utf-8",
na_rep="NULL",
columns=["event_id", "timestamp", "source", "severity", "message"],
)
# Zapis do stdout dla potoków w skryptach powłoki
print(df.to_csv(index=False))
# Zwróć jako ciąg znaków (bez zapisu pliku)
csv_string = df.to_csv(index=False)
print(len(csv_string), "znaków")Konwersja JSON do CSV z pliku i odpowiedzi API
Dwa najczęstsze scenariusze w rzeczywistości: odczytywanie JSON z pliku na dysku i konwersja go, lub pobieranie JSON z API HTTP i zapisywanie wyniku jako CSV. W czasie programowania można uniknąć obsługi błędów. Na produkcji taki wybór staje się alertem o 2 w nocy. Pliki mogą nie istnieć, API może zwracać kody statusu 4xx lub 5xx zamiast JSON, treść odpowiedzi może być obiektem błędu zamiast tablicy, lub JSON może być skrócony z powodu przekroczenia limitu czasu sieci. Poniższe wzorce obsługują wszystkie te przypadki jawnie, logują błędy do stderr i zwracają liczbę wierszy, żeby wywołujący mogli wykryć wyjścia z zerowymi wierszami i odpowiednio alarmować.
Plik na dysku — Odczyt, Konwersja, Zapis
import json
import csv
import sys
def json_file_to_csv(input_path: str, output_path: str) -> int:
"""Konwertuj plik JSON zawierający tablicę obiektów do CSV.
Zwraca liczbę zapisanych wierszy.
"""
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
# Zbierz wszystkie unikalne klucze ze wszystkich rekordów — obsługuje niespójne schematy
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")Odpowiedź API HTTP — Pobierz i Konwertuj
import json
import csv
import urllib.request
import urllib.error
def api_response_to_csv(url: str, output_path: str) -> int:
"""Pobierz JSON z endpointu REST API i zapisz go jako 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 ze standardowej biblioteki, aby skrypt był wolny od zależności. Jeśli masz zainstalowane requests, zastąp sekcję urllib przez resp = requests.get(url, timeout=30); records = resp.json() — pozostały kod do zapisu CSV zostaje identyczny.Konwersja JSON do CSV z wiersza poleceń
Czasem potrzebujesz po prostu jednolinijkowca w terminalu. Flaga -c Pythona pozwala wykonać szybką konwersję bez tworzenia pliku skryptu. Dla bardziej złożonych transformacji najpierw przepuść przez jq aby zmienić kształt danych, a następnie dokonaj konwersji.
# Jednolinijkowiec Python: wczytuje JSON ze stdin, zapisuje CSV do 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) " # Zapis wyjścia do pliku 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
# Zapisz jako json2csv.py i uruchom: 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)
" "$@"# Instalacja csvkit: pip install csvkit
# jq spłaszcza i wybiera pola, in2csv obsługuje formatowanie CSV
cat api_response.json | jq '[.[] | {id: .order_id, customer: .customer.name, total}]' | in2csv -f json > orders.csv
# Miller (mlr) to kolejna opcja dla JSON do CSV
mlr --json2csv cat orders.json > orders.csvMiller (mlr) to samodzielny plik binarny, który traktuje JSON, CSV i TSV jako formaty pierwszej klasy bez potrzeby środowiska wykonawczego Python. Flaga --json2csv konwertuje wejście JSON do CSV w jednym przejściu, a można łączyć czasowniki Millera do filtrowania, sortowania lub zmiany nazw kolumn w tym samym poleceniu przed zapisem wyjścia. Instalacja przez Homebrew na macOS (brew install miller) lub menedżer pakietów systemu Linux. Jest szczególnie przydatny w potokach CI, gdzie potrzebujesz szybkiej konwersji JSON do CSV bez uruchamiania środowiska Python.
Alternatywa wysokowydajnościowa — pandas z pyarrow
Dla zbiorów danych rzędu dziesiątek milionów wierszy, pandas z backendem pyarrow czyta i zapisuje znacznie szybciej niż domyślny. Silnik Arrow wspierany przez C przetwarza dane kolumnowe efektywniej niż moduł csv Pythona działający wiersz po wierszu. API pozostaje takie samo — po prostu ustawiasz parametr engine.
pip install pyarrow
import pandas as pd
# Wczytaj JSON z silnikiem pyarrow (szybsze parsowanie dla dużych plików)
df = pd.read_json("sensor_readings.json", engine="pyarrow")
# to_csv nie ma parametru engine, ale operacje DataFrame
# między odczytem a zapisem korzystają z kolumnowego układu pyarrow
df.to_csv("sensor_readings.csv", index=False)
# Dla naprawdę dużych eksportów rozważ zapis do Parquet zamiast CSV
# — format binarny, 5-10x mniejszy, zachowuje typy
df.to_parquet("sensor_readings.parquet", engine="pyarrow")Jeśli przetwarzasz więcej niż kilkaset MB JSON i końcowy konsument akceptuje Parquet, pomiń CSV całkowicie. Parquet jest mniejszy, zachowuje typy kolumn, a zarówno Redshift jak i BigQuery ładują go natywnie. CSV to format stratny — każda wartość staje się ciągiem znaków.
Wyjście terminala z podświetlaniem składni
Biblioteka rich renderuje tabele z obramowaniami, wyrównaniem i kolorami w terminalu — przydatne do podglądu konwersji podczas programowania bez otwierania pliku wyjściowego.
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)
# Renderuje kolorową tabelę z obramowaniami w terminalucsv.DictWriter lub DataFrame.to_csv(), a rich używaj tylko do podglądu.Praca z dużymi plikami JSON
json.load() wczytuje cały plik do pamięci. Dla pliku JSON 200 MB oznacza to ~200 MB surowego tekstu plus narzut obiektów Pythona — łatwo ponad 500 MB użycia sterty. Dla plików powyżej 100 MB, strumieniuj wejście za pomocą ijson i zapisuj wiersze CSV na bieżąco.
pip install ijson
Strumieniowanie tablicy JSON do CSV z ijson
import ijson
import csv
def stream_json_to_csv(json_path: str, csv_path: str) -> int:
"""Konwertuj dużą tablicę JSON do CSV bez ładowania jej całości do pamięci."""
with open(json_path, "rb") as jf, open(csv_path, "w", newline="", encoding="utf-8") as cf:
# ijson.items zwraca każdy element tablicy najwyższego poziomu jeden po drugim
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 — Jeden obiekt na linię
NDJSON (Newline-Delimited JSON), zwany też JSON Lines lub .jsonl, przechowuje jeden prawidłowy obiekt JSON na linię bez owijającej tablicy. Format ten jest powszechny w potokach logów, strumieniach zdarzeń (Kafka, Kinesis) i eksportach masowych z serwisów takich jak Elasticsearch i BigQuery. Ponieważ każda linia to samodzielny obiekt JSON, możesz przetwarzać plik NDJSON zwykłą pętlą Pythona for po uchwycie pliku — bez potrzeby biblioteki ijson. Pamięć pozostaje stała niezależnie od rozmiaru pliku, co czyni to najprostszym podejściem strumieniowym gdy źródłowe dane są już w formacie JSON Lines.
import json
import csv
def ndjson_to_csv(ndjson_path: str, csv_path: str) -> int:
"""Konwertuj plik newline-delimited JSON do CSV, linia po linii."""
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 # pomiń błędnie sformułowane linie
return count
rows = ndjson_to_csv("access_log.ndjson", "access_log.csv")
print(f"Converted {rows} log entries to CSV")json.load() może zużyć 3–5 GB RAM z powodu narzutu obiektów Pythona. Z ijson pamięć pozostaje stała niezależnie od rozmiaru pliku. Jeśli potrzebujesz tylko szybkiej konwersji małego pliku, wklej go do konwertera JSON do CSV zamiast tego.Typowe błędy
Problem: Moduł csv zapisuje zakończenia linii . Bez newline='', tryb tekstowy Pythona dodaje kolejny w Windows, tworząc podwójne odstępy w wyjściu.
Rozwiązanie: Zawsze przekazuj newline='' przy otwieraniu pliku do zapisu CSV. Jest nieszkodliwy na macOS/Linux.
with open("output.csv", "w") as f:
writer = csv.DictWriter(f, fieldnames=columns)
writer.writeheader()
writer.writerows(data)
# Puste wiersze między każdym wierszem danych w Windowswith open("output.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=columns)
writer.writeheader()
writer.writerows(data)
# Czyste wyjście na wszystkich platformachProblem: Bez index=False, pandas dodaje automatycznie inkrementowaną kolumnę numerów wierszy (0, 1, 2, ...), która zanieczyszcza CSV danymi, których nigdy nie było w oryginalnym JSON.
Rozwiązanie: Przekaż index=False do to_csv(). Jeśli faktycznie potrzebujesz kolumny indeksu, nadaj jej jawną nazwę za pomocą df.index.name = 'row_num'.
df = pd.read_json("events.json")
df.to_csv("events.csv")
# CSV otrzymuje dodatkową nienazwaną kolumnę: ,event_id,timestamp,...
# Wiodący przecinek psuje wiele parserów CSVdf = pd.read_json("events.json")
df.to_csv("events.csv", index=False)
# Czyste CSV: event_id,timestamp,...Problem: Jeśli obiekty JSON mają różne klucze (niektóre rekordy mają pola opcjonalne), użycie kluczy pierwszego rekordu jako fieldnames cicho pomija kolumny, które pojawiają się tylko w późniejszych rekordach.
Rozwiązanie: Zbierz wszystkie unikalne klucze ze wszystkich rekordów przed utworzeniem DictWriter.
records = json.load(f) writer = csv.DictWriter(out, fieldnames=records[0].keys()) # Pomija pole "discount" które pojawia się tylko w 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="") # Każdy klucz z każdego rekordu jest uwzględniony jako kolumna
Problem: csv.DictWriter wywołuje str() na zagnieżdżonych słownikach, tworząc kolumny z wartościami jak "{'city': 'Portland'}"— surowa reprezentacja Pythona, nie prawdziwe dane.
Rozwiązanie: Najpierw spłaszcz zagnieżdżone obiekty używając pd.json_normalize() lub niestandardowej funkcji spłaszczającej.
records = [{"id": "evt_1", "meta": {"source": "web", "region": "us-west"}}]
writer = csv.DictWriter(f, fieldnames=["id", "meta"])
writer.writerows(records)
# kolumna meta zawiera: {'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)
# Kolumny: id, meta_source, meta_regioncsv.DictWriter vs pandas — Szybkie porównanie
Użyj csv.DictWriter gdy potrzebujesz zerowych zależności, twój JSON jest płaski, a skrypt działa w ograniczonym środowisku (kontenery CI, funkcje Lambda, wbudowany Python). Użyj pd.json_normalize() + to_csv() gdy JSON jest zagnieżdżony, potrzebujesz transformować lub filtrować dane przed eksportem, lub pracujesz już w przepływie pracy pandas. Dla plików, które nie mieszczą się w pamięci, połącz ijson z csv.DictWriter dla strumieniowania ze stałą pamięcią.
Dla szybkich konwersji bez kodu, konwerter JSON do CSV na ToolDeck obsługuje to bez żadnej konfiguracji Pythona.
Najczęściej zadawane pytania
Jak przekonwertować JSON do CSV w Pythonie bez pandas?
Użyj wbudowanych modułów json i csv. Wywołaj json.load() aby sparsować plik JSON do listy słowników, wyodrębnij fieldnames z kluczy pierwszego słownika, utwórz csv.DictWriter, wywołaj writeheader(), a następnie writerows(). To podejście ma zero zewnętrznych zależności i działa w każdym środowisku Python 3.x. Działa też szybciej niż pandas dla małych plików, ponieważ nie ma narzutu alokacji DataFrame. Jeśli obiekty JSON mają niespójne klucze w różnych rekordach, najpierw zbierz wszystkie unikalne klucze za pomocą dict.fromkeys(k for r in records for k in r) przed przekazaniem ich jako fieldnames, aby uniknąć brakujących kolumn.
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)Jak obsługiwać zagnieżdżony JSON podczas konwersji do CSV?
Płaskie tablice JSON mapują się bezpośrednio na wiersze CSV, ale zagnieżdżone obiekty wymagają najpierw spłaszczenia. Z pandas, pd.json_normalize() obsługuje to automatycznie — łączy zagnieżdżone klucze separatorem kropki (np. "address.city"). Bez pandas napisz rekurencyjną funkcję, która przechodzi przez słownik i łączy klucze ogranicznikiem. Dla głęboko zagnieżdżonych struktur z wieloma poziomami json_normalize obsługuje je wszystkie w jednym przejściu. Parametr sep kontroluje znak łączący segmenty kluczy — podkreślnik jest zazwyczaj bezpieczniejszy niż domyślna kropka przy importach SQL i zgodności formuł arkuszy kalkulacyjnych.
import pandas as pd
nested_data = [
{"id": "ord_91a3", "customer": {"name": "Anna Kowalska", "email": "a.kowalska@przyklad.pl"}},
]
df = pd.json_normalize(nested_data, sep="_")
# Kolumny: id, customer_name, customer_email
df.to_csv("flat_orders.csv", index=False)Dlaczego mój plik CSV ma puste wiersze między wierszami danych w Windows?
Moduł csv domyślnie zapisuje zakończenia linii \r\n. W Windows otwarcie pliku w trybie tekstowym dodaje kolejny \r, tworząc \r\r\n — co wyświetla się jako pusty wiersz. Rozwiązaniem jest zawsze przekazywanie newline="" do open(). Mówi to Pythonowi, żeby nie tłumaczył zakończeń linii, pozwalając modułowi csv je obsłużyć. Ten wzorzec jest wymagany niezależnie od systemu operacyjnego — jest nieszkodliwy na macOS i Linux, a krytyczny w Windows. Dokumentacja Pythona wyraźnie zaznacza to w sekcji modułu csv jako prawidłowy sposób otwierania plików do zapisu CSV.
# Błędnie — puste wiersze w Windows
with open("output.csv", "w") as f:
writer = csv.writer(f)
# Prawidłowo — newline="" zapobiega podwójnemu \r
with open("output.csv", "w", newline="") as f:
writer = csv.writer(f)Jak dołączyć rekordy JSON do istniejącego pliku CSV?
Otwórz plik w trybie dołączania ("a") i utwórz DictWriter z tymi samymi fieldnames. Pomiń writeheader(), ponieważ wiersz nagłówka już istnieje. Z pandas użyj to_csv(mode="a", header=False). Upewnij się, że kolejność kolumn odpowiada istniejącemu plikowi, bo w przeciwnym razie dane trafią do złych kolumn. Jeśli nie jesteś pewien kolejności kolumn w istniejącym pliku, najpierw otwórz go za pomocą csv.DictReader i odczytaj fieldnames z jego atrybutu fieldnames przed utworzeniem writera do dołączania.
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)Jaki jest najszybszy sposób konwersji dużego pliku JSON do CSV w Pythonie?
Dla plików poniżej 500 MB, pd.read_json() a następnie to_csv() to najszybsze podejście jednoetapowe — pandas używa wewnętrznie zoptymalizowanego kodu C. Dla plików powyżej 500 MB użyj ijson do strumieniowania rekordów JSON i zapisuj je do CSV za pomocą csv.DictWriter wiersz po wierszu. Utrzymuje to stałe użycie pamięci niezależnie od rozmiaru pliku. Dla plików NDJSON (jeden obiekt JSON na linię) nie potrzebujesz w ogóle ijson — zwykła pętla Pythona for po uchwytie pliku przetwarza każdą linię niezależnie i osiąga stałą pamięć bez żadnej biblioteki zewnętrznej.
# Szybkie dla plików mieszczących się w pamięci
import pandas as pd
df = pd.read_json("large_dataset.json")
df.to_csv("large_dataset.csv", index=False)
# Strumieniowanie dla plików nie mieszczących się w pamięci
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)Czy mogę zapisać wyjście CSV do stdout zamiast do pliku w Pythonie?
Tak. Przekaż sys.stdout jako obiekt pliku do csv.writer() lub csv.DictWriter(). Jest to przydatne przy potokowaniu wyjścia w skryptach powłoki lub szybkim debugowaniu. Z pandas wywołaj to_csv(sys.stdout, index=False) lub to_csv(None) aby uzyskać ciąg znaków do wydrukowania. Nie potrzeba pliku tymczasowego. Przy zapisywaniu do stdout w Windows wywołaj najpierw sys.stdout.reconfigure(newline="") aby uniknąć problemu z podwójnym powrotem karetki, ponieważ stdout otwiera się domyślnie w trybie tekstowym.
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.1Powiązane narzędzia
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.