Python
API 串接與異步:讓你的 Python 程式連上世界
API 串接與異步:讓你的 Python 程式連上世界
OpenAI、Claude、Google Maps、LINE,你用的每一個 AI 服務,背後都是一次一次的 API 呼叫。這一課帶你看懂 AI 寫的 API 串接程式在做什麼:怎麼用 requests 送出請求、怎麼安全地取出 JSON 裡的值、怎麼用 asyncio 把十次呼叫的等待時間從十秒壓到兩秒,以及最容易被忽略但後果最嚴重的一件事:API 金鑰不能寫死在程式碼裡。
看完這一課,你會有一句可以隨時拿出來檢查 AI 寫的 API 程式的口訣:這是 GET 還是 POST?API Key 放在哪裡?回傳的 JSON 結構是什麼?有沒有處理 HTTP 錯誤?
你將學到什麼
requests 的標準寫法
GET 取資料、POST 送資料,headers 放金鑰、params 放查詢條件。
JSON 回應怎麼安全取值
用 .get() 代替 [] 取值,加上完整的例外處理,防止程式無預警崩潰。
asyncio 讓批次呼叫變快
同步等十秒,非同步只等最慢的那個,httpx + asyncio.gather 的實際寫法。
API 金鑰不能寫死
用 .env 檔案存放金鑰,看懂 AI 寫的程式裡哪裡藏著洩漏風險。
7-1 requests:HTTP 請求的標準寫法
requests 是 Python 最常用的第三方 HTTP 套件(pip install requests),AI 寫的 API 串接程式幾乎全用它。GET 用來取資料,POST 用來送資料,金鑰統一放在 headers 裡,而不是寫進網址或程式碼。
import requests
# GET 請求(取資料)
response = requests.get(
"https://api.example.com/users",
headers={"Authorization": "Bearer YOUR_TOKEN"},
params={"page": 1, "limit": 20}
)
# 確認狀態碼再取值
response.raise_for_status() # 非 2xx 自動拋出例外
data = response.json() # 回傳 dict 或 list
print(data)
# POST 請求(送資料)
response = requests.post(
"https://api.example.com/users",
headers={
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
json={"name": "Alice", "email": "alice@example.com"}
)
response.raise_for_status()
print(response.status_code) # 201 Created
.raise_for_status() 是防護必加的一行。沒有它,API 回 404 或 500 時程式不會報錯,會悄悄拿著錯誤資料繼續跑下去。看到 AI 寫的 API 程式,第一件事就是確認有沒有這一行。7-2 JSON 回應處理與錯誤管理
API 幾乎都回傳 JSON。你要能逐層取出需要的值,也要能處理回傳格式不如預期的情況,兩件事同樣重要。
# 假設 API 回傳:
# {"status": "ok", "data": {"users": [{"id": 1, "name": "Alice"}]}}
response = requests.get("https://api.example.com/users")
response.raise_for_status()
result = response.json()
# 逐層取值(用 .get() 防 KeyError)
status = result.get("status")
users = result.get("data", {}).get("users", [])
for user in users:
print(user.get("name", "未知"))
遇到超時、連線失敗、格式不對這些情況,要用完整的例外處理逐一接住,而不是讓整個程式崩潰。
try:
response = requests.get(url, timeout=10) # 設超時!
response.raise_for_status()
data = response.json()
except requests.exceptions.Timeout:
print("請求超時")
except requests.exceptions.HTTPError as e:
print(f"HTTP 錯誤:{e.response.status_code}")
except requests.exceptions.ConnectionError:
print("網路連線失敗")
except ValueError:
print("回傳格式不是 JSON")
timeout,API 不回應時程式會永遠等待。生產環境最少加 timeout=10(秒)。這是 AI 初版程式最常忘記的一行。7-3 asyncio 非同步:批次 API 呼叫
同步呼叫十個 API,要等十次一秒,總共十秒;非同步並行呼叫,只要等最慢的那一個,通常不到兩秒。批次處理 AI API 呼叫,asyncio 是必學技能。
import asyncio
import httpx # pip install httpx(async 版的 requests)
async def fetch_user(client, user_id):
"""非同步取得單一用戶資料。"""
response = await client.get(
f"https://api.example.com/users/{user_id}"
)
response.raise_for_status()
return response.json()
async def fetch_all(user_ids):
"""並行取得所有用戶。"""
async with httpx.AsyncClient(timeout=10) as client:
tasks = [fetch_user(client, uid) for uid in user_ids]
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
# 執行
ids = [1, 2, 3, 4, 5]
results = asyncio.run(fetch_all(ids))
for r in results:
if isinstance(r, Exception):
print(f"錯誤:{r}")
else:
print(r.get("name"))
return_exceptions=True 讓 gather 不會因為一個 API 失敗就中斷全部。失敗的呼叫會變成一個 Exception 物件,你可以用 isinstance(r, Exception) 判斷哪些成功、哪些失敗。7-4 API 金鑰安全管理與常見陷阱
api_key = "sk-abc123realkey" 一旦這行程式碼上傳到 GitHub,金鑰就等於公開了。.env 檔(加進 .gitignore,不上傳),程式用 python-dotenv 讀取:load_dotenv() 之後 os.getenv("OPENAI_API_KEY") 取值,金鑰本身永遠不出現在程式碼裡。| 症狀 | 原因 | 修正 |
|---|---|---|
| API 不回應,程式卡住 | 沒有設 timeout | 加 timeout=10 |
| 404 但程式不報錯 | 沒有 raise_for_status | 每次請求後必加 |
| KeyError 取 JSON 值 | 直接用 [] 取值 | 改用 .get(key, default) |
| API Key 在 log 裡出現 | 直接 print 了 headers | log 前遮蔽敏感欄位 |
三道遞進題:讀懂、改寫、抓錯
讀懂:追蹤 API 呼叫的完整流程。改寫:把同步程式改成非同步、把金鑰移到 .env。抓錯:找出 timeout、raise_for_status、直接用 [] 取 JSON 這三類常見問題。
題目一:讀懂(基礎)
逐行說明以下程式的執行流程。
import requests, os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("WEATHER_API_KEY")
def get_weather(city):
try:
r = requests.get(
"https://api.weather.com/v1/current",
headers={"X-API-Key": API_KEY},
params={"city": city},
timeout=8
)
r.raise_for_status()
data = r.json()
return data.get("temperature")
except requests.exceptions.RequestException as e:
print(f"API 錯誤:{e}")
return None
任務:說明 load_dotenv、raise_for_status、timeout=8 各自的用途,以及 API 回傳 500 時會發生什麼。
load_dotenv() 讀取 .env 檔,把環境變數載入;os.getenv() 再取出指定 key 的值,金鑰不寫在程式碼裡。②
timeout=8 設定八秒超時;raise_for_status() 讓非 2xx 狀態碼自動拋出 HTTPError。③ API 回 500 時,raise_for_status 拋出 HTTPError,except 捕捉,印出錯誤訊息,回傳 None。
題目二:改寫(進階)
AI 給的初版 API 呼叫幾乎都是同步的,適合單次呼叫。但實際場景常需要批次呼叫,例如處理一百個用戶、翻譯五十條文字,這時候同步會慢五十到一百倍。懂得請 AI 改成非同步,是效能優化的關鍵技能。
請 AI 把以下同步程式改成非同步版本。
import requests
def translate(text, target_lang):
r = requests.post(
"https://api.translate.com/v1",
json={"text": text, "target": target_lang}
)
return r.json()["translated"]
texts = ["Hello", "World", "Python"]
results = [translate(t, "zh") for t in texts]
async def translate(client, text) 這樣的非同步函式,內部呼叫改用 await client.post() 送出,並在函式內加 await。②
asyncio.gather(*tasks) 並行執行所有翻譯,速度從三秒降到約一秒。③
r.json()["translated"] 改用 .get() 語法取值,key 不存在時回傳 None,而不是直接崩潰。題目三:抓錯(高階)
API 程式的錯誤最難除錯:不是程式碼寫錯,而是網路、金鑰、格式三層問題同時交錯,你要能逐層排查。
找出以下程式碼的所有問題。
import requests
API_KEY = "sk-abc123secret" # 問題一
def get_data(endpoint):
r = requests.get( # 問題二
f"https://api.example.com/{endpoint}",
headers={"Authorization": API_KEY}
)
data = r.json() # 問題三
return data["result"] # 問題四
任務:找出四個問題,說明危險原因。
os.getenv("API_KEY") 從 .env 讀取。問題二:沒有設 timeout,API 不回應時程式永遠等待,加
timeout=10。問題三:沒有呼叫
r.raise_for_status() 檢查,API 回 404 或 500 時不報錯,直接對錯誤回應呼叫 .json() 可能崩潰。問題四:
data["result"] 直接用 [] 取值,key 不存在時拋 KeyError,改用 data.get("result") 取值。重點整理與完成清單
| 步驟 | 程式碼 | 說明 |
|---|---|---|
| 發送請求 | r = requests.get(url, headers={}, timeout=10) | GET 或 POST |
| 確認狀態 | r.raise_for_status() | 非 2xx 拋例外 |
| 取 JSON | data = r.json() | dict 或 list |
| 取值 | data.get("key", default) | 防 KeyError |
模組七完成清單
能用 requests 發送 GET 和 POST 請求,帶 headers 和 timeout。
每次請求後加 raise_for_status 檢查,取 JSON 值用 .get 語法。
能用 .env 加 python-dotenv 安全管理 API Key。
了解 asyncio 加 httpx 的基本用法和適用情境。
完成作業題目一:說明 load_dotenv、raise_for_status、timeout 的作用。
完成作業題目二:請 AI 把同步 API 改成 asyncio 並行版本。
完成作業題目三:找出硬寫 Key、沒有 timeout、沒有 raise_for_status、直接 [] 四個問題。
