Python で JSON を CSV に変換する方法

·Backend Developer·レビュー担当Priya Sharma·公開日

無料の JSONからCSVへの変換ツール をブラウザで直接使用 — インストール不要。

JSONからCSVへの変換ツール をオンラインで試す →

ほぼすべてのデータパイプラインは、同じステップに突き当たります。API が JSON を返すのに、 次の処理 — スプレッドシート、インポートスクリプト、Redshift の COPY コマンド — は CSV を必要としています。 Python で JSON を CSV に変換するのは簡単に見えますが、 ネストされたオブジェクト、キーの不一致、特別な処理が必要な日時値に直面すると話が変わります。 Python には2つの確かなアプローチがあります。依存ゼロのスクリプト向けの組み込み json + csv モジュール、そしてネストのフラット化や大規模データセット向けの pandas — またはコードなしで素早く1回だけ変換したい場合は オンライン JSON to CSV コンバーター も使えます。 このガイドでは両方のアプローチをエンドツーエンドで解説し、実行可能な Python 3.8+ のサンプルコードも紹介します。

  • csv.DictWriter は依存ゼロでディクショナリのリストを CSV に変換します — json.load() でパースし、writeheader() + writerows() を呼ぶだけ。
  • Windows での空白行を防ぐため、CSV ファイルは常に newline="" を指定して開いてください。
  • pd.json_normalize() はネストされた JSON を to_csv() 呼び出し前にフラットな DataFrame に変換します — 多段階のネストも自動処理。
  • DataFrame.to_csv() には index=False を渡してください — 指定しないと pandas が不要な行番号列を書き込みます。
  • 500 MB を超えるファイルには、ijson によるストリーミング JSON パースと csv.DictWriter を組み合わせてメモリ使用量を一定に保ちます。

JSON から CSV への変換とは?

JSON から CSV への変換は、JSON オブジェクトの配列を表形式に変換するものです。 各オブジェクトが1行になり、各キーが列ヘッダーになります。JSON は階層的で — オブジェクトは任意の深さにネストできます。CSV はフラットで — すべての値が行と列のグリッドに収まります。 すべてのオブジェクトが同じトップレベルのキーセットを共有していれば、変換はきれいに行えます。 ネストされたオブジェクト、配列、キーの不一致があると話が複雑になります。 生データは同一のまま — 構造だけが変わります。

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 — pandas なしで JSON を CSV に変換する

csv モジュールは すべての Python インストールに含まれています。pip install も 仮想環境の設定も不要です。 csv.DictWriter はディクショナリのリストを受け取り、各ディクショナリを CSV の1行として書き込み、 dict のキーを列ヘッダーにマッピングします。 fieldnames パラメータは列の順序と含めるキーの両方を制御します。

Python 3.8+ — json から csv への最小限のサンプル
import json
import csv

# サンプル JSON データ — 注文オブジェクトの配列
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

open()newline="" 引数は Windows では省略できません。指定しないと二重キャリッジリターンが発生し — Excel でデータ行の間に空白行として表示されます。macOS と Linux では無害なので、 常に含めるようにしましょう。

上のコードは文字列に対して json.loads() を使っています。ファイルハンドルから読み込む場合は json.load() (末尾の s なし)を使います。 これはよくある混乱のポイントです — 一方は文字列を読み込み、もう一方はファイルオブジェクトを読み込みます。

Python 3.8+ — JSON ファイルを読み込んで CSV ファイルに書き込む
import json
import csv

with open("server_metrics.json", encoding="utf-8") as jf:
    metrics = json.load(jf)  # ファイルオブジェクトには json.load() を使用

# 明示的な fieldnames で列の順序を制御
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)

# 指定した5列だけが、その順序通りに出力される

extrasaction="ignore" を設定すると、fieldnames リストにないキーを持つ dict が来ても黙って無視します。 デフォルトは "raise" で、 予期しないキーがある dict に対して ValueError を投げます。予期しない状況への許容度に合わせて選んでください。

