Python 課程 – 網路與 API:JSON 資料解析
簡介
在現代的 Web 應用與服務中,JSON(JavaScript Object Notation) 已成為最常見的資料交換格式。無論是呼叫第三方 API、前端與後端的資料傳遞,或是儲存設定檔,都離不開 JSON。對 Python 開發者而言,能快速、正確地將 JSON 文字轉換為 Python 物件(解析),以及把 Python 資料結構序列化成 JSON(產生),是日常工作中的必備技能。
本篇文章將從 JSON 的基本結構、Python 標準函式庫 json 的使用方式,到 常見的錯誤處理與最佳實踐,一步步帶你掌握 JSON 解析的核心概念,並提供多個實用範例,讓你在開發 API 客戶端或建構資料管線時,能夠得心應手。
核心概念
1. JSON 與 Python 資料型別的對應
| JSON 型別 | Python 對應型別 |
|---|---|
object |
dict |
array |
list / tuple |
string |
str |
number |
int / float |
true / false |
True / False |
null |
None |
了解這個對應關係是解析與序列化的基礎。例如,JSON 中的 { "name": "Alice", "age": 30 } 會被轉成 Python 的 {'name': 'Alice', 'age': 30}。
2. 讀寫 JSON:json.loads、json.load、json.dumps、json.dump
| 功能 | 說明 |
|---|---|
json.loads(s) |
把 JSON 字串 s 解析成 Python 物件 |
json.load(fp) |
從 檔案物件 fp 讀取 JSON 並解析 |
json.dumps(obj, **kwargs) |
把 Python 物件 序列化 成 JSON 字串 |
json.dump(obj, fp, **kwargs) |
把 Python 物件寫入 檔案 為 JSON 格式 |
loads 與 load 的差別在於輸入來源是 字串 還是 檔案;dumps 與 dump 則是 回傳字串 或 直接寫檔。
3. 解析 JSON 時的編碼與錯誤處理
- 編碼:JSON 預設使用 UTF‑8,讀取檔案時務必以
encoding='utf-8'開啟,避免出現UnicodeDecodeError。 - 例外:
json.JSONDecodeError會在字串不是合法 JSON 時拋出,應使用try/except包住解析程式碼,以免程式崩潰。
4. 自訂編碼/解碼:default 與 object_hook
Python 的 json 模組只能直接處理內建型別,若要序列化自訂類別,需要提供 default 函式;同理,解碼時若想把 dict 轉成特定物件,可使用 object_hook。
程式碼範例
範例 1:最簡單的字串解析與產生
import json
# JSON 字串
json_str = '{"city":"Taipei","population":2600000,"isCapital":true}'
# 解析
data = json.loads(json_str)
print(data) # {'city': 'Taipei', 'population': 2600000, 'isCapital': True}
# 序列化回 JSON(縮排 2 格,確保可讀性)
new_json = json.dumps(data, ensure_ascii=False, indent=2)
print(new_json)
重點:
ensure_ascii=False讓中文保持原樣,不會被轉成 Unicode escape。
範例 2:從檔案讀寫 JSON(UTF‑8 編碼)
import json
# 1. 寫入 JSON 檔案
person = {"name": "小明", "age": 28, "skills": ["Python", "Docker"]}
with open('person.json', 'w', encoding='utf-8') as f:
json.dump(person, f, ensure_ascii=False, indent=4)
# 2. 讀取 JSON 檔案
with open('person.json', 'r', encoding='utf-8') as f:
loaded = json.load(f)
print(loaded) # {'name': '小明', 'age': 28, 'skills': ['Python', 'Docker']}
提示:寫檔時加上
indent,方便日後除錯或手動編輯。
範例 3:處理不合法的 JSON(例外捕捉)
import json
bad_json = '{"name": "Alice", "age": 30,' # 少了結尾的 }
try:
obj = json.loads(bad_json)
except json.JSONDecodeError as e:
print(f"JSON 解析失敗:{e.msg}(第 {e.lineno} 行第 {e.colno} 列)")
技巧:
JSONDecodeError內含msg, lineno, colno,可直接回報給使用者或寫入 log。
範例 4:自訂類別的序列化(default)
import json
from datetime import datetime
class Event:
def __init__(self, title, when):
self.title = title
self.when = when # datetime 物件
def event_encoder(obj):
if isinstance(obj, Event):
return {"title": obj.title, "when": obj.when.isoformat()}
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"無法序列化型別 {type(obj)}")
e = Event("發表會", datetime(2025, 5, 20, 14, 30))
json_str = json.dumps(e, default=event_encoder, ensure_ascii=False, indent=2)
print(json_str)
說明:
default只在遇到無法直接序列化的物件時才會被呼叫,必須回傳可 JSON 化的資料結構(如dict、list、str…)。
範例 5:把 JSON 直接轉成自訂物件(object_hook)
import json
from dataclasses import dataclass
@dataclass
class User:
username: str
email: str
active: bool
def as_user(dct):
if set(dct.keys()) == {"username", "email", "active"}:
return User(**dct)
return dct
json_data = '{"username":"bob","email":"bob@example.com","active":true}'
user = json.loads(json_data, object_hook=as_user)
print(user) # User(username='bob', email='bob@example.com', active=True)
print(isinstance(user, User)) # True
應用:在大量讀取 API 回傳結果時,可直接得到具型別安全性的物件,降低後續手動轉換的錯誤。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 建議的做法 |
|---|---|---|
忘記指定 encoding='utf-8' |
讀取中文檔案時拋 UnicodeDecodeError |
使用 open(..., encoding='utf-8'),或在 json.load 前確認檔案編碼 |
直接使用 json.dumps 產生的字串寫入檔案,未設定 ensure_ascii=False |
中文會變成 \uXXXX,不易閱讀 |
設定 ensure_ascii=False,配合 indent 提升可讀性 |
忽略 JSONDecodeError |
程式在收到 malformed JSON 時崩潰 | 包住解析程式碼,捕捉例外並回報或重試 |
使用 eval 解析 JSON |
安全漏洞(執行任意程式碼) | 永遠使用 json 模組,絕不使用 eval |
序列化自訂類別時忘記 default |
TypeError: Object of type … is not JSON serializable |
為自訂類別提供 default,或先手動轉成 dict |
最佳實踐
- 統一使用
utf-8:所有檔案 I/O、網路傳輸都以 UTF‑8 為標準。 - 設定
indent與sort_keys=True(在開發或除錯階段),讓產出的 JSON 有結構、易比較。 - 建立共用的
json_helper.py,封裝load_json,dump_json等函式,統一錯誤處理與編碼設定。 - 盡量使用
object_hook或dataclass,把 API 回傳的字典直接映射成型別安全的物件。 - 在大型資料流(如串流 API)時,使用
json.JSONDecoder().raw_decode,能一次解析多個 JSON 物件而不需要一次性載入全部內容。
實際應用場景
呼叫第三方服務(如天氣、地圖)
import requests, json resp = requests.get('https://api.openweathermap.org/data/2.5/weather', params={'q': 'Taipei', 'appid': 'YOUR_KEY'}) weather = resp.json() # requests 已內建 json 解析 print(weather['main']['temp'])只要了解 JSON 內部結構,即可快速取出所需欄位。
建立自己的 RESTful API(Flask 範例)
from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/user', methods=['POST']) def create_user(): data = request.get_json() # 直接得到 dict # 假設要檢查欄位 if 'username' not in data: return jsonify({'error': 'username required'}), 400 # 回傳建立成功的資訊 return jsonify({'msg': 'created', 'user': data}), 201前端送出的 JSON 會自動被 Flask 轉成 Python dict,開發者只需關注業務邏輯。
資料持久化:將爬蟲結果寫入檔案
import json, pathlib results = [{'title': '新聞1', 'date': '2025-11-01'}, ...] pathlib.Path('data').mkdir(exist_ok=True) with open('data/news.json', 'w', encoding='utf-8') as f: json.dump(results, f, ensure_ascii=False, indent=2)後續分析時,只要
json.load即可還原原始結構。
總結
- JSON 是跨平台、跨語言的資料交換標準,在 Python 中使用
json模組即可完成 解析 (loads/load) 與 產生 (dumps/dump)。 - 了解 資料型別對應、編碼注意事項、以及 例外處理,能避免最常見的錯誤。
- 透過
default與object_hook,自訂類別的序列化與反序列化變得簡單,讓程式碼更具可讀性與型別安全。 - 在實務開發中,統一編碼、加入錯誤捕捉、使用可讀的縮排 是提升專案品質的關鍵。
掌握了上述概念與技巧後,你就能自信地在 Python 中處理各種 API 回傳、設定檔、或是大型資料流的 JSON 資料,為後續的資料分析、服務整合與系統建置奠定堅實基礎。祝你在 Python 的網路與 API 開發旅程中,玩得開心、寫得順利!