Python Requests:網路服務整合的關鍵利器

在現代應用程式開發中,與網路服務的整合已成為不可或缺的一環。無論是存取RESTful API、抓取網頁內容,還是上載檔案至雲端服務,這些需求都離不開HTTP請求的處理。Python作為一門靈活與強大的程式語言,提供了多種處理HTTP請求的方式,而其中最為優雅與受歡迎的解決方案,非Requests模組莫屬。

為何選擇Requests?

當初我在帶領一個跨國金融科技專案時,團隊需要整合超過二十個不同的外部API。最初我們嘗試使用Python內建的urllib模組,但很快就發現其API設計過於複雜,導致程式碼冗長與難以維護。改用Requests後,程式碼量減少了近40%,可讀性大幅提升,更重要的是,團隊的開發效率顯著提高。

Requests模組憑藉其「人性化」的設計理念,將複雜的HTTP請求簡化為直覺與易於理解的程式碼。它不僅是對Python標準函式庫裝,而是重新思考了開發者與HTTP互動的方式,提供了更符合現代Python風格的API。

本涵蓋內容

本文將從基礎到進階,全面剖析Python Requests模組的各項功能與實踐技巧:

  1. 基礎HTTP請求操作與回應處理
  2. 工作階段管理與進階請求客製化
  3. 身份驗證與安全機制
  4. 效能最佳化與最佳實踐
  5. 實際應用案例解析

無論你是初學者還是有經驗的開發者,這篇都能幫助你更有效地使用Requests模組,開發出更穩健、更高效的網路應用。

Requests模組基礎:簡單而強大

安裝與設定

使用pip可以輕鬆安裝Requests:

pip install requests

這個簡單的指令會安裝最新版本的Requests模組。若需要特定版本,可以指定版本號:

pip install requests==2.25.1

安裝完成後,我們可以透過簡單的程式碼測試安裝是否成功:

import requests

response = requests.get('https://api.github.com')
print(response.status_code)  # 應該輸出 200
print(response.headers['content-type'])  # 應該輸出 'application/json; charset=utf-8'

傳送基本請求

Requests模組的核心功能是傳送HTTP請求。它提供了對應HTTP協定主要方法的函式:

import requests

# GET請求
response = requests.get('https://api.github.com/users/kennethreitz')

# POST請求
response = requests.post('https://httpbin.org/post', data={'key': 'value'})

# PUT請求
response = requests.put('https://httpbin.org/put', data={'key': 'updated_value'})

# DELETE請求
response = requests.delete('https://httpbin.org/delete')

# HEAD請求
response = requests.head('https://httpbin.org/get')

# OPTIONS請求
response = requests.options('https://httpbin.org/get')

這些函式呼叫看似簡單,但背後Requests處理了許多複雜的細節,包括連線管理、編碼處理、標頭設定等。

處理回應內容

傳送請求後,Requests會回傳一個Response物件,包含伺服器的所有回應資訊:

import requests

response = requests.get('https://api.github.com/users/kennethreitz')

# 檢視狀態碼
print(response.status_code)  # 200

# 查看回應標頭
print(response.headers)

# 取得回應內容(文字形式)
print(response.text)

# 取得回應內容(JSON形式,自動解析)
print(response.json())

# 檢視請求的URL
print(response.url)

# 檢視編碼
print(response.encoding)

# 取得原始二進位內容
print(response.content)

當我在處理各種API時,發現.json()方法特別實用。它會自動將JSON回應內容轉換為Python字典,省去了手動解析的麻煩。不過要注意,如果回應內容不是有效的JSON格式,這個方法會引發異常。

自定義請求標頭

在許多API整合場景中,我們需要自定義請求標頭,例如設定認證令牌或指定內容類別:

import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Authorization': 'Bearer YOUR_TOKEN_HERE',
    'Content-Type': 'application/json',
}

response = requests.get('https://api.github.com/users/kennethreitz', headers=headers)

自定義標頭對於API認證、內容協商、快取控制等場景非常重要。

處理查詢引數

Requests提供了簡潔的方式來處理URL查詢引數:

import requests

# 方法1:直接在URL中加入引數
response = requests.get('https://api.github.com/search/repositories?q=requests+language:python')

