API 回來的 JSON 常常不是平面的表格,而是巢狀結構。
因此今天我要來學習如何解析並整理成清單或 CSV。
不只是簡單的「id」或「name」欄位,還會有子物件或清單的結構。
巢狀資料不方便直接查詢或輸出成報表,因此需要把它攤平成一層,像是把 address.city
變成 city
。
扁平化後的資料,可以存成 CSV、Excel 等格式,讓分析或分享更加方便。
下面是新聞 API 回傳的 JSON 範例,結果很常見到有巢狀資料結構:
{
"count": 1234,
"next": "https://.../?page=2",
"previous": null,
"results": [
{
"id": 1001,
"title": "SpaceX launches...",
"url": "https://example.com/article-1001",
"image_url": "https://example.com/img.jpg",
"news_site": "SpaceNews",
"summary": "A short summary...",
"published_at": "2025-09-20T12:34:56Z",
"launches": [{"provider": "SpaceX"}],
"events": [{"provider": "NASA"}]
}
]
}
最外層有以下這幾個欄位:
count
:新聞總數
next
:下一頁的網址
previous
:上一頁,這裡是 null 表示沒有上一頁
results
:一個清單,裡面放著多篇新聞文章
results
是一個清單,每一項都是一篇文章的資料
每篇文章裡面還有更多巢狀子物件:
launches
是一個清單,裡面有提供者的資料
events
也是清單,裡面有活動提供者的資料
步驟 1:呼叫 API
使用一個免費的新聞 API,Spaceflight News API,不需要金鑰。
以 limit=10
抓取最新的前 10 筆新聞文章資料。
若成功回應,從 results
(或 articles
)取出清單。
import csv
import requests
API = "https://api.spaceflightnewsapi.net/v4/articles/" # 免金鑰
def fetch_articles(limit=10, timeout=10):
# 呼叫 API,拿回多筆文章。
# 該 API 支援 ?limit= 參數
r = requests.get(API, params={"limit": limit}, timeout=timeout)
r.raise_for_status()
data = r.json()
# 常見清單 key:results 或 articles;都找看看(增加適配性)
items = data.get("results") or data.get("articles") or data
# 確保是清單
return items if isinstance(items, list) else []
步驟 2:扁平化資料
API 回傳的資料裡有巢狀結構,像 source.name
要取成單一的 source 字串。
launches
和 events
是清單,要把裡面的 provider 資訊用字串連接起來(例如用逗號分隔)。
注意欄位名稱有時不一樣,例如有的用 publishedAt
,有的用 published_at
,要處理好對應。
def flatten_article(a: dict) -> dict:
# 把一篇文章的巢狀欄位攤平成一層字典,方便後續列印/存檔。
# 有些 API 欄位名稱不同,盡量相容(右邊是替代名稱)
title = a.get("title") or a.get("headline") or ""
url = a.get("url") or a.get("link") or ""
img = a.get("image_url") or a.get("urlToImage") or ""
summary = a.get("summary") or a.get("description") or ""
published = a.get("published_at") or a.get("publishedAt") or ""
# 來源:有些在 source.name、有些在 news_site
source = ""
if isinstance(a.get("source"), dict):
source = a["source"].get("name") or ""
source = source or a.get("news_site") or ""
# 巢狀清單(launches/events)→ 取 provider 欄位後用逗號串起來
launches = ", ".join(
x.get("provider") for x in (a.get("launches") or []) if isinstance(x, dict) and x.get("provider")
)
events = ", ".join(
x.get("provider") or x.get("name", "") for x in (a.get("events") or []) if isinstance(x, dict)
)
return {
"title": title,
"source": source,
"published": published,
"url": url,
"image": img,
"summary": summary,
"launches": launches,
"events": events,
}
步驟 3:整理清單輸出
def print_list(articles):
# 把多筆文章印成清單(終端機好閱讀版本)。
if not articles: # 沒資料就提示「沒有資料」。
print("(沒有資料)")
return
for i, a in enumerate(articles, 1):
line = f"{i}. {a['title']} — {a['source']} — {a['published']}"
print(line)
步驟 4:存成 CSV
用 Python 的 csv.DictWriter
把整理完成的資料寫入articles.csv
檔案。
這個 CSV 檔案可以直接用 Excel 打開檢查。
def save_csv(articles, path="articles.csv"):
# 把整理好的多筆資料存成 CSV。
if not articles:
return
fields = ["title", "source", "published", "url", "image", "summary", "launches", "events"] # 指定要輸出的欄位順序 fields。
with open(path, "w", newline="", encoding="utf-8-sig") as f:
w = csv.DictWriter(f, fieldnames=fields)
w.writeheader()
w.writerows(articles)
print(f"已輸出 CSV:{path}({len(articles)} 筆)")
def main():
try:
raw_items = fetch_articles(limit=10)
except requests.exceptions.RequestException as e:
print("API 錯誤:", e)
return
# 把抓到的每一筆文章用 flatten_article() 轉成扁平化格式。
flat = [flatten_article(x) for x in raw_items]
# 終端機清單,先印在終端機上面看一眼。
print_list(flat)
# 存成 CSV,方便 Excel 與 分析
save_csv(flat, "articles.csv")
if __name__ == "__main__":
main()
執行結果:
學會處理 JSON 的巢狀結構
練習扁平化巢狀欄位(子物件、清單到單一欄位)
把資料整理成清單,並成功輸出成 CSV