本文 AI 產出,尚未審核

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.loadsjson.loadjson.dumpsjson.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 格式

loadsload 的差別在於輸入來源是 字串 還是 檔案dumpsdump 則是 回傳字串直接寫檔

3. 解析 JSON 時的編碼與錯誤處理

  • 編碼:JSON 預設使用 UTF‑8,讀取檔案時務必以 encoding='utf-8' 開啟,避免出現 UnicodeDecodeError
  • 例外json.JSONDecodeError 會在字串不是合法 JSON 時拋出,應使用 try/except 包住解析程式碼,以免程式崩潰。

4. 自訂編碼/解碼:defaultobject_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 化的資料結構(如 dictliststr…)。

範例 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

最佳實踐

  1. 統一使用 utf-8:所有檔案 I/O、網路傳輸都以 UTF‑8 為標準。
  2. 設定 indentsort_keys=True(在開發或除錯階段),讓產出的 JSON 有結構、易比較。
  3. 建立共用的 json_helper.py,封裝 load_json, dump_json 等函式,統一錯誤處理與編碼設定。
  4. 盡量使用 object_hookdataclass,把 API 回傳的字典直接映射成型別安全的物件。
  5. 在大型資料流(如串流 API)時,使用 json.JSONDecoder().raw_decode,能一次解析多個 JSON 物件而不需要一次性載入全部內容。

實際應用場景

  1. 呼叫第三方服務(如天氣、地圖)

    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 內部結構,即可快速取出所需欄位。

  2. 建立自己的 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,開發者只需關注業務邏輯。

  3. 資料持久化:將爬蟲結果寫入檔案

    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)。
  • 了解 資料型別對應編碼注意事項、以及 例外處理,能避免最常見的錯誤。
  • 透過 defaultobject_hook,自訂類別的序列化與反序列化變得簡單,讓程式碼更具可讀性與型別安全。
  • 在實務開發中,統一編碼、加入錯誤捕捉、使用可讀的縮排 是提升專案品質的關鍵。

掌握了上述概念與技巧後,你就能自信地在 Python 中處理各種 API 回傳、設定檔、或是大型資料流的 JSON 資料,為後續的資料分析、服務整合與系統建置奠定堅實基礎。祝你在 Python 的網路與 API 開發旅程中,玩得開心、寫得順利!