iT邦幫忙

2025 iThome 鐵人賽

DAY 5
0
Modern Web

每天一點 API:打造我的生活小工具系列 第 5

Day05 — JSON 解析入門:掌握取值、篩選與輸出

  • 分享至 

  • xImage
  •  

前幾天學會了 API 與 JSON 的基礎概念,並完成了第一次呼叫 API。

今天要進一步學習,如何解析 JSON:把 API 回傳的資料轉成 Python 物件,並安全地取出特定欄位。

1. 為什麼要用 .json()?

當用 Python 的 requests 套件去向 API 拿資料時,伺服器回傳的資料通常是「JSON 格式」的文字。
不能直接用字串去方便地拿取裡面的資料,所以要用 .json() 把它轉成 Python 的資料結構,方便使用。

1.1 .json() 做了哪些事?

  • 把伺服器回來的位元組(bytes),依照編碼(像是 utf-8)轉成文字。

  • 再用 json 解析器把 JSON 格式的文字轉換成 Python 的資料類型:

    -JSON 的物件會變成 Python 字典(dict)

    -JSON 的陣列會變成 Python 清單(list)

    -JSON 裡的數字、布林、null會變成 Python 的 int/float、True/False、None。

1.2 .json() vs r.text:差在哪?

  • r.text:是伺服器回來的「純文字」,還是純純的 JSON 字串。

  • .json():已經把 JSON 字串轉成 Python 物件,可以直接用 data.get(...) 取欄位。

什麼時候用哪個?

  • 確定回應是 JSON ,想方便處理資料,用 .json() 最直接。

  • 不確定回傳內容,或想看原始字串內容,就先用 r.text 看一看。

1.3 什麼叫「回應真的是 JSON」?

通常會看 HTTP 回應的 Content-Type 標頭,如果裡面有 application/json 或類似包含 json,代表它是 JSON。

也可以用程式檢查:

content_type = r.headers.get("Content-Type", "")
is_json_like = "json" in content_type.lower()

1.4 常見錯誤與排除方法

(1) JSONDecodeError:表示伺服器回的內容不是正確的 JSON,可能是錯誤頁面或空字串。
解決方法:先看 Content-Type,印 r.text 頭幾百字判斷,並加例外處理。

(2) 204 No Content:伺服器回成功但沒資料,這時 .json() 會出錯。可以先檢查 r.status_coder.content 是否為空。

(3) 非標準 JSON:JSON 裡不允許 NaN、Infinity 等,若 API 錯誤傳回有這些資料會解析錯。這時需要先把資料清理或自訂解析行為。

(4) XSSI 防護字首:部分 API 為防止跨站攻擊可能在 JSON 前加多餘字元,需先去除再解析。

(5) 錯誤狀態碼(4xx/5xx):有時錯誤回應不是 JSON,呼叫 .json() 會錯誤,要先檢查狀態碼或用 r.raise_for_status()

2. 如何解析單筆 JSON?

以範例 API: https://jsonplaceholder.typicode.com/users/1為例,我們要從裡面取出nameemail,還有巢狀的 address.citycompany.name

2.1 基本觀念

API 回傳的 JSON 格式資料,透過 Python 的 requests 套件轉成 Python 可以操作的資料型態(通常是 dict 字典 和 list 清單)。

例如:data = r.json() 把 API 回傳的 JSON 字串,轉成 Python dict。

2.2 範例

# 連到網站拿資料
import requests # 載入會「發送網路請求」的套件

url = "https://jsonplaceholder.typicode.com/users/1" # 要去拿的資料位置(第 1 號使用者)
r = requests.get(url, timeout=10) # 用 GET 把資料抓回來,最久等 10 秒
r.raise_for_status()        # 如果伺服器回應不是成功(非 200 系列),立刻丟錯誤,避免用到壞資料

# 把回應轉成 Python 能用的型態
user = r.json()             # 把回傳的 JSON 轉成 Python 的字典,之後就能像取字典一樣拿資料
# 直接取簡單欄位
name = user.get("name")             # 抓 "name"
email = user.get("email")           # 抓 "email"

address = user.get("address", {})   # 先拿 address,若沒這欄位就用空字典預設
city = address.get("city")          # 再從 address 拿 city

company = user.get("company", {})   # 同理,拿 company
company_name = company.get("name")  # 從 company 拿 name

print(name, email, city, company_name)

執行結果會是:

Leanne Graham Sincere@april.biz Gwenborough Romaguera-Crona

2.3 為什麼用 .get()[] 安全?

  • user["name"] 如果 key 不存在會報錯 (KeyError),程式停止。

  • user.get("name") 如果 key 不存在會回傳 None 或預設值,不會錯誤。

  • 對巢狀資料更重要,像user["company"]["name"] 若 company 不存在就會直接錯誤崩潰。

  • 安全寫法是 (user.get("company") or {}).get("name"),當 company 是 None 時會用空字典代替,避免報錯。

3. 如何解析多筆 JSON?

有些 API 會回傳多筆資料,格式是「陣列(list)裡裝著多個字典(dict)」。像這個範例 API:
https://jsonplaceholder.typicode.com/posts

