JSON para CSV em Python — Exemplos com DictWriter e pandas

·Backend Developer·Revisado porPriya Sharma·Publicado

Use o Conversor JSON para CSV gratuito diretamente no seu navegador — sem instalação.

Experimentar Conversor JSON para CSV online →

Quase todo pipeline de dados chega ao mesmo passo eventualmente: uma API retorna JSON, mas o próximo consumidor — uma planilha, um script de importação, um comando Redshift COPY — precisa de CSV. Converter JSON para CSV em Python parece trivial até você se deparar com objetos aninhados, chaves inconsistentes ou valores datetime que precisam de tratamento especial. O Python oferece dois caminhos sólidos: os módulos embutidos json + csv para scripts sem dependências externas, e pandas para achatamento de dados aninhados e conjuntos de dados maiores — ou o conversor JSON para CSV online para conversões rápidas sem nenhum código. Este guia cobre ambas as abordagens do início ao fim, com exemplos executáveis em Python 3.8+.

  • csv.DictWriter converte uma lista de dicts para CSV sem dependências — use json.load() para analisar e depois writeheader() + writerows().
  • Sempre abra arquivos CSV com newline="" no Windows para evitar linhas em branco entre as linhas de dados.
  • pd.json_normalize() achata o JSON aninhado em um DataFrame simples antes de chamar to_csv() — trata aninhamentos de vários níveis automaticamente.
  • Passe index=False para DataFrame.to_csv() — sem ele, o pandas escreve uma coluna indesejada de números de linha.
  • Para arquivos acima de 500 MB, use ijson para análise de JSON em streaming combinada com csv.DictWriter para uso constante de memória.

O que é conversão de JSON para CSV?

A conversão de JSON para CSV transforma um array de objetos JSON em um formato tabular onde cada objeto se torna uma linha e cada chave se torna um cabeçalho de coluna. JSON é hierárquico — objetos podem ser aninhados arbitrariamente. CSV é plano — cada valor fica em uma grade de linha-coluna. A conversão funciona de forma limpa quando todos os objetos compartilham o mesmo conjunto de chaves de nível superior. Objetos aninhados, arrays e chaves inconsistentes são onde as coisas ficam interessantes. Os dados brutos permanecem idênticos; apenas a estrutura muda.