注意:csv.DictWritercsv.writer の違い:DictWriter は dict のキーを列の位置に自動でマッピングします。csv.writer は 生のリストを行として書き込み — 列の順序は自分で管理します。JSON のレコードはすでにディクショナリなので、 JSON から CSV への変換にはほぼ常に DictWriter が正しい選択です。

Python の csv モジュールには3つの名前付きダイアレクトが付属しています。 excel (カンマ区切り、CRLF 行末 — デフォルト)、 excel-tab (タブ区切り、CRLF 行末)、そして unix (LF 行末、数値以外のすべてのフィールドをクォート)です。ダイアレクト名を dialect 引数として csv.DictWriter に渡します。 ターゲットシステムが特殊なクォートやデリミタのルールを持つ場合は、 csv.register_dialect() でカスタムダイアレクトを定義することもできます。ほとんどの JSON から CSV への変換では excel ダイアレクトで問題ありませんが、 awk sort などの POSIX ツールで処理するファイルを書き込む場合は unix に切り替えてください。

非標準型の処理:datetime、UUID、Decimal

API からの JSON には日付が ISO 文字列として、UUID がハイフン区切り文字列として、 金額がフロートとして含まれることがよくあります。これらを処理のために Python オブジェクトに変換してから CSV に書き込む場合、文字列に戻す必要があります。 csv モジュールはすべての値に str() を呼び出すので、ほとんどの型はそのまま動きます。しかし datetime オブジェクトはデフォルトの文字列表現が読みにくく、 Decimal 値は指数表記を避けるために明示的なフォーマットが必要です。

Python 3.8+ — CSV 書き込み前に datetime と Decimal を前処理する
import json
import csv
from datetime import datetime, timezone
from decimal import Decimal
from uuid import UUID

