JSON を読み込んで Excel に書き出す
— json モジュール × openpyxl / pandas
1. json モジュールの基本
Python 標準ライブラリの json モジュールは追加インストール不要で使えます。JSON(JavaScript Object Notation)は設定ファイル・API レスポンス・データ交換など、現代のシステムで広く使われるデータ形式です。
| 関数 | 方向 | 説明 |
|---|---|---|
json.loads(s) | JSON文字列 → Python | 文字列を Python オブジェクトに変換 |
json.load(f) | JSONファイル → Python | ファイルオブジェクトから読み取り |
json.dumps(obj) | Python → JSON文字列 | Python オブジェクトを JSON 文字列に変換 |
json.dump(obj, f) | Python → JSONファイル | ファイルオブジェクトに書き込み |
| JSON 型 | Python 型 |
|---|---|
object {} | dict |
array [] | list |
string "..." | str |
| number(整数) | int |
| number(小数) | float |
true / false | True / False |
null | None |
2. 必要なライブラリのインストール
json は標準ライブラリのため追加インストール不要です。Excel 出力に openpyxl・pandas、HTTP リクエストに requests が必要な場合は pip でインストールします。
# json は Python 標準ライブラリ — インストール不要
# Excel 出力用
pip install openpyxl pandas
# API からデータ取得する場合
pip install requests
3. 文字列から JSON を読み取る(json.loads)
API レスポンスや変数に格納された JSON 文字列を Python オブジェクトに変換するには json.loads() を使います。
import json
# ── 基本的な JSON 文字列の解析 ────────────────────────────
json_str = '{"name": "田中太郎", "age": 30, "active": true}'
data = json.loads(json_str)
print(type(data)) #
print(data["name"]) # 田中太郎
print(data["age"]) # 30
print(data["active"]) # True
# ── 配列を含む JSON ───────────────────────────────────────
json_arr = '[{"id": 1, "score": 85}, {"id": 2, "score": 92}]'
items = json.loads(json_arr)
print(type(items)) #
for item in items:
print(item["id"], item["score"])
# ── ネストされた JSON ─────────────────────────────────────
json_nested = '''
{
"company": "株式会社サンプル",
"employees": [
{"name": "田中", "dept": "営業", "salary": 450000},
{"name": "鈴木", "dept": "開発", "salary": 520000}
],
"address": {"city": "東京", "zip": "100-0001"}
}
'''
obj = json.loads(json_nested)
print(obj["company"]) # 株式会社サンプル
print(obj["address"]["city"]) # 東京
for emp in obj["employees"]:
print(emp["name"], emp["salary"])
4. ファイルから JSON を読み取る(json.load)
JSON ファイルを直接読み込むには json.load() を使います。open() と組み合わせて使うのが基本パターンです。
import json
from pathlib import Path
# ── 基本的なファイル読み込み ──────────────────────────────
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
print(type(data)) # dict または list(JSON の最上位構造による)
# ── Path を使った読み込み ─────────────────────────────────
path = Path("data/employees.json")
with path.open(encoding="utf-8") as f:
employees = json.load(f)
# ── 読み込んだデータを操作 ───────────────────────────────
# data.json の内容例:
# [
# {"id": 1, "name": "田中", "dept": "営業", "sales": 520000},
# {"id": 2, "name": "鈴木", "dept": "開発", "sales": 380000}
# ]
for emp in employees:
print(f"{emp['id']}: {emp['name']} ({emp['dept']}) — {emp['sales']:,}円")
# ── エラーハンドリング ────────────────────────────────────
try:
with open("config.json", "r", encoding="utf-8") as f:
config = json.load(f)
except FileNotFoundError:
print("ファイルが見つかりません")
config = {}
except json.JSONDecodeError as e:
print(f"JSON の解析エラー: {e}")
config = {}
encoding="utf-8" を明示するのが確実です。Windows 環境では UTF-8-BOM 形式のファイルもあるため、その場合は encoding="utf-8-sig" を使います。
5. ネスト構造の展開
実際の JSON データはネスト(入れ子)構造になっていることが多く、Excel に書き出す前にフラット(平坦)な形式に展開する必要があります。
import json
# ── ネスト JSON のサンプル ────────────────────────────────
json_str = '''
[
{
"id": 1,
"name": "田中太郎",
"contact": {"email": "tanaka@example.com", "tel": "090-1234-5678"},
"scores": [85, 92, 78]
},
{
"id": 2,
"name": "鈴木花子",
"contact": {"email": "suzuki@example.com", "tel": "090-8765-4321"},
"scores": [90, 88, 95]
}
]
'''
data = json.loads(json_str)
# ── 手動でフラット化 ──────────────────────────────────────
def flatten_record(record: dict) -> dict:
"""1 レコードをフラットな dict に変換"""
flat = {}
for key, val in record.items():
if isinstance(val, dict):
# ネスト dict をドット記法でキー展開
for sub_key, sub_val in val.items():
flat[f"{key}.{sub_key}"] = sub_val
elif isinstance(val, list):
# リストはカンマ区切りの文字列に変換(または列ごとに展開)
flat[key] = ", ".join(str(v) for v in val)
else:
flat[key] = val
return flat
flat_data = [flatten_record(r) for r in data]
for row in flat_data:
print(row)
# {'id': 1, 'name': '田中太郎',
# 'contact.email': 'tanaka@example.com', 'contact.tel': '090-1234-5678',
# 'scores': '85, 92, 78'}
# ── リストを別列に展開する場合 ──────────────────────────
def flatten_with_list_cols(record: dict, list_key: str) -> dict:
flat = flatten_record(record)
# scores を score_1, score_2, ... に展開
scores = record.get(list_key, [])
flat.pop(list_key, None)
for i, val in enumerate(scores, start=1):
flat[f"{list_key}_{i}"] = val
return flat
expanded = [flatten_with_list_cols(r, "scores") for r in data]
for row in expanded:
print(row)
6. pandas.json_normalize でフラット化
ネスト JSON のフラット化は pandas.json_normalize() を使うと自動で行えます。手動の展開よりも簡潔に書けます。
import json
import pandas as pd
json_str = '''
[
{
"id": 1, "name": "田中太郎",
"contact": {"email": "tanaka@example.com", "tel": "090-1234-5678"},
"dept": {"name": "営業", "code": "S01"}
},
{
"id": 2, "name": "鈴木花子",
"contact": {"email": "suzuki@example.com", "tel": "090-8765-4321"},
"dept": {"name": "開発", "code": "D01"}
}
]
'''
data = json.loads(json_str)
# ── 基本的な json_normalize ──────────────────────────────
df = pd.json_normalize(data)
print(df.columns.tolist())
# ['id', 'name', 'contact.email', 'contact.tel', 'dept.name', 'dept.code']
print(df)
# ── セパレータを変更 ─────────────────────────────────────
df2 = pd.json_normalize(data, sep="_")
print(df2.columns.tolist())
# ['id', 'name', 'contact_email', 'contact_tel', 'dept_name', 'dept_code']
# ── ネストが深い場合 max_level で展開深度を制御 ─────────
df3 = pd.json_normalize(data, max_level=1)
# ── 配列フィールドを record_path で展開 ─────────────────
json_with_arr = '''
[
{"store": "東京店", "items": [{"name": "A", "qty": 10}, {"name": "B", "qty": 5}]},
{"store": "大阪店", "items": [{"name": "C", "qty": 8}]}
]
'''
data2 = json.loads(json_with_arr)
# items 配列を行に展開し、親の store を各行に結合
df4 = pd.json_normalize(data2, record_path="items", meta=["store"])
print(df4)
# name qty store
# 0 A 10 東京店
# 1 B 5 東京店
# 2 C 8 大阪店
7. JSON → Excel(openpyxl)
openpyxl を使って JSON データを直接 Excel に書き出す方法です。書式設定が必要な場合はこちらのアプローチが柔軟です。
import json
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment
# ── JSON ファイルを読み込む ───────────────────────────────
with open("employees.json", "r", encoding="utf-8") as f:
records = json.load(f)
# 例: [{"id":1,"name":"田中","dept":"営業","salary":450000}, ...]
# ── openpyxl でブックを作成 ──────────────────────────────
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "社員データ"
# ── ヘッダーを JSON キーから自動生成 ─────────────────────
if records:
headers = list(records[0].keys())
ws.append(headers)
# ヘッダー書式
header_fill = PatternFill(fill_type="solid", start_color="1F3864", end_color="1F3864")
header_font = Font(bold=True, color="FFFFFF")
for cell in ws[1]:
cell.fill = header_fill
cell.font = header_font
cell.alignment = Alignment(horizontal="center")
# ── データ行を書き出す ───────────────────────────────────
for record in records:
# headers の順番に値を並べる(キーが欠けている場合は None)
row = [record.get(h) for h in headers]
ws.append(row)
# ── 列幅を自動調整 ───────────────────────────────────────
for col in ws.columns:
max_len = max((len(str(c.value or "")) for c in col), default=8)
ws.column_dimensions[col[0].column_letter].width = max(max_len * 1.2, 10)
# ── 保存 ─────────────────────────────────────────────────
wb.save("employees_output.xlsx")
print(f"Excel に書き出しました: {len(records)} 件")
dict はキーの挿入順序を保持します。records[0].keys() でヘッダーを生成するとデータ順に列が並びます。列順を指定したい場合は明示的に headers = ["id", "name", "dept", "salary"] と定義しましょう。
8. JSON → Excel(pandas)
pandas を使う方法はコードが簡潔で、pd.read_json() で JSON を直接 DataFrame に変換できます。
import json
import pandas as pd
# ── 方法① pd.read_json でファイルから直接読み込む ─────────
df = pd.read_json("employees.json", encoding="utf-8")
print(df.head())
# ── 方法② json.load + DataFrame 変換 ─────────────────────
with open("employees.json", "r", encoding="utf-8") as f:
records = json.load(f)
df = pd.DataFrame(records)
# ── 方法③ json 文字列から直接 ────────────────────────────
import io
json_str = '[{"id":1,"name":"田中","salary":450000}]'
df = pd.read_json(io.StringIO(json_str))
# ── ネスト JSON は json_normalize でフラット化してから ──────
from pandas import json_normalize
with open("nested.json", "r", encoding="utf-8") as f:
nested = json.load(f)
df = pd.json_normalize(nested, sep="_")
# ── DataFrame を Excel に書き出す ─────────────────────────
df.to_excel("output.xlsx", index=False, sheet_name="データ")
# ── 書式付きで出力(ExcelWriter を使う) ─────────────────
from openpyxl.styles import Font, PatternFill, Alignment
with pd.ExcelWriter("output_styled.xlsx", engine="openpyxl") as writer:
df.to_excel(writer, index=False, sheet_name="社員データ")
wb = writer.book
ws = writer.sheets["社員データ"]
header_fill = PatternFill(fill_type="solid", start_color="1F3864", end_color="1F3864")
for cell in ws[1]:
cell.fill = header_fill
cell.font = Font(bold=True, color="FFFFFF")
cell.alignment = Alignment(horizontal="center")
for col in ws.columns:
ws.column_dimensions[col[0].column_letter].width = 16
print("Excel に書き出しました")
9. API レスポンスを Excel に保存
Web API から取得した JSON レスポンスを Excel に保存する実践的なパターンです。requests ライブラリと組み合わせて使います。
import requests
import pandas as pd
from datetime import datetime
# ── API から JSON を取得 ──────────────────────────────────
# 例: REST API(公開サンプル)
url = "https://jsonplaceholder.typicode.com/users"
response = requests.get(url, timeout=10)
response.raise_for_status() # 4xx/5xx でエラーを発生させる
data = response.json() # レスポンスを dict/list に変換
print(f"取得件数: {len(data)}")
# ── ネストを展開して DataFrame に ────────────────────────
df = pd.json_normalize(
data,
sep="_",
meta=["id", "name", "email", "phone", "website",
["address", "city"], ["company", "name"]]
)
# 必要な列だけ選択・列名を日本語にリネーム
df = df[["id", "name", "email", "phone", "address_city", "company_name"]]
df.columns = ["ID", "氏名", "メール", "電話", "都市", "会社名"]
# ── Excel に書き出す ─────────────────────────────────────
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_path = f"api_users_{timestamp}.xlsx"
with pd.ExcelWriter(output_path, engine="openpyxl") as writer:
df.to_excel(writer, index=False, sheet_name="ユーザー一覧")
ws = writer.sheets["ユーザー一覧"]
# ヘッダースタイル
from openpyxl.styles import Font, PatternFill, Alignment
for cell in ws[1]:
cell.fill = PatternFill(fill_type="solid",
start_color="2E4057", end_color="2E4057")
cell.font = Font(bold=True, color="FFFFFF")
cell.alignment = Alignment(horizontal="center")
# 列幅調整
col_widths = [6, 18, 30, 16, 14, 22]
for i, width in enumerate(col_widths, start=1):
ws.column_dimensions[ws.cell(1, i).column_letter].width = width
print(f"保存完了: {output_path}")
response.raise_for_status() で HTTP エラーを検出し、try/except requests.RequestException で通信エラーをキャッチするのがベストプラクティスです。
10. 実践スクリプト
複数の JSON ファイルを読み込んで結合し、書式付き Excel レポートを生成するスクリプトです。
"""
JSON → Excel 実践スクリプト
- ディレクトリ内の JSON ファイルを読み込んで結合し、書式付き Excel を生成
"""
import json
import openpyxl
import pandas as pd
from pathlib import Path
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
HEADER_FILL = PatternFill(fill_type="solid", start_color="1F3864", end_color="1F3864")
HEADER_FONT = Font(bold=True, color="FFFFFF", size=11)
EVEN_FILL = PatternFill(fill_type="solid", start_color="D9E1F2", end_color="D9E1F2")
THIN = Side(border_style="thin", color="CCCCCC")
ALL_BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
def load_json_files(directory: str) -> list[dict]:
"""指定ディレクトリ内の全 JSON ファイルを読み込んで結合"""
records = []
dir_path = Path(directory)
for json_file in sorted(dir_path.glob("*.json")):
with json_file.open(encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, list):
records.extend(data)
elif isinstance(data, dict):
records.append(data)
print(f" 読み込み: {json_file.name} ({len(data) if isinstance(data, list) else 1} 件)")
return records
def records_to_df(records: list[dict]) -> pd.DataFrame:
"""レコードリストを DataFrame に変換(ネストは json_normalize で展開)"""
df = pd.json_normalize(records, sep="_")
return df
def write_styled_excel(df: pd.DataFrame, output_path: str, sheet_name: str = "データ"):
"""書式付き Excel を出力"""
with pd.ExcelWriter(output_path, engine="openpyxl") as writer:
df.to_excel(writer, sheet_name=sheet_name, index=False)
# 書式適用
wb = openpyxl.load_workbook(output_path)
ws = wb[sheet_name]
ws.freeze_panes = "A2"
# ヘッダー
for cell in ws[1]:
cell.fill = HEADER_FILL
cell.font = HEADER_FONT
cell.alignment = Alignment(horizontal="center", vertical="center")
cell.border = ALL_BORDER
ws.row_dimensions[1].height = 22
# データ行
for r_idx, row in enumerate(ws.iter_rows(min_row=2), start=2):
for cell in row:
cell.border = ALL_BORDER
cell.alignment = Alignment(
horizontal="right" if isinstance(cell.value, (int, float)) else "left",
vertical="center"
)
if r_idx % 2 == 0:
cell.fill = EVEN_FILL
# 列幅自動調整
for col in ws.columns:
max_len = max((len(str(c.value or "")) for c in col), default=8)
ws.column_dimensions[col[0].column_letter].width = min(max_len * 1.3 + 2, 40)
wb.save(output_path)
wb.close()
def main():
json_dir = "json_data" # JSON ファイルが入ったディレクトリ
output = "combined_output.xlsx"
print(f"JSON ディレクトリ: {json_dir}")
records = load_json_files(json_dir)
print(f"\n合計 {len(records)} 件のレコードを読み込みました")
if not records:
print("データがありません")
return
df = records_to_df(records)
print(f"DataFrame 形状: {df.shape[0]}行 × {df.shape[1]}列")
print(f"列名: {df.columns.tolist()}")
write_styled_excel(df, output, sheet_name="JSON結合データ")
print(f"\nExcel を保存しました: {output}")
if __name__ == "__main__":
main()