1. 为什么每个Python开发者都需要掌握requests库
在当今互联网时代,HTTP请求已经成为程序与外界交互的最基本方式之一。无论是获取天气数据、调用API接口,还是构建网络爬虫,都离不开HTTP协议的支持。Python作为最流行的编程语言之一,其生态中涌现出众多HTTP客户端库,而requests无疑是其中最闪耀的明珠。
requests库由Kenneth Reitz于2011年发布,迅速成为Python社区中最受欢迎的第三方库之一。根据PyPI的下载统计,requests的周下载量长期保持在数千万次级别,这个数字足以证明其在Python开发者心中的地位。与Python标准库中的urllib相比,requests提供了更加人性化的API设计,让HTTP请求变得异常简单。
提示:如果你还在使用urllib.request处理HTTP请求,现在是时候切换到requests了。它不仅代码更简洁,错误处理也更友好,还能自动处理连接池和会话保持。
我曾在多个生产项目中同时使用过urllib和requests,最终团队一致投票决定全面转向requests。最直接的感受是:使用requests后,原本需要20行代码实现的HTTP请求,现在只需要2-3行就能完成,而且可读性大幅提升。更不用说requests自动处理的连接复用、超时重试等细节,让开发者可以更专注于业务逻辑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. requests库的核心功能解析
2.1 基本请求方法
requests支持所有HTTP方法,最常用的包括:
- GET:获取资源
- POST:创建资源
- PUT:更新资源
- DELETE:删除资源
- HEAD:获取响应头信息
一个最简单的GET请求示例:
python复制import requests
response = requests.get('https://api.example.com/data')
print(response.text)
POST请求同样简单,只需添加data参数:
python复制data = {'key1': 'value1', 'key2': 'value2'}
response = requests.post('https://api.example.com/post', data=data)
2.2 响应处理
requests的响应对象包含了丰富的信息和便捷的处理方法:
python复制response = requests.get('https://api.example.com/data')
# 状态码
print(response.status_code)
# 响应头
print(response.headers)
# 响应内容(自动解码)
print(response.text) # 文本内容
print(response.json()) # 如果是JSON,直接解析为字典
# 原始字节内容
print(response.content)
# 获取请求的URL(考虑重定向后的最终URL)
print(response.url)
2.3 高级功能
requests还提供了许多高级功能,让HTTP请求更加灵活:
- 会话保持(Session):复用TCP连接,提升性能
- 超时设置:避免请求长时间阻塞
- 代理支持:通过代理服务器发送请求
- SSL验证:自定义证书验证逻辑
- 文件上传:简单实现multipart文件上传
- 流式请求:处理大文件时节省内存
3. requests在实际项目中的应用场景
3.1 API调用
现代Web应用大量依赖RESTful API,requests是调用这些API的理想选择。例如获取天气数据:
python复制def get_weather(city):
url = f"https://api.weather.com/v1/city/{city}/current"
params = {
'apikey': 'YOUR_API_KEY',
'units': 'metric'
}
try:
response = requests.get(url, params=params, timeout=5)
response.raise_for_status() # 如果请求失败抛出异常
return response.json()
except requests.exceptions.RequestException as e:
print(f"获取天气数据失败: {e}")
return None
3.2 网页爬虫
虽然requests本身不提供解析HTML的功能,但结合BeautifulSoup等库,可以构建强大的爬虫:
python复制from bs4 import BeautifulSoup
def crawl_news():
url = "https://news.example.com/latest"
try:
response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
soup = BeautifulSoup(response.text, 'html.parser')
titles = [h2.text for h2 in soup.select('h2.news-title')]
return titles
except Exception as e:
print(f"爬取新闻失败: {e}")
return []
3.3 文件下载
requests可以轻松处理文件下载,并显示进度:
python复制def download_file(url, save_path):
with requests.get(url, stream=True) as r:
r.raise_for_status()
with open(save_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f"文件已保存到: {save_path}")
4. requests常见问题与最佳实践
4.1 错误处理
网络请求充满不确定性,良好的错误处理至关重要:
python复制try:
response = requests.get('https://api.example.com/data', timeout=5)
response.raise_for_status() # 检查HTTP错误
# 处理响应
data = response.json()
except requests.exceptions.Timeout:
print("请求超时,请稍后重试")
except requests.exceptions.HTTPError as err:
print(f"HTTP错误: {err}")
except requests.exceptions.RequestException as err:
print(f"请求异常: {err}")
4.2 性能优化
- 使用Session对象复用连接
- 合理设置超时时间
- 启用stream=True处理大响应
- 使用连接池适配器
python复制# 创建会话
session = requests.Session()
# 配置连接池
adapter = requests.adapters.HTTPAdapter(
pool_connections=10,
pool_maxsize=10,
max_retries=3
)
session.mount('http://', adapter)
session.mount('https://', adapter)
# 使用会话发送请求
response = session.get('https://api.example.com/data')
4.3 反爬虫应对
许多网站会阻止简单的爬虫,可以通过以下方式模拟浏览器行为:
python复制headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept-Language': 'en-US,en;q=0.9',
'Referer': 'https://www.google.com/'
}
cookies = {'session_id': '123456789'}
response = requests.get(url, headers=headers, cookies=cookies)
4.4 处理429和502错误
从热搜词中可以看到,许多开发者遇到429(Too Many Requests)和502(Bad Gateway)错误:
python复制def safe_request(url, max_retries=3):
for i in range(max_retries):
try:
response = requests.get(url, timeout=10)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 5))
print(f"达到速率限制,等待{retry_after}秒后重试...")
time.sleep(retry_after)
continue
response.raise_for_status()
return response
except requests.exceptions.HTTPError as e:
if e.response.status_code == 502 and i < max_retries - 1:
print("遇到502错误,重试中...")
time.sleep(2 ** i) # 指数退避
continue
raise
return None
5. requests与其他Python HTTP库的比较
虽然requests非常优秀,但Python生态中还有其他HTTP客户端库,各有特点:
| 库名称 | 特点 | 适用场景 |
|---|---|---|
| requests | 简单易用,功能全面 | 大多数HTTP请求场景 |
| urllib3 | 底层库,requests基于它构建 | 需要更底层控制时 |
| httpx | 支持HTTP/2,异步请求 | 需要高性能或异步编程 |
| aiohttp | 异步HTTP客户端/服务器 | asyncio应用程序 |
对于大多数开发者来说,requests仍然是首选。只有在需要HTTP/2支持或异步编程时,才需要考虑httpx或aiohttp。
6. requests的高级技巧
6.1 自定义身份验证
python复制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://api.example.com/protected', auth=TokenAuth('your_token'))
6.2 请求重试策略
python复制from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[408, 429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("https://", adapter)
session.mount("http://", adapter)
response = session.get("https://api.example.com/unstable")
6.3 流式上传大文件
python复制def upload_large_file(url, file_path):
with open(file_path, 'rb') as f:
headers = {'Content-Type': 'application/octet-stream'}
response = requests.post(
url,
data=f,
headers=headers,
stream=True
)
return response
7. requests在微服务架构中的应用
在现代微服务架构中,服务间通信经常通过HTTP进行。requests非常适合这种场景:
python复制class InventoryServiceClient:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
def get_item_stock(self, item_id):
url = f"{self.base_url}/items/{item_id}/stock"
try:
response = self.session.get(url, timeout=2)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"查询库存失败: {e}")
return None
def update_item_stock(self, item_id, delta):
url = f"{self.base_url}/items/{item_id}/stock"
data = {'delta': delta}
try:
response = self.session.patch(url, json=data, timeout=2)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"更新库存失败: {e}")
return None
8. requests的安全注意事项
使用requests时需要注意以下安全问题:
-
SSL验证:默认情况下requests会验证SSL证书,生产环境中不应禁用
python复制# 不安全做法(仅用于测试) requests.get('https://example.com', verify=False) -
敏感信息:不要在代码中硬编码API密钥
python复制# 不安全 api_key = "123456" # 更安全的方式是从环境变量获取 import os api_key = os.getenv("API_KEY") -
输入清理:处理用户提供的URL时要小心
python复制# 不安全 user_url = input("请输入URL: ") requests.get(user_url) # 可能访问内部网络 # 更安全的做法 from urllib.parse import urlparse user_url = input("请输入URL: ") parsed = urlparse(user_url) if not parsed.netloc.endswith('.example.com'): print("只允许访问example.com域名") else: requests.get(user_url)
9. requests的测试与Mock
在实际项目中,我们经常需要测试包含HTTP请求的代码。使用requests-mock可以方便地模拟请求:
python复制import requests_mock
def test_get_weather():
with requests_mock.Mocker() as m:
m.get('https://api.weather.com/v1/city/beijing/current',
json={'temperature': 25, 'condition': 'sunny'})
result = get_weather('beijing')
assert result['temperature'] == 25
assert result['condition'] == 'sunny'
对于更复杂的场景,可以考虑使用pytest-fixture:
python复制import pytest
import requests_mock
@pytest.fixture
def mock_requests():
with requests_mock.Mocker() as m:
yield m
def test_api_call(mock_requests):
mock_requests.get('https://api.example.com/data',
json={'key': 'value'})
response = requests.get('https://api.example.com/data')
assert response.json() == {'key': 'value'}
10. requests的替代方案与未来发展
虽然requests非常优秀,但也有一些局限性:
- 不支持异步:对于高并发应用,可以考虑httpx或aiohttp
- HTTP/2支持:如果需要HTTP/2,httpx是更好的选择
- 类型提示:requests的代码库较老,缺乏现代类型提示
requests的未来发展可能会集中在:
- 更好的类型支持
- 可选的异步后端
- 更现代的API设计
目前,requests仍然是大多数Python项目的首选HTTP库。它的简单性、可靠性和广泛的社区支持使其成为处理HTTP请求的黄金标准。