# Python 型を持つ解析済み API レスポンスをシミュレート
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:
    """非文字列型を 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

prepare_row() 関数はここでの正しいアプローチです。 csv.DictWriter にカスタム型を教えようとするのではなく、書き込む前に各レコードを文字列に正規化します。 datetime オブジェクトには str() に頼るより.isoformat() を明示的に呼ぶ方が好ましいです — 出力フォーマットが予測しやすく、下流のパーサーも ISO 8601 を確実に処理できます。

警告:Decimal 値をフォーマットせずに渡すと、 非常に小さいまたは大きな数値が指数表記(例:1.5E+7)で表示される場合があります。 財務データを CSV に書き込む場合は、f"{value:.2f}" のような明示的な f 文字列で Decimal を常にフォーマットしてください。

多くのカスタム型を持つパイプラインでの代替パターンは、 json.JSONEncoder を拡張することです。 サブクラス化して default() メソッドをオーバーライドし、各カスタム型に対して JSON シリアライズ可能な値を返すようにしてから、 サブクラスを json.dumps()cls 引数に渡します。カスタムエンコーダーを通して再エンコードしてから CSV に書き込むことで、 行ごとの prepare_row() 呼び出しなしに全型を一括で正規化できます。上記の prepare_row() パターンはスポット的なスクリプトには簡潔ですが、同じカスタム型を持つドメインモデルが 多くのパイプラインステージやマイクロサービスで共有される場合は JSONEncoder サブクラスの方がスケールします。

csv.DictWriter パラメータリファレンス

完全なコンストラクタシグネチャは csv.DictWriter(f, fieldnames, restval="", extrasaction="raise", dialect="excel", **fmtparams) です。ほとんどはデフォルトが適切です。実際に変更するのは fieldnames delimiter、 そして extrasaction くらいです。

パラメータ
デフォルト
説明
f
ファイルオブジェクト
(必須)
write() メソッドを持つオブジェクト — 通常は open() から取得
fieldnames
シーケンス
(必須)
CSV 出力の列順を定義するキーのリスト
restval
str
""
fieldnames に含まれるキーが dict に存在しない場合に書き込む値
extrasaction
str
"raise"
"raise" は余分なキーに対して ValueError を投げる。"ignore" は黙って無視する
dialect
str / Dialect
"excel"
定義済みのフォーマットルール — "excel"、"excel-tab"、または "unix"
delimiter
str
","
フィールドを区切る1文字 — TSV 出力には "\t" を使用
quotechar
str
"
デリミタを含むフィールドをクォートするための文字
quoting
int
csv.QUOTE_MINIMAL
クォートを適用するタイミングを制御 — MINIMAL、ALL、NONNUMERIC、NONE
lineterminator
str
"\r\n"
各行の末尾に追加される文字列 — Unix スタイルの出力には "\n" に上書き

pandas — DataFrame で JSON を CSV に変換する

すでに pandas を多用しているコードベースで作業している場合、またはネストされたオブジェクトを フラット化する必要がある場合、pandas アプローチは stdlib バージョンよりもはるかにコードが少なくなります。 トレードオフは pandas が約 30 MB の依存関係である点です。使い捨てスクリプトならそれで構いません。 本番環境に出荷する Docker イメージなら、stdlib アプローチの方が軽量です。

Python 3.8+ — pandas の read_json と to_csv を使う
import pandas as pd

# JSON 配列を直接 DataFrame に読み込む
df = pd.read_json("warehouse_inventory.json")

# CSV に書き込む — index=False で自動生成の行番号を防ぐ
df.to_csv("warehouse_inventory.csv", index=False)

# 以上。2行だけ。pandas は列の型を自動推論する。

index=False フラグは毎回調べてしまうものの一つです。指定しないと、pandas が CSV の最初の列として0, 1, 2, ... の列を書き込みます。それを必要とする人はほとんどいません。

json_normalize でネストされた JSON をフラット化する

実際の API レスポンスがフラットであることはほとんどありません。注文には配送先住所が含まれ、 ユーザーにはネストされた設定が含まれ、テレメトリイベントにはネストされたメタデータが含まれます。 pd.json_normalize() はネストされたディクショナリを走査し、ドット区切りの名前を持つ列にフラット化します。

Python 3.8+ — json_normalize を使ってネストされた JSON をフラット化する
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 はネストされた dict をフラット化 — sep で区切り文字を制御
df = pd.json_normalize(orders, sep="_")
df.to_csv("flat_orders.csv", index=False)

# 結果の列:
# order_id, placed_at, customer_name, customer_email, customer_tier,
# shipping_method, shipping_address_city, shipping_address_state,
# shipping_address_zip, total

sep="_" パラメータはネストされたキー名の結合方法を制御します。デフォルトは "." で、customer.name のような列名になります。列名のドットは SQL インポートや一部のスプレッドシートの数式でトラブルを起こすため、 アンダースコアを好んで使います。

ネストされたキーの下にレコード配列をラップしている API レスポンスには、 record_path パラメータを使います。レスポンスが {"data": {"orders": [...]}} の形の場合、 record_path=["data", "orders"] を渡してリストにナビゲートします。オプションの meta パラメータを使うと、ネストされたレコードと一緒に親レベルのフィールドを取り込めます — レスポンスに含まれるトップレベルのページネーション情報(ページ番号、合計件数)を すべての行の列として持ちたい場合に便利です。 record_path meta を組み合わせれば、カスタム前処理なしに現実世界のほとんどのネストされた API レスポンスの形に対応できます。

DataFrame.to_csv() パラメータリファレンス

DataFrame.to_csv() には 20 以上のパラメータがあります。JSON から CSV へのワークフローで重要なものを紹介します。

パラメータ
デフォルト
説明
path_or_buf
str / Path / None
None
ファイルパスまたはバッファ — None の場合は CSV を文字列として返す
sep
str
","
フィールドデリミタ — TSV には "\t" を使用
index
bool
True
最初の列として行インデックスを書き込む — ほぼ常に False に設定する
columns
list
None
出力する列のサブセットと並び順を指定
header
bool / list
True
列名を書き込む — 既存ファイルに追記する場合は False に設定
encoding
str
"utf-8"
出力エンコーディング — Windows の Excel 互換には "utf-8-sig" を使用
na_rep
str
""
欠損値(NaN、None)の文字列表現
quoting
int
csv.QUOTE_MINIMAL
フィールドがクォートされるタイミングを制御
Python 3.8+ — よく使うパラメータを上書きした to_csv
import pandas as pd

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

# 明示的なエンコーディングと欠損値の処理を含む TSV 出力
df.to_csv(
    "telemetry_events.tsv",
    sep="\t",
    index=False,
    encoding="utf-8",
    na_rep="NULL",
    columns=["event_id", "timestamp", "source", "severity", "message"],
)

# シェルスクリプトでパイプするために stdout に書き込む
print(df.to_csv(index=False))

# 文字列として返す(ファイルは書き込まれない)
csv_string = df.to_csv(index=False)
print(len(csv_string), "文字")

ファイルと API レスポンスから JSON を CSV に変換する

最もよくある実際のシナリオは2つです。ディスク上のファイルから JSON を読み込んで変換するか、 HTTP API から JSON を取得して結果を CSV として保存するかです。開発中はエラー処理なしで済ませられますが、 本番環境ではそれが午前2時のアラートになります。ファイルが存在しない可能性があり、API が JSON の代わりに 4xx または 5xx ステータスコードを返すことがあり、レスポンスボディが配列ではなくエラーオブジェクトの場合があり、 ネットワークタイムアウトによって JSON が切り詰められることもあります。以下のパターンはこれらすべてを 明示的に処理し、エラーを stderr にログし、呼び出し元がゼロ行の出力を検出してアラートできるよう行数を返します。

ディスク上のファイル — 読み込み、変換、保存

Python 3.8+ — エラー処理付きで JSON ファイルを CSV に変換する
import json
import csv
import sys

def json_file_to_csv(input_path: str, output_path: str) -> int:
    """オブジェクトの配列を含む JSON ファイルを CSV に変換する。
    書き込んだ行数を返す。
    """
    try:
        with open(input_path, encoding="utf-8") as jf:
            data = json.load(jf)
    except FileNotFoundError:
        print(f"エラー: {input_path} が見つかりません", file=sys.stderr)
        return 0
    except json.JSONDecodeError as exc:
        print(f"エラー: {input_path} の JSON が不正です: {exc.msg} ({exc.lineno} 行目)", file=sys.stderr)
        return 0

    if not isinstance(data, list) or not data:
        print(f"エラー: {input_path} に空でない JSON 配列が必要です", file=sys.stderr)
        return 0

    # 全レコードのユニークなキーを収集 — スキーマが一致しない場合に対応
    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"{rows} 行を deploy_logs.csv に書き込みました")

HTTP API レスポンス — 取得して変換する

Python 3.8+ — API から JSON を取得して CSV として保存する
import json
import csv
import urllib.request
import urllib.error

def api_response_to_csv(url: str, output_path: str) -> int:
    """REST API エンドポイントから JSON を取得して 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"エラー: API がステータス {resp.status} を返しました")
                return 0
            body = resp.read().decode("utf-8")
    except urllib.error.URLError as exc:
        print(f"エラー: {url} に到達できません: {exc.reason}")
        return 0

    try:
        records = json.loads(body)
    except json.JSONDecodeError as exc:
        print(f"エラー: API が不正な JSON を返しました: {exc.msg}")
        return 0

    if not isinstance(records, list) or not records:
        print("エラー: API から空でない JSON 配列を期待しています")
        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"{rows} 件のデプロイメントを CSV にエクスポートしました")
