手順

Pythonで JSON を扱う:ファイルの読み書き、CSV、pathlib、UTF-8

更新日: · Skillaria.Top

Pythonでは、標準ライブラリの json モジュールだけでJSONを扱えます。json.load はファイルから読み込み、json.dump はファイルに書き込みます。json.loads と json.dumps は文字列を扱います。ファイルは with open(path, encoding="utf-8") で開き、パスは pathlib.Path で組み立て、表形式のデータは csv モジュールで読みます。

Pythonでのデータ形式JSONの扱いには、外部ライブラリは要りません。json モジュールは標準ライブラリに含まれていて、辞書やリストを文字列へ、また文字列から辞書やリストへ変換できます。JSONと組み合わせて、ほぼ必ず必要になるのが、with 文を使う open 関数、パスを扱う pathlib モジュール、表データを扱う csv モジュールの3つです。ここでは、動くコードと、つまずきやすいポイントを順に見ていきます。

Pythonのopen関数:モードと文字コード

テキストファイルは、組み込みの open(path, mode, encoding=...) で開きます。戻り値のファイルオブジェクトには read、readline、write メソッドがあり、for文で1行ずつ回せます。

モード動作
"r"読み込み(既定値)。ファイルがなければ FileNotFoundError
"w"書き込み。ファイルを作成するか、開いた時点で中身を空にする
"a"末尾に追記。既存の内容は残す
"x"新規作成。すでにあれば FileExistsError
"b"バイナリとして扱う("rb"、"wb")

なぜwithを使うのか

ファイルは必ず閉じる必要があります。閉じないとデータがディスクに書かれないことがあり、また、システム側でファイルのハンドルを握ったままになります。with 文を使えば、ブロックの中でエラーが起きても、自動的にファイルが閉じられます。

with open("notes.txt", "w", encoding="utf-8") as f:
    f.write("1行目\n")
    f.write("2行目\n")

with open("notes.txt", encoding="utf-8") as f:
    for line in f:
        print(line.rstrip("\n"))

for文でファイルを回すと、1行ずつ読み込まれるため、ファイル全体がメモリに載ることはありません。ギガバイト単位のログでも扱えます。f.read() は全体を1つの文字列で返し、f.readlines() は行のリストを返します。

文字列をファイルに書いても、既存の内容を消さないために

初心者が最もよくやるミスは、1行追加したいだけなのに "w" モードで開くことです。"w" は、最初の write より前、開いた時点で内容を消します。末尾に追加するには "a" を使います。すでにあるファイルを誤って上書きしたくないなら、"x" が安全です。

with open("log.txt", "a", encoding="utf-8") as f:
    f.write("新しい記録\n")

write は改行を自動で入れないため、"\n" を明示的に書く必要があります。print(..., file=f) を使えば、改行を自動で付けてくれます。

UTF-8の文字化けはなぜ起きるのか

Python 3.15 より前では、open() に encoding を指定しなければ、システムの既定の文字コードが使われました。LinuxとmacOSではUTF-8ですが、日本語のWindowsでは cp932(Shift_JIS系)です。Linuxサーバーで問題なく動いていたスクリプトが、Windowsのパソコンでは文字化けしたり、UnicodeDecodeError で止まったりしていました。

Python 3.15 では、PEP 686 によってUTF-8モードがデフォルトになりました。encoding を指定しない open() は、どのOSでもUTF-8で読み書きします。古い動作に戻すには、環境変数 PYTHONUTF8=0 を設定するか、-X utf8=0 を指定します。システムのエンコーディングを明示的に使うなら encoding="locale" と書きます。

import sys
print(sys.flags.utf8_mode)   # Python 3.15 では 1

open("legacy.txt", encoding="locale")   # システムの既定の文字コード
open("old_excel.csv", encoding="cp932")  # cp932 で保存されたファイル

注意点もあります。Excelやスクリプトで cp932 として保存された古いCSVは、UTF-8として読まれ、UnicodeDecodeError になります。その場合は、encoding="cp932" で読んでからUTF-8で保存し直します。どの版のPythonでも同じように動かすには、encoding を明示する習慣をつけてください。

