JSON em Python: leitura e gravação de arquivos, CSV, pathlib e codificação UTF-8
Para trabalhar com JSON em Python há o módulo embutido json: json.load lê dados de um arquivo aberto, json.dump grava os dados num arquivo, e json.loads e json.dumps fazem o mesmo com strings. O arquivo é aberto com with open(path, encoding="utf-8"), os caminhos são montados com pathlib.Path, e as tabelas são lidas com o módulo csv.
Trabalhar com JSON em Python não exige bibliotecas externas: o módulo json faz parte da biblioteca padrão e transforma dicionários e listas em texto e de volta. Ao lado dele quase sempre são necessárias mais três coisas: a função open com a instrução with, o módulo pathlib para os caminhos e o módulo csv para tabelas. A seguir — tudo em ordem, com código funcional e as armadilhas em que mais se tropeça.
Função open em Python: modos e codificação
Qualquer arquivo de texto é aberto pela função embutida open(path, mode, encoding=...). Ela devolve um objeto de arquivo com os métodos read, readline, write e que pode ser percorrido linha a linha em um laço.
| Modo | O que faz |
|---|---|
"r" | Leitura (padrão). Se o arquivo não existir — FileNotFoundError |
"w" | Gravação. O arquivo é criado ou zerado já na abertura |
"a" | Acrescentar ao fim, o conteúdo existente não é alterado |
"x" | Criar um arquivo novo; se ele já existir — FileExistsError |
"b" | Complemento do modo: trabalho com bytes ("rb", "wb") |
Para que serve o with
O arquivo precisa ser fechado: caso contrário, os dados podem não ser gravados no disco, e o sistema mantém o descritor ocupado. A instrução with fecha o arquivo automaticamente — mesmo que dentro do bloco ocorra um erro.
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("Primeira linha\n")
f.write("Segunda linha\n")
with open("notes.txt", encoding="utf-8") as f:
for line in f:
print(line.rstrip("\n"))
Percorrer o arquivo em um laço lê uma linha por vez e não carrega tudo na memória, por isso assim é possível processar até logs de gigabytes. O método f.read() devolve todo o conteúdo em uma única string, e f.readlines() — uma lista de linhas.
Como acrescentar uma linha em um arquivo sem apagar o que existe
O erro mais comum de quem está começando é abrir o arquivo com "w" para «acrescentar» uma linha. O modo "w" apaga o arquivo no momento da abertura, antes mesmo do primeiro write. Para acrescentar ao fim, use "a", e para ter certeza de não sobrescrever um arquivo alheio — "x".
with open("log.txt", "a", encoding="utf-8") as f:
f.write("novo registro\n")
Lembre-se de que o write não acrescenta quebra de linha sozinho — o "\n" é escrito explicitamente. Também é possível usar print(..., file=f): ele coloca a quebra de linha por você.
Codificação UTF-8: de onde vêm os «caracteres quebrados»
Até o Python 3.15 a função open(), sem o parâmetro encoding, usava a codificação do sistema. No Linux e no macOS é UTF-8, mas no Windows com localidade em português — cp1252 ou cp1251 dependendo da configuração. Um script que funcionava no servidor exibia «Ã©» no lugar de «é» no notebook de um colega, ou falhava com UnicodeDecodeError.
No Python 3.15, pela PEP 686, o modo UTF-8 é ativado por padrão: open() sem encoding lê e grava em UTF-8 em todos os sistemas. Para voltar ao comportamento antigo use a variável de ambiente PYTHONUTF8=0 ou a opção -X utf8=0, e para pedir explicitamente a codificação do sistema — encoding="locale".
import sys
print(sys.flags.utf8_mode) # 1 no Python 3.15
open("legacy.txt", encoding="locale") # codificação do sistema
open("old_excel.csv", encoding="cp1251") # arquivo salvo em cp1251
O outro lado da moeda: um CSV antigo do Excel ou de um script no Windows, salvo em cp1251, agora será lido como UTF-8 e gerará UnicodeDecodeError. A solução é ler com encoding="cp1251" e regravar em UTF-8. A boa prática continua a mesma — indique encoding de forma explícita, e o código se comportará igual em qualquer versão.
pathlib em Python: caminhos como objetos
Juntar caminhos como strings ("data" + "/" + name) é inconveniente e quebra em sistemas diferentes. O módulo pathlib traz a classe Path, em que os caminhos são unidos pelo operador /, e o objeto tem propriedades claras.
from pathlib import Path
data = Path("data")
csv_path = data / "expenses.csv"
print(csv_path.name) # expenses.csv
print(csv_path.stem) # expenses
print(csv_path.suffix) # .csv
print(csv_path.parent) # data
print(csv_path.exists())
data.mkdir(parents=True, exist_ok=True)
(data / "hello.txt").write_text("Olá", encoding="utf-8")
print((data / "hello.txt").read_text(encoding="utf-8"))
for p in data.glob("*.csv"):
print(p)
Os métodos read_text e write_text substituem a combinação open + with em arquivos pequenos. Um objeto Path pode ser passado tanto para o open comum quanto para json e csv — todos eles o aceitam. Para obter um caminho relativo ao próprio script, e não à pasta atual, use Path(__file__).parent / "data".
No Python 3.14 a classe Path ganhou os métodos copy() e move(), e para cópias simples já não é necessário o shutil. Na mesma versão entrou na biblioteca padrão o módulo compression.zstd para compressão Zstandard (PEP 784).
JSON em Python: load, dump, loads, dumps
JSON é um formato de texto para troca de dados: é isso que devolvem as APIs, e nele se guardam configurações e relatórios. O módulo json faz duas operações: serialização (objeto Python → texto JSON) e desserialização (texto → objeto). São quatro funções, e a confusão entre elas é uma dúvida frequente.
| Função | O que faz |
|---|---|
json.dumps(obj) | Objeto → string JSON |
json.loads(s) | String JSON → objeto |
json.dump(obj, f) | Objeto → gravar em um arquivo aberto |
json.load(f) | Ler JSON de um arquivo aberto |
Para memorizar: a letra s no fim significa string — trabalho com texto; sem ela — trabalho com arquivo.
Correspondência de tipos
| Python | JSON |
|---|---|
dict | objeto {} |
list, tuple | array [] |
str | string |
int, float | número |
True / False | true / false |
None | null |
Uma tupla, ao ser lida de volta, vira lista; já conjuntos, datas e Decimal o módulo não conhece e gera TypeError: Object of type ... is not JSON serializable.
Como salvar JSON em arquivo
import json
from pathlib import Path
report = {
"month": "2026-10",
"total": 15230.5,
"categories": {"alimentação": 8200, "transporte": 2100},
"tags": ["casa", "trabalho"],
}
path = Path("data/report.json")
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
O parâmetro ensure_ascii=False mantém os acentos como estão. Sem ele, no lugar de «alimentação» aparecerá "alimentação" — JSON correto, mas ilegível para uma pessoa. O indent=2 faz a formatação com recuo.
Como ler JSON de um arquivo
with open("data/report.json", encoding="utf-8") as f:
data = json.load(f)
print(data["total"]) # 15230.5
print(data["categories"]["alimentação"]) # 8200
O resultado é um dicionário ou uma lista comuns, com os quais se trabalha pelos meios habituais. Se o arquivo não for JSON ou estiver truncado, será lançada a exceção json.JSONDecodeError, com o número da linha e a posição — ela deve ser capturada quando os dados vêm de fora.
Análise de uma string JSON e saída formatada
import json
raw = '{"name": "Ana", "skills": ["python", "sql"], "active": true}'
user = json.loads(raw)
print(user["skills"][0]) # python
print(json.dumps(user, ensure_ascii=False, indent=2, sort_keys=True))
sort_keys=True ordena as chaves na saída — é cômodo para comparar arquivos no git. Para formatar rapidamente um arquivo pelo terminal existe um utilitário embutido: python -m json.tool data/report.json.
A resposta de uma API HTTP também é uma string JSON. A biblioteca requests a interpreta com o método response.json(), e por dentro é o mesmo json.loads.
Armadilha: as chaves do JSON são sempre strings
d = {1: "janeiro", 2: "fevereiro"}
back = json.loads(json.dumps(d))
print(back == d) # False
print(back) # {'1': 'janeiro', '2': 'fevereiro'}
# back[1] -> KeyError, é preciso back["1"]
fixed = {int(k): v for k, v in back.items()}
No JSON as chaves de um objeto só podem ser strings, por isso chaves numéricas, depois do ciclo de serialização, viram texto. Ao ler, converta-as de volta.
Tipos próprios: datas e Decimal
import json
from datetime import date
def default(obj):
if isinstance(obj, date):
return obj.isoformat()
raise TypeError(f"não sei serializar {type(obj).__name__}")
print(json.dumps({"day": date(2026, 10, 1)}, default=default))
# {"day": "2026-10-01"}
A função passada no parâmetro default é chamada para cada objeto desconhecido. Em casos mais complexos é possível herdar de json.JSONEncoder e redefinir o método default.
Como acrescentar um item a um array JSON
Um JSON não pode ser estendido no fim, como um log de texto: o arquivo precisa ser lido, alterado e gravado por inteiro.
path = Path("data/items.json")
items = json.loads(path.read_text(encoding="utf-8")) if path.exists() else []
items.append({"id": len(items) + 1, "title": "novo"})
path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")
Se os registros são muitos e chegam o tempo todo, é mais prático o formato JSON Lines: um objeto JSON por linha, e o arquivo é aberto no modo "a".
CSV em Python: módulo csv
CSV é uma tabela em arquivo de texto: as linhas são registros, e os valores são separados por vírgulas. O módulo csv trata corretamente aspas, vírgulas dentro dos valores e quebras de linha — o que um split(",") manual não faz.
import csv
with open("data/expenses.csv", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
total = 0.0
for row in reader:
total += float(row["amount"])
print(row["date"], row["category"], row["amount"])
print("Total:", total)
csv.DictReader transforma cada linha em um dicionário, usando o cabeçalho do arquivo. Todos os valores são strings: números e datas precisam ser convertidos manualmente (float, int, date.fromisoformat). Um arquivo com separador «;» (assim o Excel em português salva) é lido com o parâmetro delimiter=";".
rows = [
{"date": "2026-10-01", "category": "alimentação", "amount": 450},
{"date": "2026-10-02", "category": "transporte", "amount": 60},
]
with open("data/out.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["date", "category", "amount"])
writer.writeheader()
writer.writerows(rows)
Na gravação é obrigatório o newline="": sem ele, no Windows aparecerão linhas vazias entre os registros. Se o arquivo for abrir no Excel, use encoding="utf-8-sig" — a marca BOM ajuda o Excel a reconhecer o UTF-8.
De CSV para JSON: exemplo completo
import csv
import json
from collections import defaultdict
from pathlib import Path
src = Path("data/expenses.csv")
dst = Path("data/report.json")
by_category = defaultdict(float)
with src.open(newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
by_category[row["category"]] += float(row["amount"])
report = {"total": sum(by_category.values()), "categories": dict(by_category)}
dst.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
print(dst.read_text(encoding="utf-8"))
Esse é o núcleo de qualquer script de relatório: entrada em CSV, agregação em um dicionário, saída em JSON.
Erros típicos
FileNotFoundError— o caminho é relativo e é contado a partir da pasta de execução, e não do script. UsePath(__file__).parent.- O arquivo foi zerado — foi aberto com
"w"em vez de"a". UnicodeDecodeError— arquivo em outra codificação; indique-a explicitamente (cp1251,utf-8-sig).TypeError: Object of type set is not JSON serializable— converta para lista ou passedefault.KeyErrordepois de carregar um JSON — chaves numéricas viraram strings.- O arquivo do projeto foi batizado
json.pyoucsv.py— ele sobrepõe o módulo padrão, eimport jsonimporta o seu arquivo.
Resumo
Para JSON em Python basta a biblioteca padrão: json.load/json.dump para arquivos, loads/dumps para strings, ensure_ascii=False e indent para uma saída legível. Abra arquivos com with e codificação explícita, monte os caminhos com pathlib, e leia tabelas com csv.DictReader. No curso de Python esse tema é praticado: o controle de gastos lê os lançamentos de um CSV e grava o relatório em JSON.
Perguntas frequentes
Qual a diferença entre json.load e json.loads?
O json.load lê o JSON de um objeto de arquivo aberto, enquanto o json.loads interpreta uma string. Do mesmo modo, o json.dump grava em arquivo, e o json.dumps devolve uma string.
Como gravar acentos em JSON sem as sequências \u?
Passe ensure_ascii=False para json.dump ou json.dumps e abra o arquivo com encoding="utf-8".
Preciso instalar alguma biblioteca para trabalhar com JSON em Python?
Não. O módulo json já vem com o Python; basta escrever import json.
Dá para guardar datas ou conjuntos no JSON?
Diretamente, não: é preciso convertê-los, por exemplo a data em string ISO com isoformat, e o conjunto em lista; ou passar uma função no parâmetro default.
Como ler um JSON grande que não cabe na memória?
O json.load comum lê o arquivo inteiro. Para grandes volumes, guarde os dados em JSON Lines e processe linha a linha com json.loads.
Este tema é abordado em profundidade, com prática, no curso “Python 3.15: от новичка до профи”.
Ir para o curso