注意:上記の例では依存関係なしのスクリプトにするために標準ライブラリの urllib を使っています。requests がインストール済みなら、urllib の部分を resp = requests.get(url, timeout=30); records = resp.json() に置き換えてください — 残りの CSV 書き込みコードはそのままで動きます。

コマンドラインでの JSON から CSV への変換

ターミナルで1行だけ必要なこともあります。Python の -c フラグを使えばスクリプトファイルを作らずにすばやく変換できます。より複雑な変換には、jq でデータを整形してからパイプします。

bash — json から csv への1行変換
# Python ワンライナー: stdin から JSON を読み込み、stdout に CSV を書き込む
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)
"

# 出力をファイルに保存
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 — argparse を使った自己完結型の CLI スクリプト
# json2csv.py として保存して実行: python3 json2csv.py input.json -o output.csv
python3 -c "
import json, csv, argparse, sys

parser = argparse.ArgumentParser(description='JSON 配列を CSV に変換する')
parser.add_argument('input', help='JSON ファイルのパス')
parser.add_argument('-o', '--output', default=None, help='出力 CSV パス(デフォルト: stdout)')
parser.add_argument('-d', '--delimiter', default=',', help='CSV デリミタ')
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'{len(data)} 行を {args.output} に書き込みました', file=sys.stderr)
" "$@"
bash — jq + csvkit を使った複雑な変換
# csvkit のインストール: pip install csvkit