# 方法2:使用params引數(推薦)
params = {
    'q': 'requests+language:python',
    'sort': 'stars',
    'order': 'desc'
}
response = requests.get('https://api.github.com/search/repositories', params=params)

print(response.url)  # 顯示實際請求的URL

使用params引數的好處是Requests會自動處理URL編碼,避免特殊字元導致的問題。

進階請求技巧

處理表單資料

在Web應用程式中,我們經常需要處理表單提交。Requests簡化了這一過程:

import requests

# 傳送表單資料
payload = {'username': 'john', 'password': 'secret'}
response = requests.post('https://httpbin.org/post', data=payload)
print(response.json())

上述程式碼會以application/x-www-form-urlencoded內容類別傳送表單資料,這是HTML表單的預設行為。

傳送JSON資料

對於現代REST API,JSON是最常用的資料交換格式:

import requests
import json

# 方法1:使用json引數
payload = {'name': 'john', 'age': 30}
response = requests.post('https://httpbin.org/post', json=payload)

# 方法2:手動處理JSON(較舊的方式)
payload = {'name': 'john', 'age': 30}
headers = {'Content-Type': 'application/json'}
response = requests.post('https://httpbin.org/post', 
                         data=json.dumps(payload), 
                         headers=headers)

print(response.json())

使用json引數比手動處理更簡潔,Requests會自動將Python物件序列化為JSON,並設定正確的Content-Type標頭。

上載檔案

Requests簡化了檔案上載操作:

import requests

# 單檔案上載
files = {'file': open('report.pdf', 'rb')}
response = requests.post('https://httpbin.org/post', files=files)

# 多檔案上載
files = {
    'file1': open('report.pdf', 'rb'),
    'file2': open('image.jpg', 'rb')
}
response = requests.post('https://httpbin.org/post', files=files)

# 自定義檔案名和MIME類別
files = {'file': ('report.pdf', open('report.pdf', 'rb'), 'application/pdf')}
response = requests.post('https://httpbin.org/post', files=files)

記得在完成請求後關閉檔案,或使用with陳述式來自動處理檔案關閉:

import requests

with open('report.pdf', 'rb') as f:
    files = {'file': f}
    response = requests.post('https://httpbin.org/post', files=files)

設定超時

在生產環境中,設定超時是一個良好的實踐,可以避免請求陷入無限等待:

import requests

# 設定5秒的連線超時和30秒的讀取超時
try:
    response = requests.get('https://api.github.com', timeout=(5, 30))
except requests.exceptions.ConnectTimeout:
    print("連線超時")
except requests.exceptions.ReadTimeout:
    print("讀取超時")
except requests.exceptions.Timeout:
    print("請求超時")

在我的實踐中,為關鍵API設定合理的超時是防止系統因外部服務不可用而當機的重要手段。

重定向處理

預設情況下,Requests會自動處理重定向。我們可以透過allow_redirects引數控制這一行為:

import requests

# 停用重定向
response = requests.get('http://github.com', allow_redirects=False)
print(response.status_code)  # 301

# 啟用重定向(預設行為)
response = requests.get('http://github.com', allow_redirects=True)
print(response.status_code)  # 200

# 檢視重定向歷史
response = requests.get('http://github.com')
print(response.history)  # [<Response [301]>]

Requests預設最多允許30次重定向,可以透過設定max_redirects引數修改這一限制。

工作階段管理與進階功能

使用Session物件

在需要維護狀態(如cookies或連線)的場景中,Session物件非常有用:

import requests

# 建立工作階段
session = requests.Session()

# 設定工作階段級別的標頭或cookies
session.headers.update({'User-Agent': 'Mozilla/5.0'})
session.cookies.update({'session_id': '12345'})

# 使用工作階段傳送請求
response = session.get('https://httpbin.org/cookies')
print(response.json())  # 包含我們設定的cookies

# 工作階段會自動儲存伺服器設定的cookies
response = session.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
print(session.cookies)  # 包含伺服器設定的新cookie

# 傳送另一個請求,自動使用儲存的cookies
response = session.get('https://httpbin.org/cookies')
print(response.json())  # 顯示所有cookies

Session物件還有助於提高效能,因為它會在多個請求之間重用底層的TCP連線。

身份驗證

Requests支援多種身份驗證方式:

import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth

