Python爬虫urllib3与requests模块详解
Python爬虫urllib3与requests模块详解
在构建网络爬虫或调用API接口时,我们总会面临一个选择:该用哪个HTTP客户端?Python标准库中的 urllib 虽然可用,但使用起来繁琐且功能有限。于是社区催生了更高效的替代方案——先是 urllib3,随后是基于它进一步封装的 requests。
为什么需要这些第三方库?简单来说:效率、易用性、工程化支持。尤其是在对接现代Web服务(如AI模型推理平台)时,能否快速稳定地发起请求并处理响应,直接决定了开发效率和系统可靠性。
从连接池说起:urllib3 的核心优势
如果你曾因频繁请求同一站点而遭遇性能瓶颈,那很可能是因为没有复用TCP连接。每次HTTP请求都经历“建立连接 → 发送数据 → 关闭连接”的完整流程,开销巨大。
urllib3 正是为此而生。它原生支持连接池管理,通过 PoolManager 自动维护一组到目标主机的持久连接,避免重复握手,显著提升吞吐量。
import urllib3
from urllib.parse import urlencode
params = urlencode({
'type': 'movie',
'tag': '热门',
'page_limit': 10,
'page_start': 10
})
url = f"https://movie.douban.com/j/search_subjects?{params}"
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..."
with urllib3.PoolManager() as http:
res = http.request('GET', url, headers={'User-agent': ua})
print(type(res)) # <class 'urllib3.response.HTTPResponse'>
print("状态码:", res.status)
print("响应数据(bytes):", res.data[:200])
这个例子中,PoolManager 会自动缓存与豆瓣服务器的连接。若后续继续向该域名发送请求,将直接复用已有连接,省去DNS解析和TLS握手时间。
不过需要注意的是,res.data 返回的是原始字节流(bytes),若要转为字符串需手动解码:
text = res.data.decode('utf-8')
此外,对于JSON接口,还需配合 json.loads() 解析。这虽然灵活,但也增加了出错概率——比如编码识别错误导致乱码。
requests:让HTTP请求回归“人性化”
如果说 urllib3 是一把精准但需要调校的工具枪,那 requests 就是一辆即插即用的智能汽车。它基于 urllib3 构建,却提供了近乎自然语言般的API设计。
安装只需一行命令:
pip install requests
再来看同样的豆瓣电影请求示例:
import requests
from urllib.parse import urlencode
params = {
'type': 'movie',
'tag': '热门',
'page_limit': 10,
'page_start': 10
}
url = 'https://movie.douban.com/j/search_subjects?' + urlencode(params)
headers = {
'User-Agent': 'Mozilla/5.0 (...)'
}
res = requests.get(url, headers=headers)
print('响应正文:', res.text) # 已自动解码为str
print('状态码:', res.status_code)
print('最终URL:', res.url) # 支持重定向跟踪
print('响应头:', dict(res.headers))
print('请求方式:', res.request.method)
print('Cookie信息:', res.cookies)
输出结果清晰直观:
响应正文: {"subjects":[{"rate":"6.3","title":"摘金奇缘",...}]
状态码: 200
最终URL: https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8...
几个关键改进点值得强调:
- .text 属性自动根据响应头或内容推测编码并解码,无需手动干预;
- .json() 方法可直接返回解析后的JSON对象,异常时抛出清晰提示;
- 内置压缩支持(gzip/deflate),默认开启;
- 文件上传极其简单:files={'file': open('a.jpg', 'rb')} 即可完成 multipart 表单提交。
更重要的是,它的API设计符合直觉。比如POST表单:
requests.post(url, data={'key': 'value'})
而不是像 urllib 那样拼接 urlencode 并设置多个底层参数。
维持会话状态:Session对象的实战价值
很多网站依赖Cookie来维持登录态或用户偏好。如果每次请求都是独立的,服务器就会认为你是“新访客”,无法实现连续交互。
requests.Session() 解决了这个问题。它能跨请求自动保存Cookies、共用连接池,并允许预设通用头部,非常适合模拟用户行为或多步操作场景。
以下是一个访问百度搜索的示例:
import requests
urls = [
"https://www.baidu.com/s?wd=%E5%A7%AC",
"https://www.baidu.com/s?wd=%E5%A7%AC"
]
ua = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"
session = requests.Session()
session.headers['User-Agent'] = ua # 全局设置UA
for url in urls:
res = session.get(url)
print('响应正文片段:', res.text[:50])
print('状态码:', res.status_code)
print('实际请求URL:', res.url)
print('Cookie信息:', res.cookies) # 包含BAIDUID等关键标识
print('原始内容(bytes):', res.content[:100])
运行后你会发现,第二次请求携带了第一次响应中设置的Cookie(如 BAIDUID, H_PS_PSSID),从而被识别为同一用户。这种机制对绕过部分反爬策略非常有效——因为不少系统仅在首次访问时进行严格风控。
⚠️ 实践建议:在爬虫项目中尽早引入
Session,即使当前不需要保持状态,也能获得连接复用带来的性能提升。
对接AI服务:Hunyuan-MT-7B-WEBUI 的工程启示
当我们把视野从传统网页爬取转向AI模型调用时,HTTP客户端的角色变得更加关键。以腾讯推出的 Hunyuan-MT-7B-WEBUI 为例,这是一个集成了7B参数机器翻译模型的完整推理系统,提供Web界面和REST API,专为快速部署设计。
这类系统的典型使用流程如下:
- 启动Docker镜像或云实例;
- 执行脚本加载模型并启动服务;
- 通过浏览器或程序调用
/translate接口完成翻译任务。
在这种场景下,requests 成为了最理想的客户端选择。例如:
import requests
# 假设本地已启动 Hunyuan-MT-7B-WEBUI 服务
translate_url = "http://localhost:8080/translate"
data = {
"text": "今天天气真好",
"source_lang": "zh",
"target_lang": "en"
}
session = requests.Session()
res = session.post(translate_url, json=data, timeout=30)
if res.status_code == 200:
result = res.json()
print("翻译结果:", result['translated_text'])
else:
print("调用失败:", res.status_code, res.text)
这里不仅用到了 .json() 的便捷解析,还体现了 Session 在长周期服务调用中的稳定性优势——特别是在批量翻译任务中,复用连接可以大幅降低延迟累积。
更进一步,企业可将其作为微服务嵌入后台系统,前端通过JavaScript调用,后端用Python批量处理文档翻译需求。整个链条的起点,往往就是这样一个简洁的 requests.post()。
技术选型对比:urllib3 vs requests
| 功能维度 | urllib3 | requests |
|---|---|---|
| 安装便利性 | 需 pip install urllib3 | 同样需安装,但生态更广 |
| API友好度 | 较底层,需手动处理细节 | 极其简洁,接近自然表达 |
| 连接池支持 | 原生 PoolManager | 封装于 Session 中,开箱即用 |
| 编码处理 | .data 为 bytes,需手动decode | .text 自动解码,.content 保留原始流 |
| JSON支持 | 无内置支持 | .json() 一键解析 |
| Cookie管理 | 需自行解析Set-Cookie | Session 自动维护 |
| 文件上传 | 复杂 | files 参数一行搞定 |
| 社区活跃度 | 中等 | 极高,GitHub星标超48k |
结论很明确:
- 若你在开发高性能代理网关或底层网络组件,追求极致控制力,urllib3 更合适;
- 但对于绝大多数应用场景——无论是写爬虫、做自动化测试,还是对接AI模型API——requests 都是首选。
实战技巧补充
如何处理SSL证书问题?
某些内网服务或测试环境使用自签名证书,会导致请求报错:
requests.get("https://self-signed.example.com", verify=False)
虽然 verify=False 可临时跳过验证,但存在中间人攻击风险,绝不推荐用于生产环境。
更安全的做法是指定可信CA路径:
requests.get(url, verify='/path/to/cert.pem')
或者使用 mount 方法为特定域名配置适配器:
adapter = requests.adapters.HTTPAdapter(max_retries=3)
session.mount('https://', adapter)
text vs content:何时该用哪个?
| 属性 | 类型 | 使用场景 |
|---|---|---|
.text | str | 显示内容、文本提取、HTML解析 |
.content | bytes | 图片/文件下载、编码异常备用、二进制处理 |
当 .text 出现乱码时,优先尝试手动指定编码:
res.encoding = 'gbk' # 或 utf-8, big5 等
print(res.text)
也可以结合 chardet 库自动检测:
import chardet
encoding = chardet.detect(res.content)['encoding']
res.encoding = encoding
结语:从工具到系统的思维跃迁
回顾全文,我们经历了三个层次的认知升级:
- 最底层是
urllib3,它教会我们关注连接复用、资源管理和性能优化; - 中间层是
requests,它让我们体会到“开发者体验”如何重塑技术实践; - 最上层是像 Hunyuan-MT-7B-WEBUI 这样的工程化AI系统,展示了从模型权重到可用服务的完整交付形态。
最终我们可以这样比喻:
urllib3 是发动机,requests 是整车,而 Hunyuan-MT-7B-WEBUI 是一辆出厂即上路的智能翻译车。
在这个AI与Web深度融合的时代,掌握这两类工具的协作方式,不仅能写出更高效的爬虫脚本,更能从容应对日益复杂的系统集成挑战。无论你是数据工程师、AI应用开发者,还是全栈程序员,这份能力都将成为你技术栈中不可或缺的一环。
更多推荐
所有评论(0)