Python 的 urllib3 是开发中非常实用的 HTTP 客户端库,相比内置 urllib 更简洁、连接池管理更优秀,requests 底层实际也依赖它。本文从基础用法到异常处理,覆盖日常接口调用的核心场景。
### 安装与连接池
首先安装库:
创建连接池对象,这是推荐的用法,内部维护 TCP 连接复用,避免每次新建连接的性能损失:
- import urllib3
- http = urllib3.PoolManager()
复制代码
### GET 请求
最基础的 GET 请求:
- resp = http.request('GET', 'https://httpbin.org/get')
- print(resp.status) # 200
- print(resp.data) # bytes 类型,需要用 decode('utf-8') 转换成字符串
- print(resp.headers) # 响应头字典
复制代码
如果需要携带 URL 参数,使用 fields 参数传递字典,urllib3 会自动处理编码和拼接:
- resp = http.request(
- 'GET',
- 'https://httpbin.org/get',
- fields={'key': 'value', 'page': 1}
- )
复制代码
### POST 请求的三种常见场景
1. 表单提交(application/x-www-form-urlencoded)
- resp = http.request(
- 'POST',
- 'https://httpbin.org/post',
- fields={'username': 'admin', 'password': '123456'}
- )
复制代码
2. JSON 提交(application/json)
需手动序列化 body 并设置 Content-Type 头:
- import json
- resp = http.request(
- 'POST',
- 'https://httpbin.org/post',
- body=json.dumps({'username': 'admin'}),
- headers={'Content-Type': 'application/json'}
- )
复制代码
3. 上传文件(multipart/form-data)
fields 中使用元组格式 (文件名, 文件对象, MIME 类型):
- with open('photo.jpg', 'rb') as f:
- resp = http.request(
- 'POST',
- 'https://httpbin.org/post',
- fields={'file': ('photo.jpg', f, 'image/jpeg')}
- )
复制代码
### 自定义请求头
常见需求是添加 User-Agent 和 Authorization:
- resp = http.request(
- 'GET',
- 'https://api.example.com/data',
- headers={
- 'User-Agent': 'my-app/1.0',
- 'Authorization': 'Bearer xxxxxxx'
- }
- )
复制代码
User-Agent 必加,很多接口会据此判断是否拒绝请求。
### 超时设置
生产环境必须设置超时,避免请求挂死:
- # 整体超时 3 秒
- resp = http.request('GET', 'https://httpbin.org/delay/5', timeout=3.0)
- # 分别设置连接超时和读取超时
- resp = http.request('GET', 'https://example.com', timeout=(3.0, 10.0))
复制代码
超时后会抛出 urllib3.exceptions.TimeoutError。
### 异常处理与重试
结合 retries 参数和 try/except 保证程序健壮性:
- import urllib3
- from urllib3.exceptions import HTTPError, TimeoutError, MaxRetryError
- http = urllib3.PoolManager(retries=3) # 自动重试 3 次
- try:
- resp = http.request('GET', 'https://httpbin.org/status/500')
- resp.raise_for_status() # 非 2xx 抛出 HTTPError
- except TimeoutError:
- print('请求超时')
- except HTTPError as e:
- print(f'HTTP错误: {e.response.status}')
- except MaxRetryError:
- print('重试次数耗尽')
复制代码
常用异常类型:TimeoutError(超时)、HTTPError(状态码错误)、MaxRetryError(重试用完仍失败)。
### 连接池复用(性能关键)
创建 PoolManager 时配置连接池参数,然后复用一个实例发送多个请求,避免每次新建:
- http = urllib3.PoolManager(
- num_pools=10, # 最大连接池数量
- maxsize=10, # 每个池最大连接数
- retries=3, # 自动重试次数
- timeout=5.0
- )
- r1 = http.request('GET', 'https://api.example.com/users')
- r2 = http.request('GET', 'https://api.example.com/orders')
复制代码
### 完整实战示例:调用 REST API
以下是一个封装函数,支持 GET/POST,自动处理 JSON 和异常:
- import urllib3
- import json
- http = urllib3.PoolManager(timeout=10.0, retries=3)
- def call_api(endpoint, data=None, method='GET'):
- url = f'https://api.example.com{endpoint}'
- kwargs = {
- 'method': method,
- 'headers': {
- 'Content-Type': 'application/json',
- 'Authorization': 'Bearer your_token_here'
- }
- }
- if data:
- kwargs['body'] = json.dumps(data)
- try:
- resp = http.request(url, **kwargs)
- resp.raise_for_status()
- return resp.data.decode('utf-8')
- except urllib3.exceptions.HTTPError as e:
- print(f'API返回错误: {e.response.status} - {e.response.data}')
- return None
- except urllib3.exceptions.TimeoutError:
- print('请求超时')
- return None
- # 调用示例
- result = call_api('/users', data={'name': 'zhangsan'}, method='POST')
- print(result)
复制代码
### 速查表
| 场景 | 核心参数 |
|------|----------|
| GET 请求 | http.request('GET', url) |
| 带参数 GET | fields={'k': 'v'} |
| POST 表单 | fields={'k': 'v'} |
| POST JSON | body=json.dumps(data), headers={'Content-Type': 'application/json'} |
| 上传文件 | fields={'file': ('name', fp, 'type')} |
| 自定义头 | headers={'Key': 'Value'} |
| 超时 | timeout=3.0 或 (conn_timeout, read_timeout) |
| 自动重试 | PoolManager(retries=3) |
掌握以上内容,日常 HTTP 接口调用可轻松应对。urllib3 不花哨,但足够稳定可靠。 |