# 基本認證
response = requests.get('https://api.github.com/user', 
                        auth=HTTPBasicAuth('username', 'password'))

# 簡化的基本認證寫法
response = requests.get('https://api.github.com/user', auth=('username', 'password'))

# 摘要認證
response = requests.get('https://httpbin.org/digest-auth/auth/user/pass', 
                        auth=HTTPDigestAuth('user', 'pass'))

# 自定義認證
from requests.auth import AuthBase

class TokenAuth(AuthBase):
    def __init__(self, token):
        self.token = token
        
    def __call__(self, r):
        r.headers['Authorization'] = f'Bearer {self.token}'
        return r

response = requests.get('https://httpbin.org/headers', 
                        auth=TokenAuth('my-token'))

在企業應用中,我經常使用自定義認證類別來處理OAuth令牌或API金鑰等認證機制。

代理設定

在某些情況下,我們需要透過代理傳送請求:

import requests

proxies = {
    'http': 'http://10.10.1.10:3128',
    'https': 'http://10.10.1.10:1080',
}

response = requests.get('https://api.github.com', proxies=proxies)

# 使用帶認證的代理
proxies = {
    'http': 'http://user:pass@10.10.1.10:3128',
}
response = requests.get('https://api.github.com', proxies=proxies)

# 使用SOCKS代理
proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}
response = requests.get('https://api.github.com', proxies=proxies)

注意,使用SOCKS代理需要安裝額外的依賴:pip install requests[socks]

處理SSL證書驗證

預設情況下,Requests會驗證SSL證書。在開發或特殊環境中,我們可能需要停用這一驗證:

import requests

# 停用SSL證書驗證(不推薦用於生產環境)
response = requests.get('https://expired.badssl.com/', verify=False)