# jq でフィールドを選択・整形し、in2csv が CSV フォーマットを処理する
cat api_response.json | jq '[.[] | {id: .order_id, customer: .customer.name, total}]' | in2csv -f json > orders.csv

# Miller (mlr) も JSON から CSV への別の選択肢
mlr --json2csv cat orders.json > orders.csv

Miller(mlr)は JSON、CSV、TSV をファーストクラスのフォーマットとして扱うスタンドアロンバイナリで、 Python ランタイムが不要です。 --json2csv フラグは JSON 入力を1回のパスで CSV に変換し、出力を書き込む前に同じコマンドで Miller の verb を連結してフィルタリング、ソート、列名変更ができます。macOS では Homebrew(brew install miller) またはお使いの Linux パッケージマネージャーでインストールできます。 Python 環境を起動せずに高速な JSON から CSV への変換が必要な CI パイプラインで特に便利です。

高パフォーマンスの代替手段 — pyarrow を使った pandas

数千万行規模のデータセットには、 pyarrow バックエンドを使った pandas がデフォルトよりも大幅に高速に読み書きできます。 C バックエンドの Arrow エンジンは、Python の行ごとの csv モジュールよりも列指向データを 効率的に処理します。API は同じ — エンジンパラメータを設定するだけです。

bash — pyarrow のインストール
pip install pyarrow
Python 3.8+ — より高速な CSV 書き込みのために pyarrow を使った pandas
import pandas as pd

# pyarrow エンジンで JSON を読み込む(大きなファイルの高速パース)
df = pd.read_json("sensor_readings.json", engine="pyarrow")

# to_csv にはエンジンパラメータがないが、読み込みと書き込みの間の
# DataFrame 操作は pyarrow の列指向レイアウトから恩恵を受ける
df.to_csv("sensor_readings.csv", index=False)

# 本当に大きなエクスポートには、CSV の代わりに Parquet への書き込みを検討する
# — バイナリフォーマット、5〜10倍小さく、型を保持する
df.to_parquet("sensor_readings.parquet", engine="pyarrow")

数百 MB 以上の JSON を処理していて、最終的なコンシューマーが Parquet を受け入れるなら、 CSV は完全にスキップしましょう。Parquet は小さく、列の型を保持し、Redshift と BigQuery の両方が ネイティブでロードできます。CSV は損失のあるフォーマット — すべての値が文字列になります。

ターミナルでのシンタックスハイライト付き出力

rich ライブラリはターミナルで罫線、整列、色付きのテーブルを描画します — 出力ファイルを開かずに開発中の変換をプレビューするのに便利です。

