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,专为快速部署设计。

这类系统的典型使用流程如下:

  1. 启动Docker镜像或云实例;
  2. 执行脚本加载模型并启动服务;
  3. 通过浏览器或程序调用 /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

功能维度urllib3requests
安装便利性需 pip install urllib3同样需安装,但生态更广
API友好度较底层,需手动处理细节极其简洁,接近自然表达
连接池支持原生 PoolManager封装于 Session 中,开箱即用
编码处理.data 为 bytes,需手动decode.text 自动解码,.content 保留原始流
JSON支持无内置支持.json() 一键解析
Cookie管理需自行解析Set-CookieSession 自动维护
文件上传复杂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:何时该用哪个?

属性类型使用场景
.textstr显示内容、文本提取、HTML解析
.contentbytes图片/文件下载、编码异常备用、二进制处理

当 .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应用开发者,还是全栈程序员,这份能力都将成为你技术栈中不可或缺的一环。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