# 自定義CA證書
response = requests.get('https://api.github.
## 玄貓的 Python Requests 實戰:HTTP 互動與網路爬蟲技術

### 深入 Requests 的世界

Python Requests 套件已成為現代網路互動的標準工具它徹底改變了開發者與 HTTP 協定互動的方式這個套件以其優雅的 API 設計和直覺的使用體驗讓網路請求變得簡單而強大在我多年開發經驗中Requests 一直是我首選的 HTTP 客戶端函式庫要是因為它能讓複雜的網路互動簡化為幾行程式碼

#### HTTP 請求基礎與 Python 模組比較

HTTP 請求是現代網路通訊的根本 Python 提供了多種處理 HTTP 的模組傳統上Python 標準函式庫 urllib2 是主要選擇但它的 API 設計較為複雜與不直覺相比之下Requests 套件提供了更現代更人性化的介面

以下是 Requests  urllib2 的關鍵差異

```python
# urllib2 範例 - 較複雜
import urllib2

req = urllib2.Request('https://api.example.com/data')
req.add_header('User-Agent', 'Mozilla/5.0')
response = urllib2.urlopen(req)
data = response.read()

# Requests 範例 - 更簡潔直觀
import requests

response = requests.get('https://api.example.com/data', 
                        headers={'User-Agent': 'Mozilla/5.0'})
data = response.text

這個簡單的比較就能看出 Requests 的優勢 - 它將複雜的 HTTP 互動簡化為更直觀的方法呼叫。

Requests 的核心特性

Requests 套件的設計理念是「HTTP for Humans」,它徹底實作了這一理念。以下是 Requests 的核心特性:

  1. 簡潔的 API 設計
  2. 自動處理常見的 HTTP 功能(如編碼、cookies、重定向)
  3. 內建 JSON 解析功能
  4. 對 SSL 驗證的完整支援
  5. 永續性連線(Keep-alive)的自動處理
  6. 多種身份驗證機制的支援

發起簡單請求

讓我們從最基本的 HTTP GET 請求開始:

import requests

# 發起 GET 請求
response = requests.get('https://api.github.com')

# 檢查狀態碼
print(f"狀態碼: {response.status_code}")

# 查看回應內容
print(response.text[:100])  # 只顯示前100個字元

當執行上述程式碼時,Requests 會向 GitHub API 傳送 GET 請求,然後回傳回應物件。這個物件包含了狀態碼、標頭和內容等重要資訊。

回應內容處理

Requests 提供了多種方式處理回應內容:

import requests

response = requests.get('https://api.github.com/users/kennethreitz')

# 自動解析 JSON 內容
json_data = response.json()
print(f"User: {json_data['name']}")
print(f"Bio: {json_data['bio']}")

# 取得二進位內容
binary_data = response.content

# 取得文字內容
text_data = response.text

這個範例展示瞭如何使用 .json() 方法自動將 JSON 回應解析為 Python 字典。Requests 也提供了 .content 屬性來取得原始二進位內容,以及 .text 屬性來取得文字內容。

高階 HTTP 請求技巧

自定義標頭

在實際開發中,我們經常需要設定自定義 HTTP 標頭,例如 User-Agent 或授權資訊:

import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept-Language': 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7',
    'Authorization': 'Bearer your_token_here'
}

response = requests.get('https://api.example.com/data', headers=headers)
print(f"狀態碼: {response.status_code}")

這個技巧在開發爬蟲或與需要認證的 API 互動時特別有用。

傳送表單編碼資料

對於 POST 請求,經常需要傳送表單資料:

import requests

# 表單資料
form_data = {
    'username': 'testuser',
    'password': 'password123'
}

# 傳送 POST 請求
response = requests.post('https://api.example.com/login', data=form_data)

# 檢查登入是否成功
if response.status_code == 200:
    print("登入成功!")
else:
    print(f"登入失敗: {response.text}")

當使用 data 引數時,Requests 會自動將資料編碼為 application/x-www-form-urlencoded 格式,這是 HTML 表單的預設格式。

上載多部分編碼檔案

Requests 也支援檔案上載:

import requests

# 準備要上載的檔案
files = {
    'document': open('report.pdf', 'rb'),
    'image': ('photo.jpg', open('photo.jpg', 'rb'), 'image/jpeg')
}

# 傳送帶檔案的 POST 請求
response = requests.post('https://api.example.com/upload', files=files)

# 檢查上載結果
print(f"上載狀態: {response.status_code}")
print(response.text)

在這個例子中,我們上載了兩個檔案,一個是 PDF 檔案,另一個是 JPEG 圖片。Requests 會自動將這些檔案編碼為 multipart/form-data 格式,這是檔案上載的標準格式。

檢視內建回應狀態碼

Requests 提供了方便的方式來檢查 HTTP 狀態碼:

import requests

response = requests.get('https://api.github.com')

# 檢查是否成功
if response.ok:  # 檢查狀態碼是否為 2xx
    print("請求成功!")
elif response.status_code == requests.codes.not_found:  # 404
    print("資源不存在")
elif response.status_code == requests.codes.forbidden:  # 403
    print("無許可權存取")
else:
    print(f"發生錯誤: {response.status_code}")
    response.raise_for_status()  # 如果狀態碼表示錯誤,引發異常

requests.codes 物件提供了所有標準 HTTP 狀態碼的符號名稱,使程式碼更具可讀性。而 raise_for_status() 方法則可以在狀態碼表示錯誤時自動引發相應的異常。

查看回應標頭

標頭中包含了許多有用的資訊,如內容類別、快取控制等:

import requests

response = requests.get('https://www.python.org')

# 檢視所有標頭
for name, value in response.headers.items():
    print(f"{name}: {value}")

# 檢視特定標頭
content_type = response.headers['Content-Type']
print(f"內容類別: {content_type}")

# 不區分大小寫的標頭存取
server = response.headers.get('server')
print(f"伺服器: {server}")

Requests 的標頭存取不區分大小寫,這使得標頭的存取更加便捷。

使用 Requests 存取 Cookies

Cookies 在網路應用中扮演著重要的角色,尤其是在身份驗證和工作階段管理方面:

import requests

# 傳送請求並儲存 Cookies
response = requests.get('https://www.example.com')
cookies = response.cookies
print(f"取得的 Cookies: {cookies}")

# 使用特定的 Cookies 傳送請求
custom_cookies = {'session_id': '12345', 'user_id': '67890'}
response = requests.get('https://www.example.com/dashboard', cookies=custom_cookies)

Requests 會自動處理 Cookies,這使得模擬使用者工作階段變得非常簡單。

追蹤請求重定向

重定向是 HTTP 的常見功能,Requests 預設會自動處理重定向:

import requests

# 預設情況下會自動處理重定向
response = requests.get('http://github.com')
print(f"最終 URL: {response.url}")  # 應該是 https://github.com/

# 檢視重定向歷史
print("重定向歷史:")
for resp in response.history:
    print(f"  {resp.status_code}: {resp.url} -> {resp.headers['Location']}")

# 停用自動重定向
response = requests.get('http://github.com', allow_redirects=False)
print(f"狀態碼: {response.status_code}")  # 應該是 301
print(f"重定向到: {response.headers['Location']}")  # 應該是 https://github.com/

透過檢視 response.history 屬性,我們可以取得請求的重定向歷史。這在除錯或需要了解完整請求路徑時非常有用。

使用超時機制確保生產環境穩定

在生產環境中,設定請求超時是一個良好的實踐,可以防止因網路問題而導致的程式卡死:

import requests
from requests.exceptions import Timeout

try:
    # 設定連線超時為 3 秒,讀取超時為 10 秒
    response = requests.get('https://api.github.com', timeout=(3, 10))
    print(f"回應時間: {response.elapsed.total_seconds()}秒")
except Timeout:
    print("請求超時!")

在這個例子中,我們設定了兩種超時:連線超時(3秒)和讀取超時(10秒)。如果連線或讀取操作超過了指定的時間,Requests 會引發 Timeout 異常。

錯誤與例外處理

Requests 提供了豐富的異常類別,使錯誤處理變得更加精確:

import requests
from requests.exceptions import RequestException, HTTPError, ConnectionError, Timeout

try:
    response = requests.get('https://api.example.com/data')
    response.raise_for_status()  # 引發 HTTP 錯誤
    data = response.json()
except HTTPError as e:
    print(f"HTTP 錯誤: {e}")
except ConnectionError as e:
    print(f"連線錯誤: {e}")
except Timeout as e:
    print(f"請求超時: {e}")
except RequestException as e:
    print(f"其他請求錯誤: {e}")
except Exception as e:
    print(f"其他錯誤: {e}")

透過捕捉不同類別的異常,我們可以針對不同的錯誤情況採取不同的處理策略。

深入挖掘 Requests 的進階功能

使用 Session 物件持久化引數

在需要保持工作階段狀態的場景中,Session 物件非常有用:

import requests

# 建立工作階段物件
session = requests.Session()

# 設定工作階段級別的引數
session.headers.update({'User-Agent': 'Mozilla/5.0', 'Accept-Language': 'zh-TW'})
session.auth = ('username', 'password')
session.cookies.set('session_token', 'abc123')

# 使用工作階段傳送請求
# 所有引數都會自動應用
response1 = session.get('https://api.example.com/profile')
response2 = session.get('https://api.example.com/settings')

# 工作階段會自動儲存和傳送 cookies
print(f"Cookies: {session.cookies}")

使用 Session 物件可以避免在每個請求中重複設定相同的引數,同時也能自動處理 cookies,這使得模擬完整的使用者工作階段變得簡單。

揭示請求與回應的結構

Requests 提供了方便的屬性來檢查請求和回應的詳細資訊:

import requests

response = requests.get('https://api.github.com', headers={'Accept': 'application/vnd.github.v3+json'})

# 檢查請求資訊
print(f"請求 URL: {response.request.url}")
print(f"請求方法: {response.request.method}")
print(f"請求標頭: {response.request.headers}")
print(f"請求體: {response.request.body}")

# 檢查回應資訊
print(f"回應版本: {response.raw.version}")
print(f"回應狀態: {response.status_code} {response.reason}")
print(f"回應標頭: {response.headers}")
print(f"回應編碼: {response.encoding}")

這些資訊在除錯過程中非常有用,可以幫助我們瞭解請求和回應的完整細節。

使用預備請求

有時候,我們需要更細粒度的控制請求的準備和傳送過程:

import requests
from requests import Request, Session

# 建立工作階段
session = Session()

# 準備請求
req = Request('POST', 'https://api.example.com/data',
             data={'key': 'value'},
             headers={'X-Custom-Header': 'value'})

# 準備請求
prepped = session.prepare_request(req)

# 修改準備好的請求
prepped.headers['X-Another-Header'] = 'another_value'

# 傳送請求
response = session.send(preppe