1. Python发送HTTP请求的基础概览
1.1 urllib的角色与组成
在 Python 标准库中,urllib提供了完整的 HTTP 请求能力,核心模块是 urllib.request、urllib.parse 与 urllib.error。理解它们的职责有助于编写稳定的网络请求代码。
使用 urllib 的核心思想是:先构建一个 Request 对象,附带 URL、方法、头部、以及可选的请求体,然后通过 urlopen 发送并获取响应。这个过程天然支持异常处理、超时控制和数据解码。
1.2 使用 urllib.request 发送简单的 GET 请求
GET 请求是最常见的网络交互方式。正确的 URL 与合适的 User-Agent 头部可以提高兼容性,避免被服务端误判。
以下示例展示如何使用 Request 和 urlopen 进行简易的 GET 请求,并读取响应内容。
import urllib.request
url = 'https://httpbin.org/get'
headers = {'User-Agent': 'Mozilla/5.0 (compatible; urllib/3.0)'}
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=10) as resp:
content = resp.read()
print('状态码:', resp.status)
print('响应长度:', len(content))
1.3 如何读取响应并解码文本
响应内容通常以字节形式返回,需要进行正确的解码以获得文本。编码格式的正确处理可以避免中文乱码或 JSON 数据解析失败。
通过读取 resp.read(),再用合适的 decode 方法,可以得到文本内容。如果服务器返回的是二进制数据(如图片),可直接保存文件。
1.4 处理响应头和状态码
HTTP 的响应包含状态码、头部与主体。状态码200表示成功,但也要关注其他状态码如 301/302(重定向)和 4xx/5xx(错误)。
urllib 的异常处理会在遇到非 200 的状态码时抛出 HTTPError,需要在代码中进行捕获和重试策略设计。
2. Python urllib 实现 POST 请求与表单数据
2.1 POST 请求的基本思路
POST 请求通常用于提交表单或发送数据。与 GET 不同之处在于需要在请求体中携带数据。把参数放在请求体中,并设置正确的 Content-Type。
2.2 编码表单数据与构造请求体
对于 application/x-www-form-urlencoded 的表单数据,可以使用 urllib.parse.urlencode 将字典转为查询字符串,然后编码为字节放入 Request。
import urllib.request, urllib.parse
url = 'https://httpbin.org/post'
payload = {'username': 'test', 'password': 'secret'}
data = urllib.parse.urlencode(payload).encode('utf-8')
req = urllib.request.Request(url, data=data, headers={
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0'
})
with urllib.request.urlopen(req, timeout=15) as resp:
resp_text = resp.read().decode('utf-8')
print(resp_text)
2.3 发送 JSON 数据的注意事项
如果后端接收 JSON,请将对象编码为 JSON 字符串并设置 Content-Type: application/json。urllib 自身没有直接的 json 序列化方法,需要配合 json.dumps。
import urllib.request, json
url = 'https://httpbin.org/post'
payload = {'name': 'alice', 'active': True}
data = json.dumps(payload).encode('utf-8')
req = urllib.request.Request(url, data=data, headers={
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0'
})
with urllib.request.urlopen(req, timeout=10) as resp:
print(resp.read().decode('utf-8'))
3. 错误处理、超时与重试的鲁棒性
3.1 设置合理的超时
超时可以避免请求 interminably 阻塞。timeout 参数用于控制等待服务器响应的时长,单位为秒。
在网络波动时,适当的超时策略能提升应用的稳定性。还可以对连接与读取分别设置不同的超时。
3.2 处理异常与 HTTPError
urllib 在 HTTP 错误时会抛出 HTTPError,同时也可能抛出 URLError(如网络不可达)。对这些异常进行捕获是健壮网络客户端的基础。
import urllib.request, urllib.error
url = 'https://httpbin.org/status/404'
try:
with urllib.request.urlopen(url, timeout=5) as resp:
_ = resp.read()
except urllib.error.HTTPError as e:
print('错误码:', e.code)
except urllib.error.URLError as e:
print('网络错误:', e.reason)
3.3 简易重试机制与幂等性考量
在可重复提交的场景中,幂等性很关键。通过简单的循环和指数退避,可以在短时间内对部分失败进行重试。
实现策略时应确保非幂等的操作避免重复提交,必要时使用唯一请求标识符和服务端幂等性设计。
4. 进阶技巧:代理、SSL、头部与Cookies
4.1 使用代理与网络调试
在开发和测试阶段,代理能够帮助调试请求。通过在 Request 对象中设置 代理,并使用系统代理,urllib 可以走代理通道。
对于需要抓包分析的场景,可以结合工具如 Fiddler、Charles。请确保代理证书和网络环境的安全性。
4.2 跳过或验证 SSL 证书的注意事项
默认情况下,urllib 会执行 SSL 证书验证。在自签名证书的内网环境,可以通过自定义 ssl.SSLContext 来禁用验证,但这会带来风险,务必在受控环境使用。
import urllib.request, ssl
url = 'https://example.com'
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE # 不建议在生产环境使用
req = urllib.request.Request(url)
with urllib.request.urlopen(req, context=ctx) as resp:
print(resp.status)
4.3 自定义请求头、Cookies 与会话管理
维护一个统一的 请求头集合、以及简单的 Cookies 记录,有助于跨请求保持会话状态。
可以把公共头部、认证信息等放在一个 Headers 字典 中,便于重复使用。
import urllib.request
def make_request(url, headers=None, data=None):
if headers is None:
headers = {'User-Agent': 'Mozilla/5.0'}
return urllib.request.Request(url, data=data, headers=headers)
url = 'https://httpbin.org/headers'
req = make_request(url)
with urllib.request.urlopen(req) as resp:
print(resp.read().decode())
5. 实战案例:从网页抓取到数据提取
5.1 简单网页抓取与解析
实际项目中,常需要从网页获取数据并提取关键信息。先发起请求获取页面,再用字符串处理或正则提取目标字段。
对于结构化抓取,建议与解析库结合,例如 BeautifulSoup、lxml,但在这里只用 urllib 获取页面。
import urllib.request
from urllib.parse import urljoin
from html.parser import HTMLParser
url = 'https://httpbin.org/html'
with urllib.request.urlopen(url) as resp:
html = resp.read().decode('utf-8')
print(len(html))
5.2 提交表单并获取回传数据
通过 POST 将表单数据发送到服务器后,可以读取响应以确认提交结果。
import urllib.request, urllib.parse
url = 'https://httpbin.org/post'
payload = {'query': 'python urllib'}
data = urllib.parse.urlencode(payload).encode('utf-8')
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
req = urllib.request.Request(url, data=data, headers=headers)
with urllib.request.urlopen(req) as resp:
print(resp.read().decode('utf-8')) 

