POST 請求用於向伺服器提交資料:表單送出、API 呼叫、檔案上傳。關鍵要素:
# pip install requests
import requests
import json
# 1. 表單資料 (application/x-www-form-urlencoded)
def post_form(url, data):
resp = requests.post(url, data=data, timeout=10)
return resp
# 2. JSON 資料 (application/json)
def post_json(url, payload):
resp = requests.post(url, json=payload, timeout=10) # 自動設定 Content-Type
return resp
# 3. 檔案上傳 (multipart/form-data)
def post_file(url, file_path, field_name="file"):
with open(file_path, 'rb') as f:
files = {field_name: f}
resp = requests.post(url, files=files, timeout=30)
return resp
# 4. 同時送表單欄位與檔案
def post_mixed(url, data, file_path):
with open(file_path, 'rb') as f:
files = {'file': f}
resp = requests.post(url, data=data, files=files, timeout=30)
return resp
# 使用範例
if __name__ == "__main__":
# 表單
r = post_form("https://httpbin.org/post", {"username": "alice", "password": "secret"})
print(r.json()['form'])
# JSON
r = post_json("https://httpbin.org/post", {"action": "create", "item": {"id": 1, "name": "test"}})
print(r.json()['json'])
# 檔案上傳
# r = post_file("https://httpbin.org/post", "test.txt")
session = requests.Session()
session.headers.update({'User-Agent': 'MyApp/1.0'})
# 登入取得 Cookie
login_resp = session.post("https://example.com/login", data={"user": "a", "pass": "b"})
# 後續請求自動攜帶 Cookie
profile_resp = session.get("https://example.com/profile")
# Basic Auth
requests.post(url, auth=('username', 'password'), json=data)
# Bearer Token
headers = {'Authorization': 'Bearer YOUR_TOKEN'}
requests.post(url, headers=headers, json=data)
# API Key
headers = {'X-API-Key': 'your-key'}
requests.post(url, headers=headers, json=data)
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def requests_with_retry():
session = requests.Session()
retry = Retry(
total=3,
backoff_factor=1, # 1s, 2s, 4s
status_forcelist=[500, 502, 503, 504],
allowed_methods=["POST"]
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('https://', adapter)
session.mount('https://', adapter)
return session
session = requests_with_retry()
resp = session.post("https://api.example.com/data", json={"key": "value"}, timeout=(5, 30))
# timeout=(連線超時, 讀取超時)
def stream_upload_large_file(url, file_path, chunk_size=8192):
headers = {'Content-Type': 'application/octet-stream'}
with open(file_path, 'rb') as f:
# 使用生成器逐塊讀取
def gen():
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
resp = requests.post(url, data=gen(), headers=headers, timeout=300)
return resp
Python 3 整合為 urllib.request,Python 2 為 urllib2。
import urllib.request
import urllib.parse
import json
def urllib_post_form(url, data_dict):
# 編碼為 application/x-www-form-urlencoded
data = urllib.parse.urlencode(data_dict).encode('utf-8')
req = urllib.request.Request(url, data=data, method='POST')
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
with urllib.request.urlopen(req, timeout=10) as resp:
return resp.read().decode('utf-8')
# 使用
result = urllib_post_form("https://httpbin.org/post", {"key": "value"})
print(json.loads(result)['form'])
def urllib_post_json(url, payload):
data = json.dumps(payload).encode('utf-8')
req = urllib.request.Request(url, data=data, method='POST')
req.add_header('Content-Type', 'application/json')
req.add_header('Content-Length', str(len(data)))
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read().decode('utf-8'))
import uuid
import urllib.request
def urllib_post_file(url, file_path, field_name="file"):
boundary = uuid.uuid4().hex
CRLF = b'\r\n'
with open(file_path, 'rb') as f:
file_content = f.read()
# 構建 multipart body
body = (
b'--' + boundary.encode() + CRLF +
f'Content-Disposition: form-data; name="{field_name}"; filename="{file_path}"'.encode() + CRLF +
b'Content-Type: application/octet-stream' + CRLF + CRLF +
file_content + CRLF +
b'--' + boundary.encode() + b'--' + CRLF
)
req = urllib.request.Request(url, data=body, method='POST')
req.add_header('Content-Type', f'multipart/form-data; boundary={boundary}')
req.add_header('Content-Length', str(len(body)))
with urllib.request.urlopen(req, timeout=30) as resp:
return resp.read().decode('utf-8')
import http.cookiejar
# 建立 Cookie 容器
cookie_jar = http.cookiejar.CookieJar()
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie_jar))
urllib.request.install_opener(opener)
# 後續請求自動處理 Cookie
urllib.request.urlopen("https://example.com/login", data=login_data)
# Cookie 已自動儲存
# pip install httpx
import httpx
import asyncio
async def async_post():
async with httpx.AsyncClient(timeout=30) as client:
# 並發多個 POST
tasks = [
client.post("https://httpbin.org/post", json={"id": i})
for i in range(10)
]
responses = await asyncio.gather(*tasks)
return [r.json() for r in responses]
# asyncio.run(async_post())
import requests
from requests.exceptions import RequestException, Timeout, ConnectionError, HTTPError
def safe_post(url, **kwargs):
try:
resp = requests.post(url, timeout=10, **kwargs)
resp.raise_for_status() # 4xx/5xx 拋出 HTTPError
return resp.json()
except Timeout:
return {"error": "請求超時"}
except ConnectionError:
return {"error": "連線失敗"}
except HTTPError as e:
return {"error": f"HTTP 錯誤: {e.response.status_code}"}
except RequestException as e:
return {"error": f"請求異常: {e}"}
except ValueError: # JSON 解析失敗
return {"error": "回應非 JSON 格式"}
def validate_response(resp, required_keys=None):
if not isinstance(resp, dict):
raise ValueError("回應非字典格式")
if required_keys:
for key in required_keys:
if key not in resp:
raise KeyError(f"缺少必要欄位: {key}")
return resp
| 庫 | 易用性 | 效能 | 功能完整度 | 適用場景 |
|---|---|---|---|---|
| requests | ★★★★★ | 良 | 完整 | 大多數專案首選 |
| urllib | ★★☆☆☆ | 良 | 基礎 | 無法安裝第三方套件環境 |
| httpx | ★★★★★ | 優(支援 HTTP/2) | 完整+異步 | 高併發、微服務、現代專案 |
| aiohttp | ★★★★☆ | 極優 | 完整+異步 | 大規模非同步爬蟲、Web 伺服器 |
verify=False)發送 HTTP POST 請求:首選 requests,語法直觀、功能完整、生態豐富。標準庫 urllib.request 適合無外部依賴環境,但需手動處理編碼、multipart、Cookie 等細節。現代高併發專案改用 httpx(同步/非同步雙支援)或 aiohttp。核心關鍵:正確設定 Content-Type、處理超時與重試、統一錯誤處理、保護敏感資料。掌握表單、JSON、檔案上傳三大常見格式,覆蓋 99% 實務場景。