Tutorial

JSON em Python: leitura e gravação de arquivos, CSV, pathlib e codificação UTF-8

Atualizado: · Skillaria.Top

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.

ModoO 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çãoO 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

PythonJSON
dictobjeto {}
list, tuplearray []
strstring
int, floatnúmero
True / Falsetrue / false
Nonenull

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. Use Path(__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 passe default.
  • KeyError depois de carregar um JSON — chaves numéricas viraram strings.
  • O arquivo do projeto foi batizado json.py ou csv.py — ele sobrepõe o módulo padrão, e import json importa 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.

Aprenda este tema em vídeo

Este tema é abordado em profundidade, com prática, no curso “Python 3.15: от новичка до профи”.

Ir para o curso