pathlib:パスをオブジェクトとして扱う

パスを文字列で連結する("data" + "/" + name)のは扱いにくく、OSによって壊れることもあります。pathlib モジュールの Path クラスでは、/ 演算子でパスを結合でき、便利なプロパティも使えます。

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("こんにちは", encoding="utf-8")
print((data / "hello.txt").read_text(encoding="utf-8"))

for p in data.glob("*.csv"):
    print(p)

read_text と write_text は、小さなファイルなら open と with の組み合わせの代わりになります。Path オブジェクトは、通常の open にも、json や csv にも渡せます。スクリプトの置かれた場所を基準にしたパスは、Path(__file__).parent / "data" で作れます。

Python 3.14では、Path に copy() と move() メソッドが加わりました。単純なコピーであれば、もう shutil は必要ありません。同じバージョンでは、Zstandard圧縮のための標準モジュール compression.zstd も追加されました(PEP 784)。

Pythonのjson:load、dump、loads、dumps

JSONはテキストのデータ交換形式です。APIの応答や、設定ファイル、レポートの保存に使われます。json モジュールは、2つの操作を提供します。シリアライズ(Pythonオブジェクトからテキストへ)と、デシリアライズ(テキストからオブジェクトへ)です。関数は4つあり、混同しやすいので表にまとめます。

関数動作
json.dumps(obj)オブジェクトをJSON文字列にする
json.loads(s)JSON文字列をオブジェクトにする
json.dump(obj, f)オブジェクトを開いたファイルに書く
json.load(f)開いたファイルからJSONを読む

覚え方は簡単です。末尾の s は string(文字列)の意味で、文字列を扱うことを表します。s がなければ、ファイルを扱います。

型の対応

PythonJSON
dictオブジェクト {}
list、tuple配列 []
str文字列
int、float数値
True / Falsetrue / false
Nonenull

タプルは読み戻すとリストになります。また、集合、日付、Decimal はjsonモジュールが直接扱えず、TypeError: Object of type ... is not JSON serializable になります。

JSONをファイルに保存する

import json
from pathlib import Path

report = {
    "month": "2026-10",
    "total": 15230.5,
    "categories": {"食費": 8200, "交通費": 2100},
    "tags": ["家", "仕事"],
}

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)

ensure_ascii=False を指定すると、日本語がそのまま保存されます。指定しないと、「食費」は "食費" のような形で書かれます。これは正しいJSONですが、人間には読みにくくなります。indent=2 は、2つのスペースによる字下げで整形します。

JSONをファイルから読み込む

with open("data/report.json", encoding="utf-8") as f:
    data = json.load(f)

print(data["total"])               # 15230.5
print(data["categories"]["食費"])   # 8200

結果は普通の辞書やリストなので、いつもどおり扱えます。ファイルがJSONとして正しくないか、途中で切れている場合は、json.JSONDecodeError が、行番号と位置の情報とともに発生します。外部から届くデータを扱うときは、このエラーを捕まえるようにしてください。

JSON文字列の解析と見やすい出力

import json

raw = '{"name": "花子", "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 は、出力時にキーを並べ替えます。gitでファイルの差分を比べるときに便利です。ターミナルからJSONファイルを整形するには、標準の方法があります。python -m json.tool data/report.json です。

HTTP APIの応答も、JSON文字列です。requests ライブラリの response.json() は、内部で json.loads を使っています。

注意:JSONのキーは常に文字列

d = {1: "1月", 2: "2月"}
back = json.loads(json.dumps(d))
print(back == d)   # False
print(back)        # {'1': '1月', '2': '2月'}
# back[1] -> KeyError。back["1"] と書く必要がある

fixed = {int(k): v for k, v in back.items()}

JSONのオブジェクトのキーは文字列だけです。そのため、数値のキーは変換の往復で文字列になります。読み込んだ後で、元の型に戻してください。

独自の型:日付とDecimal

import json
from datetime import date

def default(obj):
    if isinstance(obj, date):
        return obj.isoformat()
    raise TypeError(f"{type(obj).__name__} はシリアライズできません")