Before · json
After · json
[{"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 — Converter JSON para CSV sem pandas

O módulo csv vem com toda instalação Python. Sem pip install, sem malabarismos com ambiente virtual. csv.DictWriter recebe uma lista de dicionários e escreve cada um como uma linha CSV, mapeando chaves do dict para cabeçalhos de coluna. O parâmetro fieldnames controla tanto a ordem das colunas quanto quais chaves são incluídas.

Python 3.8+ — exemplo mínimo de json para csv
import json
import csv

# Dados JSON de exemplo — um array de objetos de pedido
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.95

O argumento newline="" no open() não é opcional no Windows. Sem ele, você obtém retornos de carro duplos — que aparecem como linhas em branco entre cada linha de dados no Excel. No macOS e Linux é inofensivo, então simplesmente sempre inclua.

O código acima usa json.loads() para uma string. Use json.load() (sem o s no final) ao ler de um identificador de arquivo. Isso confunde as pessoas constantemente — um lê uma string, o outro lê um objeto de arquivo.

Python 3.8+ — ler arquivo JSON, escrever arquivo CSV
import json
import csv

with open("server_metrics.json", encoding="utf-8") as jf:
    metrics = json.load(jf)  # json.load() para objetos de arquivo

# Fieldnames explícitos controlam a ordem das colunas
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)

# Apenas as cinco colunas especificadas aparecem, exatamente nessa ordem

Definir extrasaction="ignore" descarta silenciosamente quaisquer chaves nos dicts que não estão na sua lista de fieldnames. O padrão é "raise", que lança um ValueError se algum dict tiver uma chave inesperada. Escolha o que corresponde à sua tolerância a surpresas.

Nota:csv.DictWriter vs csv.writer: DictWriter mapeia as chaves do dict para posições de coluna automaticamente. csv.writer escreve listas brutas como linhas — você mesmo gerencia a ordenação das colunas. DictWriter é quase sempre a escolha certa para JSON-para-CSV porque os registros JSON já são dicionários.

O módulo csv do Python vem com três dialetos nomeados: excel (delimitador de vírgula, terminações de linha CRLF — o padrão), excel-tab (delimitador de tabulação, terminações CRLF) e unix (terminações LF, coloca aspas em todos os campos não numéricos). Passe o nome do dialeto como argumento dialect para csv.DictWriter. Você também pode definir um dialeto personalizado com csv.register_dialect() quando o sistema de destino tiver regras incomuns de aspas ou delimitadores. Para a maioria dos fluxos de trabalho JSON-para-CSV, o dialeto excel é o correto, mas mude para unix ao escrever arquivos que serão processados por ferramentas POSIX como awk ou sort.

Tratando tipos não padrão: datetime, UUID e Decimal

JSON de APIs frequentemente contém datas como strings ISO, UUIDs como strings com hífen e valores monetários como floats. Quando você analisa esses dados em objetos Python para processamento antes de escrever o CSV, você precisa convertê-los de volta para strings. O módulo csv chama str() em cada valor, então a maioria dos tipos funciona direto. Mas objetos datetime produzem representações de string padrão confusas, e valores Decimal precisam de formatação explícita para evitar notação científica.

Python 3.8+ — pré-processar datetime e Decimal antes de escrever CSV
import json
import csv
from datetime import datetime, timezone
from decimal import Decimal
from uuid import UUID

# Simulando resposta de API analisada com tipos Python
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:
    """Converte tipos não-string para strings compatíveis com 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 GmbH

A função prepare_row() é a abordagem correta aqui. Em vez de tentar ensinar csv.DictWriter sobre tipos customizados, você normaliza cada registro para strings antes de escrever. Prefiro chamar .isoformat() explicitamente em objetos datetime em vez de depender de str() — o formato de saída é mais previsível, e os parsers downstream lidam com ISO 8601 de forma confiável.

Aviso:Se você deixar valores Decimal passarem sem formatação, números muito pequenos ou muito grandes podem ser renderizados em notação científica (ex.: 1.5E+7). Sempre formate Decimal com uma f-string explícita como f"{value:.2f}" ao escrever dados financeiros em CSV.

Um padrão alternativo para pipelines com muitos tipos customizados é estender json.JSONEncoder. Crie uma subclasse, sobrescreva o método default() para retornar um valor serializável em JSON para cada tipo customizado, e passe a subclasse como argumento cls para json.dumps(). Recodificar pelo encoder customizado antes de escrever no CSV normaliza todos os tipos em uma etapa sem uma chamada prepare_row() por linha. O padrão prepare_row() mostrado acima é mais simples para scripts pontuais; a abordagem de subclasse JSONEncoder escala melhor quando o mesmo modelo de domínio com tipos customizados é compartilhado entre muitos estágios de pipeline ou microsserviços.

Referência de parâmetros do csv.DictWriter

A assinatura completa do construtor é csv.DictWriter(f, fieldnames, restval="", extrasaction="raise", dialect="excel", **fmtparams). A maioria tem padrões sensatos. Os que você realmente vai alterar são fieldnames, delimiter, e extrasaction.

Parâmetro
Tipo
Padrão
Descrição
f
file object
(obrigatório)
Qualquer objeto com um método write() — tipicamente retornado por open()
fieldnames
sequence
(obrigatório)
Lista de chaves que define a ordem das colunas na saída CSV
restval
str
""
Valor escrito quando um dict não possui uma chave de fieldnames
extrasaction
str
"raise"
"raise" lança ValueError para chaves extras; "ignore" as descarta silenciosamente
dialect
str / Dialect
"excel"
Regras de formatação predefinidas — "excel", "excel-tab" ou "unix"
delimiter
str
","
Caractere único separando os campos — use "\t" para saída TSV
quotechar
str
"
Caractere usado para colocar aspas em campos que contêm o delimitador
quoting
int
csv.QUOTE_MINIMAL
Controla quando as aspas são aplicadas — MINIMAL, ALL, NONNUMERIC, NONE
lineterminator
str
"\r\n"
String adicionada após cada linha — substitua por "\n" para saída no estilo Unix

pandas — Converter JSON para CSV com DataFrames

Se você já trabalha em uma base de código intensiva em pandas, ou seu JSON tem objetos aninhados que você precisa achatar, a abordagem pandas tem significativamente menos código do que a versão stdlib. A contrapartida: pandas é uma dependência de ~30 MB. Para um script descartável, isso é aceitável. Para uma imagem Docker que você envia para produção, a abordagem stdlib mantém as coisas mais leves.

Python 3.8+ — pandas read_json e depois to_csv
import pandas as pd

# Lê o array JSON diretamente em um DataFrame
df = pd.read_json("warehouse_inventory.json")

# Escreve no CSV — index=False evita os números de linha gerados automaticamente
df.to_csv("warehouse_inventory.csv", index=False)

# É isso. Duas linhas. O pandas infere os tipos de coluna automaticamente.

O flag index=False é uma daquelas coisas que você consulta toda vez. Sem ele, o pandas escreve uma coluna 0, 1, 2, ... como primeira coluna do seu CSV. Ninguém quer isso.

Achatando JSON aninhado com json_normalize

Respostas reais de API raramente são simples. Pedidos contêm endereços de envio, usuários contêm preferências aninhadas, eventos de telemetria contêm metadados aninhados. pd.json_normalize() percorre dicionários aninhados e os achata em colunas com nomes separados por ponto.

Python 3.8+ — achatar JSON aninhado usando json_normalize
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 achata os dicts aninhados — sep controla o delimitador
df = pd.json_normalize(orders, sep="_")
df.to_csv("flat_orders.csv", index=False)

# Colunas resultantes:
# order_id, placed_at, customer_name, customer_email, customer_tier,
# shipping_method, shipping_address_city, shipping_address_state,
# shipping_address_zip, total

O parâmetro sep="_" controla como os nomes de chaves aninhadas são unidos. O padrão é ".", que produz colunas como customer.name. Prefiro sublinhados porque pontos em nomes de colunas causam problemas com importações SQL e algumas fórmulas de planilhas.

Para respostas de API que encapsulam o array de registros sob uma chave aninhada, use o parâmetro record_path. Se a resposta parecer {"data": {"orders": [...]}}, passe record_path=["data", "orders"] para navegar até a lista correta. O parâmetro opcional meta permite que você extraia campos do nível pai junto com os registros aninhados — útil quando a resposta inclui informações de paginação de nível superior (número da página, contagem total) que você deseja como coluna em cada linha. Juntos, record_path e meta lidam com a maioria das formas de resposta de API aninhadas do mundo real sem pré-processamento customizado.

Referência de parâmetros de DataFrame.to_csv()

DataFrame.to_csv() tem mais de 20 parâmetros. Estes são os que importam para fluxos de trabalho JSON-para-CSV.

Parâmetro
Tipo
Padrão
Descrição
path_or_buf
str / Path / None
None
Caminho do arquivo ou buffer — None retorna o CSV como string
sep
str
","
Delimitador de campo — use "\t" para TSV
index
bool
True
Escreve o índice da linha como primeira coluna — quase sempre defina como False
columns
list
None
Seleciona e reordena colunas na saída
header
bool / list
True
Escreve os nomes das colunas — defina False ao anexar a um arquivo existente
encoding
str
"utf-8"
Codificação da saída — use "utf-8-sig" para compatibilidade com Excel no Windows
na_rep
str
""
Representação em string para valores ausentes (NaN, None)
quoting
int
csv.QUOTE_MINIMAL
Controla quando os campos recebem aspas
Python 3.8+ — to_csv com substituições comuns de parâmetros
import pandas as pd

df = pd.read_json("telemetry_events.json")

# Saída TSV com codificação explícita e tratamento de valores ausentes
df.to_csv(
    "telemetry_events.tsv",
    sep="\t",
    index=False,
    encoding="utf-8",
    na_rep="NULL",
    columns=["event_id", "timestamp", "source", "severity", "message"],
)

# Escreve no stdout para redirecionamento em scripts shell
print(df.to_csv(index=False))

# Retorna como string (nenhum arquivo escrito)
csv_string = df.to_csv(index=False)
print(len(csv_string), "characters")

Converter JSON para CSV de um arquivo e resposta de API

Os dois cenários mais comuns no mundo real: ler JSON de um arquivo em disco e convertê-lo, ou buscar JSON de uma API HTTP e salvar o resultado como CSV. No desenvolvimento você pode se virar sem tratamento de erros. Em produção, essa escolha vira um alerta às 2 da manhã. Arquivos podem não existir, APIs podem retornar códigos de status 4xx ou 5xx em vez de JSON, o corpo da resposta pode ser um objeto de erro em vez de um array, ou o JSON pode estar truncado devido a um timeout de rede. Os padrões abaixo tratam todos esses casos explicitamente, registram erros no stderr e retornam uma contagem de linhas para que os chamadores possam detectar saídas com zero linhas e alertar adequadamente.

Arquivo em disco — Ler, converter, salvar

Python 3.8+ — converter arquivo JSON para CSV com tratamento de erros
import json
import csv
import sys

def json_file_to_csv(input_path: str, output_path: str) -> int:
    """Converte um arquivo JSON contendo um array de objetos para CSV.
    Retorna o número de linhas escritas.
    """
    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

    # Coleta todas as chaves únicas de todos os registros — trata esquemas inconsistentes
    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")

Resposta de API HTTP — Buscar e converter

Python 3.8+ — buscar JSON da API e salvar como CSV
import json
import csv
import urllib.request
import urllib.error

def api_response_to_csv(url: str, output_path: str) -> int:
    """Busca JSON de um endpoint de API REST e o escreve como 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")
Nota:O exemplo acima usa urllib da biblioteca padrão para manter o script sem dependências. Se você tiver requests instalado, substitua a seção urllib por resp = requests.get(url, timeout=30); records = resp.json() — o restante do código de escrita CSV permanece idêntico.

Conversão de JSON para CSV pela linha de comando

Às vezes você só precisa de um comando rápido no terminal. O flag -c do Python permite executar uma conversão rápida sem criar um arquivo de script. Para transformações mais complexas, redirecione por jq primeiro para remodelar os dados e depois converta.

bash — conversão json para csv em uma linha
# Uma linha Python: lê JSON do stdin, escreve CSV no 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)
"

# Salva a saída em um arquivo
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
bash — script CLI independente com argparse
# Salve como json2csv.py e execute: 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)
" "$@"
bash — usando jq + csvkit para transformações complexas
# Instale csvkit: pip install csvkit

# jq seleciona e achata campos, in2csv cuida da formatação CSV
cat api_response.json | jq '[.[] | {id: .order_id, customer: .customer.name, total}]' | in2csv -f json > orders.csv

# Miller (mlr) é outra opção para JSON-para-CSV
mlr --json2csv cat orders.json > orders.csv

Miller (mlr) é um binário independente que trata JSON, CSV e TSV como formatos de primeira classe sem necessidade de runtime Python. O flag --json2csv converte entrada JSON para CSV em uma única passagem, e você pode encadear verbos Miller para filtrar, ordenar ou renomear colunas no mesmo comando antes de escrever a saída. Instale via Homebrew no macOS (brew install miller) ou pelo gerenciador de pacotes do Linux. É particularmente útil em pipelines de CI onde você quer conversão rápida de JSON para CSV sem inicializar um ambiente Python.

Alternativa de alto desempenho — pandas com pyarrow

Para conjuntos de dados na faixa de dezenas de milhões de linhas, pandas com o backend pyarrow lê e escreve significativamente mais rápido do que o padrão. O motor Arrow baseado em C processa dados colunar de forma mais eficiente do que o módulo csv linha a linha do Python. A API permanece a mesma — você apenas define o parâmetro engine.

bash — instalar pyarrow
pip install pyarrow
Python 3.8+ — pandas com pyarrow para escrita CSV mais rápida
import pandas as pd

# Lê JSON com motor pyarrow (análise mais rápida para arquivos grandes)
df = pd.read_json("sensor_readings.json", engine="pyarrow")

# to_csv não tem parâmetro engine, mas as operações do DataFrame
# entre leitura e escrita se beneficiam do layout colunar do pyarrow
df.to_csv("sensor_readings.csv", index=False)

# Para exportações realmente grandes, considere escrever em Parquet em vez de CSV
# — formato binário, 5-10x menor, preserva os tipos
df.to_parquet("sensor_readings.parquet", engine="pyarrow")

Se você está processando mais do que algumas centenas de MB de JSON e o consumidor final aceita Parquet, pule o CSV completamente. Parquet é menor, preserva os tipos de coluna, e tanto o Redshift quanto o BigQuery o carregam nativamente. CSV é um formato com perda — cada valor se torna uma string.

Saída no terminal com destaque de sintaxe

A biblioteca rich renderiza tabelas com bordas, alinhamento e cor no terminal — útil para visualizar uma conversão durante o desenvolvimento sem abrir o arquivo de saída.

bash — instalar rich
pip install rich
Python 3.8+ — visualizar saída CSV no terminal com 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="Visualização de Métricas do Servidor", 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)
# Renderiza uma tabela com destaque de cores e bordas no terminal
Aviso:Rich é apenas para exibição no terminal. Não o use para gerar arquivos CSV — ele adiciona códigos de escape ANSI que vão corromper a saída. Escreva em arquivos com csv.DictWriter ou DataFrame.to_csv(), e use rich apenas para visualização.