我們拿到的資料是多篇貼文,每篇資料是字典,裡面有 userId、id、title、body 等欄位。

3.1 遍歷並且只印出前 5 筆

import requests

r = requests.get("https://jsonplaceholder.typicode.com/posts", timeout=10) # 去網站把文章資料抓回來,最多等 10 秒
r.raise_for_status() # 如果不是成功回應(200 系列),馬上丟錯,避免用到壞資料
posts = r.json()  # 把回傳的 JSON 轉成 Python 的 list

# 用 for 迴圈遍歷,印出前 5 筆的 id 和 title
for p in posts[:5]:
    print(p.get("id"), p.get("title"))

3.2 加上條件篩選(只列出 userId 是 1 的貼文)

# 篩選 userId 是1的貼文
user1_posts = [p for p in posts if p.get("userId") == 1]
# 從 posts 裡一筆一筆拿出文章 p,如果 p 的 userId 等於 1,就把它放進新的列表 user1_posts。
用 .get("userId") 比 p["userId"] 安全:缺欄位時不會拋錯,只會回 None

# 只看前 5 筆並輸出想看的欄位
for p in user1_posts[:5]: # 把篩好的結果取前 5 筆,逐筆處理
    print(p.get("id"), p.get("title")) # 對每筆文章,安全地取出 id 跟 title 並印出來

3.3 常見實務技巧

  • 先確認資料型態是 list,避免後面當成陣列操作時出錯:
if not isinstance(posts, list): #檢查 posts 是不是 list。
# isinstance(變數, 類型) 會回傳 True/False;這裡加上 not,代表「如果不是 list」
    raise TypeError("預期是 list,但拿到不是") # 如果型態不符合,就主動拋出 TypeError,並附上容易看懂的錯誤訊息
  • 遍歷時用切片 [:5] 比用 for i in range(5) 更安全,避免索引超出範圍導致錯誤

4. 有哪些常見的 JSON 取值技巧?

4.1 用 .get("key", default) 安全取值

當想從字典拿某個欄位,但怕這個欄位沒資料,可以用 .get() 搭配預設值。

email = user.get("email", "(無 email)")  # 如果 user 沒 email,會回傳括號內字串,不會報錯

4.2 巢狀安全取值

JSON 常有巢狀結構(字典裡有字典),取值時要避免中間某層是 None 或沒資料而錯誤。
可以這樣寫:

city = (user.get("address") or {}).get("city")  # 若 address 不存在,先用空字典代替,避免錯誤
company_name = (user.get("company") or {}).get("name", "(無公司)")  # 若 company 無 name,回預設字串

4.3 用列表解析(List Comprehension)快速取欄位

當有一串多筆資料,可以用簡潔語法一次取出所有欄位:

titles = [p.get("title") for p in posts]               # 取得所有貼文的標題
pairs  = [(p.get("id"), p.get("title")) for p in posts] # 同時取得 id 和標題,成為 tuple 清單

4.4 條件過濾

篩選符合條件的資料,例如只取標題裡有 "qui" 的資料:

important = [p for p in posts if "qui" in (p.get("title") or "")]
  • for p in posts if ...:列表生成式。從 posts 逐筆拿出 p,符合條件就收進新列表。
  • p.get("title") or "":先拿 title;如果沒有,就用空字串 "" 來代替,避免對 None 做字串查找而報錯。
  • "qui" in (...):檢查子字串是否存在;有就為 True 。

4.5 合併技巧:取值 + 預設 + 過濾

結合以上的技巧,先篩選,再取欄位,缺欄位用預設值填補:

rows = [
    {"id": p.get("id"), "title": p.get("title") or "(untitled)"}
    # 把每一篇文章變成一個小字典,只留下兩個欄位 # 安全取值 p.get("id")
    #先拿 title;如果是 None 或空字串,就用 "(untitled)" 當預設
    for p in posts
    if p.get("userId") == 1 # 從所有 posts 裡挑出 userId 等於 1 的文章
]

5. 如何將資料輸出成 JSON 檔案?

Python 有內建的 json 模組,可以把資料轉成 JSON 格式存到檔案中。

5.1 使用 json.dump() 的基本寫法

import json

data = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "王小明"}]
# data = [...]:這是要輸出的資料,兩筆物件放在一個列表裡。
with open("data_out.json", "w", encoding="utf-8") as f:
# 開啟(或新建)一個叫 data_out.json 的檔案,用「寫入模式」w,編碼是 UTF-8。
# with ... as f::用 with 來管理檔案,區塊結束會自動關檔。
    json.dump(data, f, indent=2, ensure_ascii=False) # 把 data 寫進檔案 f

這段程式是在「把一份 Python 資料(list of dict)存成漂亮可讀的 JSON 檔案」。

5.2 抓 API 資料,挑想要的欄位,存檔

利用這個方法,就能抓到網路上的資料,挑想用的欄位,存成好看的 JSON 檔案。

這個部分 Day03 有實作過了,這邊就先跳過。


上一篇
Day04 — HTTP 怎麼運作?搞懂 API 傳資料的秘密
下一篇
Day 06 — 不怕程式崩潰!學會 API 錯誤處理技巧
系列文
每天一點 API:打造我的生活小工具11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言