Python 課程 – 網路與 API(Networking & APIs)
主題:RESTful API 呼叫
簡介
在現代軟體開發中,RESTful API 已成為前後端、微服務以及第三方服務之間最常使用的溝通方式。只要會呼叫 API,就能把資料從雲端服務、社群平台或公開資料庫即時取得,讓應用程式具備即時性與擴充性。
對於 Python 初學者而言,掌握如何正確、有效率地發送 HTTP 請求與處理回應,是進階開發路上不可或缺的基礎。本文將以 requests 套件為例,從概念說明到實作範例,帶你一步步完成安全、可維護的 RESTful 呼叫。
核心概念
1️⃣ HTTP 方法與資源定位
| 方法 | 語意 | 常見用途 |
|---|---|---|
| GET | 取得資源 | 讀取資料、搜尋 |
| POST | 建立資源 | 新增資料、觸發動作 |
| PUT | 完全取代資源 | 更新整筆資料 |
| PATCH | 部分更新 | 只改變部分欄位 |
| DELETE | 刪除資源 | 移除資料 |
每一次呼叫都需要一個 URL(統一資源定位符),它指向特定的資源,例如 https://api.example.com/users/123。
2️⃣ 標頭(Headers)與認證
- Content-Type:告訴伺服器傳送資料的格式,最常見的是
application/json。 - Authorization:放置 API 金鑰或 Bearer Token,用於驗證身分。
- Accept:告訴伺服器希望收到的回應格式,通常也是
application/json。
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
3️⃣ 請求參數與資料格式
- Query string(URL 參數):
?page=2&limit=20,適合 GET。 - Request body(請求主體): 以 JSON、form‑data 或 multipart 方式傳送,適合 POST / PUT / PATCH。
4️⃣ 回應與狀態碼
| 狀態碼 | 說明 |
|---|---|
| 200 | 請求成功(GET) |
| 201 | 成功建立(POST) |
| 400 | 請求格式錯誤 |
| 401 | 未授權(認證失敗) |
| 404 | 資源不存在 |
| 429 | 請求過於頻繁(Rate limit) |
| 500 | 伺服器內部錯誤 |
重要:永遠先檢查 response.status_code,再根據需要呼叫 response.json()。
5️⃣ 錯誤處理與重試機制
網路環境不穩定或第三方服務會臨時失效,重試(retry) 與 逾時(timeout) 設定是必備的防護措施。
程式碼範例
以下示範 5 個實用範例,涵蓋 GET、POST、參數、錯誤處理與 Session 重用。所有範例皆使用 requests 套件,請先執行 pip install requests。
1️⃣ 基本 GET 請求
import requests
url = "https://api.github.com/repos/python/cpython"
response = requests.get(url, timeout=5) # 設定 5 秒逾時
if response.status_code == 200:
data = response.json() # 解析 JSON
print(f"Repo 名稱: {data['full_name']}")
print(f"星星數: {data['stargazers_count']}")
else:
print(f"發生錯誤,狀態碼: {response.status_code}")
重點:
timeout可以避免程式卡住;response.json()只在回傳內容為 JSON 時才有效。
2️⃣ 搭配 Query Parameters 的 GET
import requests
url = "https://api.openweathermap.org/data/2.5/weather"
params = {
"q": "Taipei,tw",
"appid": "YOUR_API_KEY", # 請自行申請
"units": "metric"
}
resp = requests.get(url, params=params, timeout=10)
if resp.ok: # 等同於 200 <= status_code < 400
weather = resp.json()
print(f"目前溫度: {weather['main']['temp']}°C")
else:
print(f"查詢失敗 ({resp.status_code})")
params 會自動轉換成 ?q=Taipei,tw&appid=... 並編碼。
3️⃣ POST JSON 資料(建立資源)
import requests
import json
url = "https://jsonplaceholder.typicode.com/posts"
payload = {
"title": "測試文章",
"body": "這是一段測試內容",
"userId": 1
}
headers = {"Content-Type": "application/json"}
resp = requests.post(url, data=json.dumps(payload), headers=headers, timeout=5)
if resp.status_code == 201:
result = resp.json()
print(f"建立成功,ID 為 {result['id']}")
else:
print(f"建立失敗 ({resp.status_code})")
小技巧:
json=參數會自動幫你json.dumps並設定Content-Type,寫法更簡潔:requests.post(url, json=payload, timeout=5)
4️⃣ 使用 Session 與重試(Retry)機制
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry_strategy = Retry(
total=3, # 最多重試 3 次
backoff_factor=1, # 重試間隔 1, 2, 4 秒...
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["GET", "POST"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
try:
resp = session.get("https://httpbin.org/status/503", timeout=5)
resp.raise_for_status() # 若仍非 2xx,拋出例外
print("成功取得回應")
except requests.exceptions.RequestException as e:
print(f"最終失敗:{e}")
Retry 能自動處理 5xx、429 等暫時失效的情形,減少手動寫迴圈的複雜度。
5️⃣ 下載檔案(串流)並顯示進度
import requests
from tqdm import tqdm # pip install tqdm
url = "https://speed.hetzner.de/100MB.bin"
resp = requests.get(url, stream=True, timeout=10)
total = int(resp.headers.get('content-length', 0))
chunk_size = 1024 * 1024 # 1 MB
with open("100MB.bin", "wb") as f, tqdm(
total=total, unit='iB', unit_scale=True) as bar:
for chunk in resp.iter_content(chunk_size=chunk_size):
f.write(chunk)
bar.update(len(chunk))
print("下載完成")
使用 stream=True 可以避免一次把整個檔案載入記憶體,tqdm 則提供即時進度條,提升使用者體驗。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 最佳做法 |
|---|---|---|
忘記檢查 status_code |
直接呼叫 response.json() 會在非 2xx 時拋例外 |
使用 if response.ok: 或 response.raise_for_status() |
| 硬編碼 API 金鑰 | 代碼上直接寫 YOUR_API_KEY 會洩漏機密 |
把金鑰放在環境變數或 .env 檔,使用 os.getenv() 讀取 |
| 未設定逾時 | 請求卡住導致程式無回應 | requests.get(..., timeout=5),依需求調整 |
| 忽略 SSL 驗證 | verify=False 會降低安全性 |
除非測試環境,否則務必保持 verify=True |
| 過度使用全局 Session | 多執行緒共享同一 Session 可能產生競爭條件 |
為每個執行緒或任務建立獨立 Session,或使用 requests-futures |
| 未處理 Rate Limit | 被 429 限頻時直接失敗 | 捕捉 429,讀取 Retry-After 頭部,等待後重試 |
實際應用場景
- 整合第三方服務:在電商平台上使用 Stripe API 處理信用卡付款,只要把付款資訊 POST 給 Stripe,即可取得交易結果。
- 資料蒐集與分析:利用 Twitter API 抓取特定關鍵字的推文,結合 pandas 進行文字探勘與情感分析。
- 微服務間通訊:在大型系統中,前端服務透過 RESTful 呼叫後端的使用者服務(User Service),完成登入、註冊與權限驗證。
- 自動化測試:使用 pytest 搭配 requests-mock 模擬 API 回應,驗證程式在不同狀態碼下的行為。
- 檔案上傳/下載:在雲端儲存服務(如 AWS S3)使用 presigned URL 直接上傳大檔案,減少伺服器負載。
總結
- RESTful API 是現代應用程式與服務互動的核心,掌握 HTTP 方法、標頭、參數與狀態碼是基礎。
- Python 的 requests 套件提供直覺且功能完整的介面,配合 Session、timeout、retry 能寫出安全、可維護的網路程式。
- 常見的陷阱(未檢查回應、硬編碼金鑰、忽略逾時)只要遵守 最佳實踐,即可大幅降低錯誤與資安風險。
- 透過本文的範例,你已能 發送 GET/POST、處理 JSON、實作重試與檔案下載,接下來可以將這些技巧套用到實際的專案或第三方服務整合中。
祝你在 Python 網路開發的旅程中,玩得開心、寫得順手! 🚀