Trabalhando com arquivos JSON grandes

json.load() lê o arquivo inteiro na memória. Para um arquivo JSON de 200 MB, isso significa ~200 MB de texto bruto mais a sobrecarga do objeto Python — facilmente 500 MB+ de uso de heap. Para arquivos acima de 100 MB, transmita a entrada com ijson e escreva as linhas CSV conforme avança.

bash — instalar ijson
pip install ijson

Transmitindo array JSON para CSV com ijson

Python 3.8+ — transmitir array JSON grande para CSV com memória constante
import ijson
import csv

def stream_json_to_csv(json_path: str, csv_path: str) -> int:
    """Converte um array JSON grande para CSV sem carregá-lo todo na memória."""
    with open(json_path, "rb") as jf, open(csv_path, "w", newline="", encoding="utf-8") as cf:
        # ijson.items produz cada elemento do array de nível superior um por vez
        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 — Um objeto por linha

NDJSON (JSON delimitado por nova linha), também chamado de JSON Lines ou .jsonl, armazena um objeto JSON válido por linha sem array envolvente. Esse formato é comum em pipelines de log, streams de eventos (Kafka, Kinesis) e exportações em massa de serviços como Elasticsearch e BigQuery. Como cada linha é um objeto JSON independente, você pode processar um arquivo NDJSON com um simples loop for do Python sobre o identificador de arquivo — sem necessidade da biblioteca ijson. A memória permanece constante independentemente do tamanho do arquivo, tornando esta a abordagem de streaming mais simples quando seus dados de origem já estão no formato JSON Lines.

