API 抓回來的資料通常是 JSON,可是實際工作時,很多時候需要把它轉成 CSV 或 Excel,才比較好查資料或分享給別人。
因此今天我就要來動手練習,把 JSON 轉成不同格式存起來。
不同的工作需求會用到不同格式的資料。
JSON:程式設計師最常用,能表現複雜巢狀結構的資料,適合程式操作和系統間交換。
CSV:最輕量的格式,像純文字表格,很方便匯入許多分析工具。
Excel:給不懂程式的人用,非常友善。還支援更多功能,像是篩選、公式、圖表。
工程師可能要拿 JSON,方便用程式處理。
分析師要 CSV,好用各種資料分析軟體。
非技術背景的人可能愛用 Excel,因為它操作簡單、視覺化功能強。
只有一份 API 取得的資料,轉成各種格式,能滿足不同部門不同需求。
讓資料能更好、更方便被應用。
JSON
給系統或程式使用的格式
支援巢狀和複雜結構
不太適合用來做報表或直接看
CSV
純文字的表格格式,資料用逗號隔開
非常輕量,適合存簡單表格資料
很適合快速匯入很多資料分析工具
Excel
具備豐富格式,可以有顏色、公式、篩選等功能
更適合人閱讀和進一步加工
是非工程人員最喜歡使用的格式
步驟 1:準備開發環境
pip install requests pandas openpyxl
day16_convert_formats.py
的 Python 檔案。步驟 2:抓取 API 資料
使用免費的 JSONPlaceholder API,抓取用戶資料。
回傳的是 JSON 格式的清單。
import os, csv, json, argparse
from datetime import datetime
import requests
# 可重用的免費測試 API
API = "https://jsonplaceholder.typicode.com/users"
OUT_DIR = "output"
# 資料抓取
def fetch_users(timeout=10):
r = requests.get(API, timeout=timeout)
r.raise_for_status()
return r.json() # list[dict]
步驟 3:扁平化資料
有巢狀的資料需要攤平成一層才能方便使用。
例如從 address.city
抓出 city
,從 company.name
抓出 company
,保持結構簡單。
# 扁平化:把巢狀欄位攤平成一層
def pick(d, path, default=""):
cur = d
for k in path.split("."):
if isinstance(cur, dict) and k in cur:
cur = cur[k]
else:
return default
return cur
def flatten_user(u: dict) -> dict:
return {
"id": u.get("id"),
"name": u.get("name",""),
"username": u.get("username",""),
"email": u.get("email",""),
"city": pick(u, "address.city"),
"zipcode": pick(u, "address.zipcode"),
"phone": u.get("phone",""),
"website": u.get("website",""),
"company": pick(u, "company.name"),
}
步驟 4:存成不同格式
存 JSON:用 Python 的 json.dump()
。
存 CSV:用 csv.DictWriter()
,並加 utf-8-sig
解決 Excel 打開亂碼問題。
存 Excel:用 pandas.DataFrame.to_excel()
。
# 檔名工具
def stamp():
return datetime.now().strftime("%Y%m%d_%H%M%S")
# 輸出:JSON / CSV / XLSX
def save_json(rows, path):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(rows, f, ensure_ascii=False, indent=2)
print(f"JSON 已輸出:{path}({len(rows)} 筆)")
def save_csv(rows, path, fieldnames):
os.makedirs(os.path.dirname(path), exist_ok=True)
# 用 utf-8-sig 讓 Windows Excel 直接開不亂碼
tmp = path + ".tmp"
with open(tmp, "w", newline="", encoding="utf-8-sig") as f:
w = csv.DictWriter(f, fieldnames=fieldnames)
w.writeheader()
w.writerows(rows)
os.replace(tmp, path)
print(f"CSV 已輸出:{path}({len(rows)} 筆)")
def save_xlsx(rows, path):
try:
import pandas as pd # 需要 pandas + openpyxl
except ImportError:
print("未安裝 pandas/openpyxl,略過 XLSX 輸出。請先:pip install pandas openpyxl")
return
os.makedirs(os.path.dirname(path), exist_ok=True)
df = pd.DataFrame(rows)
df.to_excel(path, index=False)
print(f"Excel 已輸出:{path}({len(rows)} 筆)")
步驟 5:測試與使用
預設會輸出 JSON、CSV、Excel 三種格式。
也可以指定只輸出其中一種格式。
輸出檔案都會加上時間戳記,避免被覆蓋。
# 主流程
def main():
ap = argparse.ArgumentParser(description="API 資料轉換為 CSV / JSON / Excel")
ap.add_argument("--fmt", choices=["json","csv","xlsx","all"], default="all",
help="輸出格式(預設 all)")
ap.add_argument("--outdir", default=OUT_DIR, help="輸出資料夾(預設 output)")
ap.add_argument("--timeout", type=float, default=10.0, help="API 逾時秒數(預設 10)")
args = ap.parse_args()
try:
raw = fetch_users(timeout=args.timeout)
except requests.exceptions.RequestException as e:
print("API 失敗:", e)
return
rows = [flatten_user(u) for u in raw]
fields = ["id","name","username","email","city","zipcode","phone","website","company"]
ts = stamp()
# 檔名(帶時間戳,避免覆蓋)
json_path = os.path.join(args.outdir, f"users_{ts}.json")
csv_path = os.path.join(args.outdir, f"users_{ts}.csv")
xlsx_path = os.path.join(args.outdir, f"users_{ts}.xlsx")
# 依選擇輸出
if args.fmt in ("json", "all"):
save_json(rows, json_path)
if args.fmt in ("csv", "all"):
save_csv(rows, csv_path, fields)
if args.fmt in ("xlsx", "all"):
save_xlsx(rows, xlsx_path)
print("完成!")
if __name__ == "__main__":
main()
執行結果:
接著可以到 day16/output 的資料夾中找到剛剛輸出的三個檔案。
為什麼輸出的 CSV 是 Excel 分隔值檔案?
因為 Windows 會把 .csv
副檔名預設關聯到 Excel,檔案總管就用「Excel 分隔值檔案」當成中文名稱;但本質上它仍然是純文字的 CSV(逗號分隔),不是 Excel 的 .xlsx
活頁簿。
懂了 CSV 、 JSON 、 Excel 的使用情境與差異
實作從 API 、 扁平化 、 輸出多種格式
體驗了實務上「一份資料,多種格式」的工作流程