print(json.dumps({"day": date(2026, 10, 1)}, default=default))
# {"day": "2026-10-01"}

default 引数に渡した関数は、扱えないオブジェクトごとに呼ばれます。より複雑な場合は、json.JSONEncoder を継承して default メソッドを上書きできます。

JSON配列に要素を追加する

JSONは、テキストのログのように末尾へ追記できません。ファイルを読み込み、変更して、全体を書き直す必要があります。

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": "新規"})
path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")

レコードが多く、継続的に追加される場合は、JSON Lines という形式が向いています。1行に1つのJSONオブジェクトを書き、ファイルを "a" モードで開きます。

Pythonのcsvモジュール

CSVは、テキストファイルの中の表です。行がレコードで、値はカンマで区切られます。csv モジュールは、引用符、値の中のカンマ、改行を正しく扱います。手作業の split(",") では、これができません。

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)

csv.DictReader は、ファイルの見出し行をキーにして、各行を辞書に変換します。値はすべて文字列です。数値や日付は自分で変換してください(float、int、date.fromisoformat)。区切り文字がセミコロン「;」のファイル(日本語版Excelが保存する形式のもの)は、delimiter=";" を指定して読みます。

rows = [
    {"date": "2026-10-01", "category": "食費", "amount": 450},
    {"date": "2026-10-02", "category": "交通費", "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)

書き込みのときは newline="" が必要です。これがないと、Windowsでは行の間に空行が入ります。ファイルをExcelで開く予定があるなら、encoding="utf-8-sig" を使ってください。先頭のBOMにより、ExcelがUTF-8として認識しやすくなります。

CSVからJSONへ:完全な例

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

これは、レポートを作るスクリプトの基本形です。CSVを入力として読み、辞書で集計し、JSONとして出力します。

よくあるミス

  • FileNotFoundError ― 相対パスは、スクリプトの場所ではなく、起動したフォルダを基準に解釈されます。Path(__file__).parent を使いましょう。
  • ファイルの中身が消えた ― "a" ではなく "w" で開いてしまった。
  • UnicodeDecodeError ― 別の文字コードのファイルです。cp932 や utf-8-sig を明示してください。
  • TypeError: Object of type set is not JSON serializable ― リストに変換するか、default 引数を渡します。
  • JSONを読み込んだ後の KeyError ― 数値のキーが文字列になっています。
  • 自作のファイルを json.py や csv.py と名付けた ― 標準モジュールより優先されてしまい、import json で自分のファイルが読み込まれます。

まとめ

JSONをPythonで扱うのに、標準ライブラリで十分です。ファイルには json.load と json.dump、文字列には loads と dumps を使います。読みやすい出力には ensure_ascii=False と indent を指定します。ファイルは with で、文字コードを明示して開き、パスは pathlib で組み立て、表は csv.DictReader で読みます。講座では、このテーマを実践で扱います。家計簿ツールがCSVから支出を読み込み、レポートをJSONに保存します。

よくある質問

json.load と json.loads の違いは何ですか?

json.load は開いたファイルオブジェクトからJSONを読み込み、json.loads は文字列を解析します。同様に json.dump はファイルへ書き込み、json.dumps は文字列を返します。

日本語をJSONに \u で始まるエスケープなしで保存するには?

json.dump または json.dumps に ensure_ascii=False を渡し、ファイルを encoding="utf-8" で開きます。

JSONを扱うために、ライブラリをインストールする必要はありますか?

いいえ。json モジュールは標準ライブラリに含まれているので、import json と書くだけで使えます。

JSONに日付や集合を保存できますか?

直接はできません。日付は isoformat で文字列にし、集合はリストに変換するか、default 引数に関数を渡して処理します。

メモリに入らないほど大きなJSONはどう読めばよいですか?

標準の json.load はファイル全体を読みます。大量のデータは JSON Lines 形式で保存し、1行ずつ json.loads で解析するのがよい方法です。

動画でこのテーマを学ぶ

このテーマはコース「Python 3.15: от новичка до профи」で実践とともに詳しく解説しています。

コースへ