Python 3.8+ — converter NDJSON para CSV linha a linha
import json
import csv

def ndjson_to_csv(ndjson_path: str, csv_path: str) -> int:
    """Converte um arquivo JSON delimitado por nova linha para CSV, uma linha por vez."""
    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  # pula linhas malformadas

    return count

rows = ndjson_to_csv("access_log.ndjson", "access_log.csv")
print(f"Converted {rows} log entries to CSV")
Nota:Mude para streaming quando o arquivo JSON exceder 100 MB. Um array JSON de 1 GB carregado com json.load() pode consumir 3–5 GB de RAM devido à sobrecarga de objetos Python. Com ijson, a memória permanece plana independentemente do tamanho do arquivo. Se você só precisa de uma conversão rápida de um arquivo pequeno, cole-o no conversor JSON para CSV em vez disso.

Erros comuns

newline='' ausente em open() — linhas em branco no Windows

Problema: O módulo csv escreve terminações de linha \r\n. Sem newline='', o modo texto do Python adiciona outro \r no Windows, produzindo saída com espaço duplo.

Correção: Sempre passe newline='' ao abrir um arquivo para escrita CSV. É inofensivo no macOS/Linux.

Before · Python
After · Python
with open("output.csv", "w") as f:
    writer = csv.DictWriter(f, fieldnames=columns)
    writer.writeheader()
    writer.writerows(data)