bash — rich のインストール
pip install rich
Python 3.8+ — rich でターミナルに CSV 出力をプレビューする
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="サーバーメトリクスのプレビュー", 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)
# ターミナルに罫線付きのカラーハイライトテーブルを描画する
警告:Rich はターミナル表示専用です。CSV ファイルの生成には使わないでください — ANSI エスケープコードが 出力を壊します。ファイルへの書き込みには csv.DictWriter または DataFrame.to_csv() を使い、rich はプレビューのみに使ってください。

大きな JSON ファイルの処理

json.load() はファイル全体をメモリに読み込みます。200 MB の JSON ファイルでは、生テキストが約 200 MB、 さらに Python オブジェクトのオーバーヘッドが加わり、ヒープ使用量は 500 MB 以上になりやすいです。 100 MB を超えるファイルには、 ijson で入力をストリーミングし、行ごとに CSV を書き込みましょう。

bash — ijson のインストール
pip install ijson

ijson を使って JSON 配列を CSV にストリーミングする

Python 3.8+ — 定数メモリで大きな JSON 配列を CSV にストリーミングする
import ijson
import csv

def stream_json_to_csv(json_path: str, csv_path: str) -> int:
    """大きな JSON 配列をすべてメモリに読み込まずに CSV に変換する。"""
    with open(json_path, "rb") as jf, open(csv_path, "w", newline="", encoding="utf-8") as cf:
        # ijson.items はトップレベル配列の各要素を1つずつ yield する
        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"{rows} レコードを CSV にストリーミングしました")

NDJSON / JSON Lines — 1行に1オブジェクト

NDJSON(Newline-Delimited JSON)、JSON Lines または .jsonl とも呼ばれるこのフォーマットは、ラッピングの配列なしに1行に1つの有効な JSON オブジェクトを格納します。 このフォーマットは、ログパイプライン、イベントストリーム(Kafka、Kinesis)、および Elasticsearch や BigQuery などのサービスからの一括エクスポートでよく使われます。 各行は自己完結した JSON オブジェクトなので、ファイルハンドルに対するプレーンな Python の for ループで NDJSON ファイルを処理できます — ijson ライブラリは不要です。 ソースデータがすでに JSON Lines フォーマットの場合、これが最もシンプルなストリーミングアプローチであり、 ファイルサイズに関係なくメモリを一定に保ちます。

Python 3.8+ — NDJSON を1行ずつ CSV に変換する
import json
import csv

def ndjson_to_csv(ndjson_path: str, csv_path: str) -> int:
    """改行区切り JSON ファイルを1行ずつ CSV に変換する。"""
    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  # 不正な行はスキップ

    return count

rows = ndjson_to_csv("access_log.ndjson", "access_log.csv")
print(f"{rows} 件のログエントリを CSV に変換しました")
注意:JSON ファイルが 100 MB を超えたらストリーミングに切り替えてください。1 GB の JSON 配列を json.load() で読み込むと、Python オブジェクトのオーバーヘッドにより 3〜5 GB の RAM を消費することがあります。ijson を使えば、ファイルサイズに関わらずメモリは一定に保たれます。 小さなファイルをさっと変換したいだけなら、 JSON to CSV コンバーター に貼り付ける方が手軽です。

よくあるミス

open() に newline='' を忘れる — Windows での空白行

問題: csv モジュールは \r\n の行末を書き込みます。newline='' なしでは Python のテキストモードが Windows でさらに \r を追加し、二重スペースの出力になります。

解決策: CSV 書き込み用にファイルを開く際は常に newline='' を渡してください。macOS/Linux では無害です。

Before · Python
After · Python
with open("output.csv", "w") as f:
    writer = csv.DictWriter(f, fieldnames=columns)
    writer.writeheader()
    writer.writerows(data)
