首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python urllib/urllib2/requests 發送 HTTP POST 請求完整指南:表單、JSON、檔案上傳與錯誤處理

     編輯:更多程式設計知識

HTTP POST 請求核心概念

POST 請求用於向伺服器提交資料:表單送出、API 呼叫、檔案上傳。關鍵要素:

  • Content-Type:指定資料格式(form-data、json、multipart)
  • Request Body:攜帶實際資料
  • Headers:認證 Token、Cookie、User-Agent 等

現代首選:requests 庫(強烈推薦)


# 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")

進階 requests 技巧

Session 保持連線與 Cookie


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

標準庫 urllib/urllib.request(無外部依賴)

Python 3 整合為 urllib.request,Python 2 為 urllib2。

基本 POST(表單)


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'])

JSON POST


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'))

檔案上傳(multipart)- 較複雜,需手動構建


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')

Cookie 處理


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 已自動儲存

異步高效能:aiohttp / httpx


# 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 伺服器

安全性檢查清單

  1. ✅ 使用 HTTPS
  2. ✅ 驗證 SSL 憑證(預設開啟,勿輕易 verify=False)
  3. ✅ 設定合理超時避免阻塞
  4. ✅ 敏感資料勿硬編碼,用環境變數/密鑰管理
  5. ✅ 限制重試次數避免放大攻擊
  6. ✅ 記錄請求/回應日誌(隱藏敏感欄位)

總結

發送 HTTP POST 請求:首選 requests,語法直觀、功能完整、生態豐富。標準庫 urllib.request 適合無外部依賴環境,但需手動處理編碼、multipart、Cookie 等細節。現代高併發專案改用 httpx(同步/非同步雙支援)或 aiohttp。核心關鍵:正確設定 Content-Type、處理超時與重試、統一錯誤處理、保護敏感資料。掌握表單、JSON、檔案上傳三大常見格式,覆蓋 99% 實務場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved