4. Python 网络请求库:urllib 与 requests 实战
网络请求是数据采集的第一步。说白了,就是让你的 Python 程序像浏览器一样去访问网页、拿回数据。Python 里最常用的两个库就是 urllib(内置)和 requests(第三方)。我个人习惯是:简单测试用 urllib,正式项目必用 requests。为什么?往下看你就明白了。
本章核心脉络:urllib 基础 → requests 进阶 → Session 会话管理 → 代理与超时控制。这四个环节串起来,你就能应对 90% 的采集场景。
4.1 urllib 库基础使用
urllib 是 Python 自带的网络请求库,不需要额外安装。你想想看,一个连 pip 都不需要就能用的库,多方便。但它的 API 设计比较「原始」,用起来没那么顺手。
核心模块:
urllib.request— 发送请求、获取响应urllib.parse— URL 解析与编码urllib.error— 异常处理
来个最简单的例子:
import urllib.request
# 发送 GET 请求
response = urllib.request.urlopen('https://httpbin.org/get')
print(response.status) # 200
print(response.read().decode('utf-8'))
嗯,这里要注意:urlopen() 返回的是一个类文件对象,需要用 .read() 读取内容,而且返回的是字节流,得手动 decode。我在项目中遇到过不少新手直接打印 response.read(),结果看到一堆乱码就慌了。
带参数的请求:
import urllib.request
import urllib.parse
# 构造查询参数
params = urllib.parse.urlencode({'q': '数据采集', 'page': 1})
url = f'https://httpbin.org/get?{params}'
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
response = urllib.request.urlopen(req)
print(response.read().decode())
我的经验:urllib 的 Request 对象可以让你自定义请求头。很多网站会检查 User-Agent,不加的话容易被拦截。我曾经爬一个论坛,用默认的 Python-urllib 头,直接返回 403,加上浏览器 UA 就正常了。
4.2 requests 库安装与使用
说实话,urllib 用起来有点啰嗦。每次都要手动处理编码、构造请求头、处理异常……所以业界更推荐 requests 库。它把底层细节都封装好了,API 设计得非常人性化。
安装:
pip install requests
一行命令搞定。来看看它有多简洁:
import requests
# GET 请求
resp = requests.get('https://httpbin.org/get')
print(resp.status_code) # 200
print(resp.text) # 自动解码
# POST 请求
data = {'username': 'admin', 'password': '123456'}
resp = requests.post('https://httpbin.org/post', data=data)
print(resp.json()) # 自动解析 JSON
你发现没?resp.text 直接返回字符串,resp.json() 直接返回字典。urllib 要写好几行的事,requests 一行搞定。
常用方法对比:
| 功能 | urllib 写法 | requests 写法 |
|---|---|---|
| GET 请求 | urlopen(url) |
requests.get(url) |
| POST 请求 | urlopen(url, data=bytes) |
requests.post(url, data=dict) |
| 添加请求头 | Request(url, headers=dict) |
requests.get(url, headers=dict) |
| 解析 JSON | json.loads(resp.read()) |
resp.json() |
| 异常处理 | urllib.error.URLError |
requests.exceptions.RequestException |
我的建议:除非你是在受限环境(比如某些服务器不允许装第三方库),否则直接用 requests。它节省的时间值得那一次 pip install。
4.3 Session 与会话管理
很多网站需要登录后才能采集数据。你想想看,每次请求都重新登录,多麻烦。这时候就需要 Session 来维持会话状态。
Session 的核心作用就是 自动保存 Cookie,并在后续请求中自动携带。说白了,它模拟了浏览器的「记住我」功能。
import requests
# 创建 Session 对象
session = requests.Session()
# 第一次请求:登录
login_data = {'username': 'test', 'password': 'test123'}
resp = session.post('https://httpbin.org/post', data=login_data)
print('登录成功')
# 第二次请求:自动携带 Cookie
resp = session.get('https://httpbin.org/cookies')
print(resp.text) # 可以看到登录后的 Cookie 信息
我在项目中遇到过这样一个场景:爬一个需要登录的电商网站,第一次用 requests.get() 直接请求,返回登录页面。后来改用 Session,先登录再请求,数据就正常返回了。你想想看,Session 帮你省了多少事。
注意:Session 对象默认会保持长连接,用完记得调用 session.close() 释放资源。或者用 with 上下文管理器自动管理。
with requests.Session() as session:
session.post('https://httpbin.org/post', data={'key': 'value'})
resp = session.get('https://httpbin.org/get')
# 自动关闭
4.4 代理设置与超时控制
数据采集时,你可能会遇到两个问题:IP 被封 和 请求卡死。代理和超时就是解决这两个问题的利器。
代理设置:
import requests
# 设置代理
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080'
}
resp = requests.get('https://httpbin.org/ip', proxies=proxies)
print(resp.text) # 显示代理 IP
我曾经爬一个反爬很严的网站,同一个 IP 请求 50 次就被封了。后来用代理池,每次请求换一个 IP,再也没被封过。嗯,这里要注意:免费代理往往不稳定,建议用付费代理或自建代理池。
超时控制:
import requests
try:
# 设置连接超时 3 秒,读取超时 5 秒
resp = requests.get('https://httpbin.org/delay/10', timeout=(3, 5))
print(resp.text)
except requests.exceptions.Timeout:
print('请求超时,请检查网络或目标服务器')
你想想看,如果不设超时,遇到一个响应慢的服务器,你的程序可能卡在那里几分钟甚至更久。我见过一个采集任务,因为没设超时,一个请求卡了 10 分钟,整个任务链都断了。
代理 + 超时 + Session 组合使用:
import requests
session = requests.Session()
session.proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080'
}
try:
resp = session.get(
'https://httpbin.org/delay/3',
timeout=(5, 10),
headers={'User-Agent': 'Mozilla/5.0'}
)
print(resp.status_code)
except requests.exceptions.RequestException as e:
print(f'请求失败: {e}')
finally:
session.close()
我的经验:生产环境中,建议把代理、超时、重试机制封装成一个工具函数。这样每个采集任务都能复用,不用重复写这些配置代码。
4.5 避坑指南
做数据采集这几年,我踩过不少坑。分享几个最常见的:
- 忘记设置 User-Agent:很多网站会拦截非浏览器请求。我建议至少设置一个常见的浏览器 UA。
- 不处理重定向:requests 默认会跟随重定向,但 urllib 需要手动设置。如果你不想跟随,可以设置
allow_redirects=False。 - 忽略 SSL 证书验证:测试时可以设置
verify=False,但生产环境建议用verify='/path/to/cert.pem'指定证书。 - 不关闭 Session:Session 对象会占用连接资源,用完一定要关闭。
我曾经:有一次爬一个 HTTPS 网站,忘了关 SSL 验证,结果在测试环境跑得好好的,部署到生产环境就报 SSL 错误。排查了半天才发现是生产环境的 Python 版本没有更新 CA 证书。从那以后,我都在代码里显式指定证书路径。