# Windows ではすべてのデータ行の間に空白行が入る
with open("output.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=columns)
    writer.writeheader()
    writer.writerows(data)
# 全プラットフォームでクリーンな出力
pandas の to_csv() で index=False を忘れる

問題: index=False なしでは、pandas が自動インクリメントの行番号列(0, 1, 2, ...)を追加し、元の JSON には存在しなかったデータで CSV が汚染されます。

解決策: to_csv() に index=False を渡してください。インデックス列が本当に必要なら df.index.name = 'row_num' で明示的に名前を付けてください。

Before · Python
After · Python
df = pd.read_json("events.json")
df.to_csv("events.csv")
# CSV に余分な名前のない列が入る: ,event_id,timestamp,...
# 先頭のカンマが多くの CSV パーサーを壊す
df = pd.read_json("events.json")
df.to_csv("events.csv", index=False)
# クリーンな CSV: event_id,timestamp,...
キーが一致しないレコードで records[0].keys() を使う

問題: JSON オブジェクトのキーが異なる場合(一部のレコードにオプションフィールドがある場合)、最初のレコードのキーを fieldnames として使うと、後のレコードにしか現れない列が黙って削除されます。

解決策: DictWriter を作成する前に全レコードのユニークなキーを収集してください。

Before · Python
After · Python
records = json.load(f)
writer = csv.DictWriter(out, fieldnames=records[0].keys())
# records[2] にしか現れない "discount" フィールドが消える
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="")
# 全レコードのすべてのキーが列として含まれる
フラット化せずにネストされた dict を CSV に直接書き込む

問題: csv.DictWriter はネストされた dict に str() を呼び出すため、"{'city': 'Portland'}"のような生の Python repr が列の値になります。

解決策: pd.json_normalize() またはカスタムのフラット化関数を使って、ネストされたオブジェクトを最初にフラット化してください。

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

csv.DictWriter vs pandas — 簡易比較

手法
ネスト JSON
カスタム型
ストリーミング
依存関係
インストール
csv.DictWriter
✗(手動フラット化)
✓(行単位)
なし
不要(stdlib)
csv.writer
✓(行単位)
なし
不要(stdlib)
pd.DataFrame.to_csv()
✗(フラットのみ)
✓(dtypes 経由)
pandas + numpy
pip install
pd.json_normalize() + to_csv()
✓(dtypes 経由)
pandas + numpy
pip install
csv.writer + json_flatten
flatten_json
pip install
jq + csvkit(CLI)
✓(jq 経由)
N/A
jq, csvkit
システムインストール

依存ゼロが必要で JSON がフラットであり、スクリプトが制限された環境(CI コンテナ、Lambda 関数、 組み込み Python)で動く場合は csv.DictWriter を使ってください。 JSON がネストされていて、エクスポート前にデータの変換やフィルタリングが必要な場合、または すでに pandas のワークフローにいる場合は pd.json_normalize() + to_csv() を使ってください。 メモリに収まらないファイルには、定数メモリのストリーミングのために ijson csv.DictWriter を組み合わせてください。

コードなしのすばやい変換には、 ToolDeck の JSON to CSV コンバーター が Python のセットアップなしで対応します。

よくある質問

pandas を使わずに Python で JSON を CSV に変換するには?

組み込みの json モジュールと csv モジュールを使用します。json.load() で JSON ファイルをディクショナリのリストにパースし、最初のディクショナリのキーから fieldnames を取得して csv.DictWriter を作成し、writeheader() と writerows() を呼び出します。この方法は外部依存ゼロで、あらゆる Python 3.x 環境で動作します。また、DataFrame の割り当てオーバーヘッドがないため、小さなファイルでは pandas より高速です。JSON オブジェクトのキーがレコード間で一致しない場合は、dict.fromkeys(k for r in records for k in r) で全ユニークキーを収集してから fieldnames に渡すことで、列の欠落を防げます。

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)

CSV に変換するとき、ネストされた JSON はどう処理しますか?