# Linhas em branco entre cada linha de dados no Windows
with open("output.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=columns)
    writer.writeheader()
    writer.writerows(data)
# Saída limpa em todas as plataformas
Esquecendo index=False no to_csv() do pandas

Problema: Sem index=False, o pandas antecede uma coluna de número de linha auto-incremental (0, 1, 2, ...) que polui o CSV com dados que nunca estavam no JSON original.

Correção: Passe index=False para to_csv(). Se você realmente precisar de uma coluna de índice, nomeie-a explicitamente com df.index.name = 'row_num'.

Before · Python
After · Python
df = pd.read_json("events.json")
df.to_csv("events.csv")
# CSV recebe uma coluna extra sem nome: ,event_id,timestamp,...
# A vírgula inicial quebra muitos parsers CSV
df = pd.read_json("events.json")
df.to_csv("events.csv", index=False)
# CSV limpo: event_id,timestamp,...
Usar records[0].keys() quando os registros têm chaves inconsistentes

Problema: Se os objetos JSON têm chaves diferentes (alguns registros têm campos opcionais), usar as chaves do primeiro registro como fieldnames descarta silenciosamente colunas que só aparecem em registros posteriores.

Correção: Colete todas as chaves únicas de todos os registros antes de criar o DictWriter.

Before · Python
After · Python
records = json.load(f)
writer = csv.DictWriter(out, fieldnames=records[0].keys())
# Perde o campo "discount" que só aparece em 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="")
# Cada chave de cada registro é incluída como coluna
Escrever dicts aninhados diretamente no CSV sem achatar

Problema: csv.DictWriter chama str() em dicts aninhados, produzindo colunas com valores como "{'city': 'Portland'}"— repr bruto do Python, não dados reais.

Correção: Achate os objetos aninhados primeiro usando pd.json_normalize() ou uma função de achatamento customizada.

Before · Python
After · Python
records = [{"id": "evt_1", "meta": {"source": "web", "region": "us-west"}}]
writer = csv.DictWriter(f, fieldnames=["id", "meta"])
writer.writerows(records)
# coluna meta contém: {'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)
# Colunas: id, meta_source, meta_region

csv.DictWriter vs pandas — Comparação rápida

Método
JSON aninhado
Tipos customizados
Streaming
Dependências
Requer instalação
csv.DictWriter
✗ (achatamento manual)
✓ (linha a linha)
Nenhuma
Não (stdlib)
csv.writer
✓ (linha a linha)
Nenhuma
Não (stdlib)
pd.DataFrame.to_csv()
✗ (apenas flat)
✓ (via dtypes)
pandas + numpy
pip install
pd.json_normalize() + to_csv()
✓ (via dtypes)
pandas + numpy
pip install
csv.writer + json_flatten
flatten_json
pip install
jq + csvkit (CLI)
✓ (via jq)
N/A
jq, csvkit
Instalação do sistema

Use csv.DictWriter quando você precisar de zero dependências, seu JSON for simples e o script rodar em um ambiente restrito (contêineres de CI, funções Lambda, Python embarcado). Use pd.json_normalize() + to_csv() quando o JSON for aninhado, você precisar transformar ou filtrar dados antes da exportação, ou já estiver em um fluxo de trabalho pandas. Para arquivos que não cabem na memória, combine ijson com csv.DictWriter para streaming com memória constante.

Para conversões rápidas sem código, o conversor JSON para CSV no ToolDeck resolve sem nenhuma configuração Python.

Perguntas frequentes

Como converter JSON para CSV em Python sem pandas?

Use os módulos embutidos json e csv. Chame json.load() para analisar o arquivo JSON em uma lista de dicts, extraia os fieldnames das chaves do primeiro dict, crie um csv.DictWriter, chame writeheader() e depois writerows(). Essa abordagem não possui dependências externas e funciona em qualquer ambiente Python 3.x. Também é mais rápida que o pandas para arquivos pequenos, pois não há sobrecarga de alocação de DataFrame. Se os seus objetos JSON tiverem chaves inconsistentes entre os registros, colete primeiro todas as chaves únicas com dict.fromkeys(k for r in records for k in r) antes de passá-las como fieldnames para evitar colunas faltando.

Python
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)

