1. 为什么选择requests库作为Python HTTP请求的首选
在Python生态中处理HTTP请求时,requests库几乎是所有开发者的默认选择。这个第三方库之所以能成为行业标准,主要得益于以下几个核心优势:
-
人性化的API设计:相比Python内置的urllib库,requests的API设计完全从开发者体验出发。发送GET请求只需
requests.get(url),POST请求也只需requests.post(url, data=payload),这种直观的接口设计大幅降低了学习成本。 -
完善的自动化处理:requests自动处理了很多底层细节:
- 连接池管理
- Keep-Alive连接复用
- 自动内容解码
- 基本的认证处理
- Cookie持久化
-
丰富的功能支持:
python复制# 支持各种HTTP方法 requests.get() requests.post() requests.put() requests.delete() # 支持各种参数类型 requests.get(params={...}) requests.post(json={...}) requests.post(files={...}) -
完善的错误处理:
python复制try: r = requests.get('https://example.com', timeout=5) r.raise_for_status() # 自动检查HTTP状态码 except requests.exceptions.RequestException as e: print(f"请求失败: {e}")
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. requests核心功能深度解析
2.1 请求与响应基础
一个完整的requests交互包含请求发送和响应处理两个部分:
请求构造示例:
python复制response = requests.get(
'https://api.example.com/data',
params={'page': 1, 'per_page': 20}, # 查询参数
headers={'Authorization': 'Bearer token123'}, # 请求头
timeout=10 # 超时设置
)
响应处理要点:
python复制print(response.status_code) # HTTP状态码
print(response.headers) # 响应头
print(response.text) # 文本内容
print(response.json()) # 解析JSON
print(response.content) # 二进制内容
print(response.elapsed) # 请求耗时
2.2 高级功能详解
2.2.1 会话(Session)管理
使用Session对象可以复用TCP连接,显著提升性能:
python复制with requests.Session() as s:
s.headers.update({'User-Agent': 'MyApp/1.0'})
# 第一次请求会建立连接
s.get('https://api.example.com/login', auth=('user', 'pass'))
# 后续请求复用已有连接
data = s.get('https://api.example.com/data').json()
2.2.2 文件上传与下载
文件上传:
python复制files = {'file': ('report.xlsx', open('report.xlsx', 'rb'), 'application/vnd.ms-excel')}
r = requests.post('https://example.com/upload', files=files)
流式下载大文件:
python复制url = 'https://example.com/large-file.zip'
with requests.get(url, stream=True) as r:
r.raise_for_status()
with open('large-file.zip', 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
2.2.3 超时与重试机制
基础超时设置:
python复制# 连接超时5秒,读取超时30秒
requests.get('https://example.com', timeout=(5, 30))
自定义重试策略:
python复制from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retries = Retry(
total=3,
backoff_factor=1,
status_forcelist=[500, 502, 503, 504]
)
session.mount('https://', HTTPAdapter(max_retries=retries))
3. 实战:构建健壮的HTTP客户端
3.1 处理常见HTTP状态码
python复制def make_request(url):
try:
response = requests.get(url, timeout=10)
if response.status_code == 200:
return response.json()
elif response.status_code == 401:
raise Exception("认证失败,请检查API密钥")
elif response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
raise Exception(f"请求过于频繁,请{retry_after}秒后重试")
elif response.status_code >= 500:
raise Exception("服务器内部错误")
else:
response.raise_for_status()
except requests.exceptions.Timeout:
raise Exception("请求超时,请检查网络连接")
except requests.exceptions.TooManyRedirects:
raise Exception("重定向次数过多,请检查URL")
except requests.exceptions.RequestException as e:
raise Exception(f"请求失败: {str(e)}")
3.2 构建API客户端类
python复制class APIClient:
def __init__(self, base_url, api_key=None):
self.base_url = base_url
self.session = requests.Session()
if api_key:
self.session.headers.update({'Authorization': f'Bearer {api_key}'})
def get(self, endpoint, params=None):
url = f"{self.base_url}/{endpoint}"
try:
response = self.session.get(url, params=params, timeout=10)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
print(f"HTTP错误: {e.response.status_code}")
raise
def post(self, endpoint, data=None):
url = f"{self.base_url}/{endpoint}"
try:
response = self.session.post(url, json=data, timeout=10)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
print(f"HTTP错误: {e.response.status_code}")
raise
4. 性能优化与最佳实践
4.1 连接池优化
python复制adapter = HTTPAdapter(
pool_connections=20, # 连接池数量
pool_maxsize=20, # 最大连接数
max_retries=3 # 重试次数
)
session = requests.Session()
session.mount('https://', adapter)
4.2 异步请求方案
虽然requests本身是同步的,但可以通过以下方式实现并发:
使用concurrent.futures:
python复制from concurrent.futures import ThreadPoolExecutor
urls = ['https://example.com/1', 'https://example.com/2']
def fetch(url):
return requests.get(url).text
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(fetch, urls))
使用requests-futures:
python复制from requests_futures.sessions import FuturesSession
session = FuturesSession(max_workers=5)
futures = [session.get(f'https://example.com/{i}') for i in range(10)]
results = [f.result().text for f in futures]
4.3 调试与日志记录
启用详细日志:
python复制import logging
import http.client
http.client.HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
5. 常见问题与解决方案
5.1 证书验证问题
python复制# 禁用SSL验证(不推荐生产环境使用)
requests.get('https://example.com', verify=False)
# 使用自定义CA证书
requests.get('https://example.com', verify='/path/to/cert.pem')
5.2 代理设置
python复制proxies = {
'http': 'http://proxy.example.com:8080',
'https': 'http://proxy.example.com:8080',
}
requests.get('https://example.com', proxies=proxies)
5.3 处理Cookie
python复制# 获取Cookie
response = requests.get('https://example.com')
print(response.cookies['sessionid'])
# 发送Cookie
cookies = {'sessionid': '123456'}
requests.get('https://example.com', cookies=cookies)
5.4 性能问题排查
当遇到性能问题时,可以检查以下方面:
- 连接复用:确保使用Session对象
- 响应流式处理:对于大响应使用
stream=True - 超时设置:避免请求挂起
- 压缩传输:检查
Accept-Encoding头 - DNS缓存:考虑使用
requests-toolbelt的DNS缓存功能
6. 实际项目中的应用案例
6.1 天气数据获取
python复制def get_weather(city):
url = f"https://api.weather.com/v1/city/{city}/forecast"
params = {
'apikey': 'your_api_key',
'language': 'zh-CN',
'metric': True
}
response = requests.get(url, params=params)
data = response.json()
return {
'city': city,
'temp': data['current']['temp'],
'condition': data['current']['condition']
}
6.2 网页内容抓取
python复制def scrape_website(url, selector):
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
return [elem.text for elem in soup.select(selector)]
6.3 API速率限制处理
python复制class RateLimitedClient:
def __init__(self, max_calls_per_minute=60):
self.max_calls = max_calls_per_minute
self.call_times = []
def make_request(self, url):
now = time.time()
# 移除1分钟前的记录
self.call_times = [t for t in self.call_times if t > now - 60]
if len(self.call_times) >= self.max_calls:
sleep_time = 60 - (now - self.call_times[0])
time.sleep(sleep_time)
response = requests.get(url)
self.call_times.append(time.time())
return response
7. 进阶:requests的替代方案
虽然requests是大多数情况下的最佳选择,但在某些特殊场景下可能需要考虑替代方案:
- aiohttp:需要真正异步IO支持时
- httpx:需要同时支持同步和异步接口时
- urllib3:需要更底层控制时
- http.client:需要完全标准库解决方案时
比较requests与httpx的主要区别:
| 特性 | requests | httpx |
|---|---|---|
| 同步支持 | ✓ | ✓ |
| 异步支持 | ✗ | ✓ |
| HTTP/2 | ✗ | ✓ |
| 类型注解 | 有限 | 完整 |
| 安装大小 | 较小 | 较大 |
| 社区生态 | 成熟 | 成长中 |
在实际项目中,我通常会这样选择:
- 普通同步应用:requests
- 需要异步支持:httpx
- 极简环境:urllib3