フラットな JSON 配列は直接 CSV の行にマップできますが、ネストされたオブジェクトは最初にフラット化する必要があります。pandas を使う場合、pd.json_normalize() が自動的に処理してくれます — ネストされたキーをドット区切りで結合します(例:「address.city」)。pandas なしの場合は、ディクショナリを再帰的に走査してキーをデリミタで連結する関数を書きます。複数レベルの深いネスト構造にも json_normalize は1回のパスで対応します。sep パラメータはキーセグメント間の結合文字を制御します — SQL インポートやスプレッドシートの数式の互換性を考えると、デフォルトのドットよりアンダースコアの方が安全です。

Python
import pandas as pd

nested_data = [
    {"id": "ord_91a3", "customer": {"name": "田中花子", "email": "tanaka.hanako@example.com"}},
]
df = pd.json_normalize(nested_data, sep="_")
# カラム: id, customer_name, customer_email
df.to_csv("flat_orders.csv", index=False)

Windows でデータ行の間に空白行が入るのはなぜですか?

csv モジュールはデフォルトで \r\n の行末を書き込みます。Windows でテキストモードでファイルを開くと、さらに \r が追加されて \r\r\n になり、空白行として表示されます。解決策は open() に常に newline="" を渡すことです。これにより Python は行末の変換を行わず、csv モジュールに処理を委ねます。このパターンは OS に関係なく必要です — macOS と Linux では無害で、Windows では必須です。Python のドキュメントでも、CSV 書き込み用にファイルを開く正しい方法として csv モジュールのセクションで明示されています。

Python
# 誤り — Windows で空白行が入る
with open("output.csv", "w") as f:
    writer = csv.writer(f)

# 正しい — newline="" で二重の \r を防ぐ
with open("output.csv", "w", newline="") as f:
    writer = csv.writer(f)

既存の CSV ファイルに JSON レコードを追記するには?

ファイルを追記モード("a")で開き、同じ fieldnames で DictWriter を作成します。ヘッダー行はすでに存在するため writeheader() はスキップします。pandas では to_csv(mode="a", header=False) を使います。既存ファイルの列順と一致させてください。そうしないとデータが間違った列に入ります。既存ファイルの列順が不明な場合は、csv.DictReader でファイルを開き、その fieldnames 属性から列名を読み取ってから追記用のライターを作成してください。

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)

Python で大きな JSON ファイルを最も速く CSV に変換するには?

500 MB 未満のファイルでは、pd.read_json() の後に to_csv() を呼ぶ方法が最速の単一呼び出しアプローチです — pandas は内部で最適化された C コードを使用しています。500 MB を超えるファイルには、ijson を使って JSON レコードをストリーミングし、csv.DictWriter で行ごとに CSV に書き込みます。これにより、ファイルサイズに関わらずメモリ使用量を一定に保てます。NDJSON ファイル(1行に1つの JSON オブジェクト)の場合は ijson すら不要です — ファイルハンドルに対する普通の Python for ループで各行を独立して処理でき、サードパーティライブラリなしで定数メモリを実現できます。

Python
# メモリに収まるファイルに高速
import pandas as pd
df = pd.read_json("large_dataset.json")
df.to_csv("large_dataset.csv", index=False)

# メモリに収まらないファイルにストリーミング
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)

Python でファイルではなく stdout に CSV 出力できますか?

はい。csv.writer() または csv.DictWriter() のファイルオブジェクトとして sys.stdout を渡します。シェルスクリプトでの出力パイプやクイックデバッグに便利です。pandas では to_csv(sys.stdout, index=False) を呼ぶか、to_csv(None) で文字列として取得して print できます。一時ファイルは不要です。Windows の stdout に書き込む場合は、stdout がデフォルトでテキストモードで開くため、二重キャリッジリターン問題を避けるために最初に sys.stdout.reconfigure(newline="") を呼び出してください。

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

関連ツール

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 Sharma技術レビュアー

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.