Como lidar com JSON aninhado ao converter para CSV?

Arrays JSON simples mapeiam diretamente para linhas CSV, mas objetos aninhados precisam ser achatados primeiro. Com pandas, pd.json_normalize() faz isso automaticamente — ele une chaves aninhadas com um separador de ponto (ex.: "address.city"). Sem pandas, escreva uma função recursiva que percorre o dict e concatena chaves com um delimitador. Para estruturas profundamente aninhadas com múltiplos níveis, json_normalize lida com todos eles em uma única passagem. O parâmetro sep controla o caractere de junção entre segmentos de chave — o sublinhado geralmente é mais seguro do que o ponto padrão para importações SQL e compatibilidade com fórmulas de planilhas.

Python
import pandas as pd

nested_data = [
    {"id": "ord_91a3", "customer": {"name": "Ana Silva", "email": "a.silva@exemplo.com"}},
]
df = pd.json_normalize(nested_data, sep="_")
# Colunas: id, customer_name, customer_email
df.to_csv("flat_orders.csv", index=False)

Por que meu CSV tem linhas em branco entre as linhas de dados no Windows?

O módulo csv escreve terminações de linha \r\n por padrão. No Windows, abrir o arquivo em modo texto adiciona outro \r, produzindo \r\r\n — o que aparece como uma linha em branco. A correção é sempre passar newline="" para open(). Isso instrui o Python a não traduzir as terminações de linha, deixando o módulo csv cuidar delas. Esse padrão é obrigatório independentemente do sistema operacional — é inofensivo no macOS e Linux, e crítico no Windows. A documentação do Python menciona explicitamente isso na seção do módulo csv como a forma correta de abrir arquivos para escrita CSV.

Python
# Errado — linhas em branco no Windows
with open("output.csv", "w") as f:
    writer = csv.writer(f)

# Correto — newline="" previne o \r duplo
with open("output.csv", "w", newline="") as f:
    writer = csv.writer(f)

Como anexar registros JSON a um arquivo CSV existente?

Abra o arquivo em modo de anexação ("a") e crie um DictWriter com os mesmos fieldnames. Pule writeheader() pois a linha de cabeçalho já existe. Com pandas, use to_csv(mode="a", header=False). Certifique-se de que a ordem das colunas corresponde ao arquivo existente, ou os dados irão parar nas colunas erradas. Se não tiver certeza sobre a ordem das colunas no arquivo existente, abra-o primeiro com csv.DictReader e leia os fieldnames a partir do atributo fieldnames antes de criar o writer para anexação.

Python
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)

Qual é a forma mais rápida de converter um arquivo JSON grande para CSV em Python?

Para arquivos abaixo de 500 MB, pd.read_json() seguido de to_csv() é a abordagem mais rápida de chamada única — o pandas usa código C otimizado internamente. Para arquivos acima de 500 MB, use ijson para transmitir registros JSON e escrevê-los no CSV com csv.DictWriter linha a linha. Isso mantém o uso de memória constante independentemente do tamanho do arquivo. Para arquivos NDJSON (um objeto JSON por linha), você não precisa de ijson — um simples loop for do Python sobre o identificador de arquivo processa cada linha de forma independente e mantém memória constante sem nenhuma biblioteca de terceiros.

Python
# Rápido para arquivos que cabem na memória
import pandas as pd
df = pd.read_json("large_dataset.json")
df.to_csv("large_dataset.csv", index=False)

# Streaming para arquivos que não cabem na memória
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)

Posso escrever a saída CSV no stdout em vez de um arquivo em Python?

Sim. Passe sys.stdout como o objeto de arquivo para csv.writer() ou csv.DictWriter(). Isso é útil para redirecionar a saída em scripts shell ou para depuração rápida. Com pandas, chame to_csv(sys.stdout, index=False) ou to_csv(None) para obter uma string que você pode imprimir. Nenhum arquivo temporário necessário. Ao escrever no stdout no Windows, chame sys.stdout.reconfigure(newline="") primeiro para evitar o problema de duplo retorno de carro, já que o stdout é aberto em modo texto por padrão.

Python
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.1

Ferramentas relacionadas

MS
Maria SantosBackend Developer

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.

PS
Priya SharmaRevisor técnico

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.