httplib / http.client 是 Python 中一个非常底层的 HTTP 客户端库,它提供了对 HTTP 协议的直接操作,设置代理需要手动处理连接细节。

核心概念
使用代理的核心思想是:不直接连接目标服务器,而是先连接到代理服务器,然后通过代理服务器去请求目标资源。
在 HTTP 协议中,这通常通过 CONNECT 方法实现(用于 HTTPS)或在请求的 URL 中明确指定代理(用于 HTTP)。
为 HTTP 请求设置代理
对于 HTTP 请求,设置代理相对简单,你只需要构造一个完整的 URL,格式为 http://代理服务器地址:端口/目标URL。
示例代码
假设我们要通过代理 168.1.100:8080 访问 http://example.com。

import http.client
import urllib.parse
# --- 配置 ---
proxy_host = '192.168.1.100'
proxy_port = 8080
target_host = 'example.com'
target_path = '/'
# 1. 构造代理请求的完整路径
# 格式为: http://<proxy_host>:<proxy_port>/<target_url>
# 注意:需要对目标主机和路径进行编码,以防包含特殊字符
proxy_request_path = f"http://{target_host}{target_path}"
encoded_path = urllib.parse.quote(proxy_request_path)
# 2. 连接到代理服务器
# 注意:我们连接的是代理服务器,而不是目标服务器
conn = http.client.HTTPConnection(proxy_host, proxy_port)
# 3. 发送请求到代理服务器
# 请求的目标是 encoded_path
# Host 头部仍然是目标服务器的 host
headers = {
'Host': target_host,
'User-Agent': 'Python-http.client/Proxy Test'
}
conn.request("GET", encoded_path, headers=headers)
# 4. 获取并打印响应
response = conn.getresponse()
print(f"Status: {response.status} {response.reason}")
print("Headers:")
for header, value in response.getheaders():
print(f" {header}: {value}")
print("\nBody:")
# 读取响应体,并解码(如果需要)
body = response.read()
print(body.decode('utf-8'))
# 5. 关闭连接
conn.close()
代码解释:
- 构造代理路径: 我们创建了一个特殊的路径
http://example.com/,并将其编码后作为请求路径发送给代理,代理服务器看到这个路径就知道它需要去连接example.com并获取资源。 - 连接代理:
http.client.HTTPConnection(proxy_host, proxy_port)建立到代理服务器的 TCP 连接。 - 发送请求:
conn.request(...)将请求发送给代理。Host头部设置为目标服务器的地址,这是代理服务器所需要的。 - 处理响应: 后续的步骤与直接连接服务器没有区别。
为 HTTPS 请求设置代理(SOCKS 代理除外)
HTTPS 请求更复杂,因为连接一开始就是加密的,代理服务器无法直接解析 https://example.com 这样的路径。
标准做法是使用 CONNECT 方法:
- 客户端首先与代理服务器建立一个未加密的 TCP 连接。
- 客户端向代理服务器发送一个
CONNECT example.com:443 HTTP/1.1请求。 - 代理服务器与目标服务器的
443端口(HTTPS 默认端口)建立一个 TCP 连接。 - 代理服务器返回
200 Connection established响应。 - 从此时起,客户端和目标服务器之间的所有通信都通过这个已建立的 TCP 隧道进行,客户端会直接在该连接上发送加密的 HTTPS 请求,代理服务器只是简单地转发数据包,不再解析内容。
http.client 提供了便捷的方式来处理这个过程。

示例代码
假设我们要通过代理 168.1.100:8080 访问 https://example.com。
import http.client
import ssl
# --- 配置 ---
proxy_host = '192.168.1.100'
proxy_port = 8080
target_host = 'example.com'
target_port = 443 # HTTPS 默认端口
try:
# 1. 连接到代理服务器(使用 HTTPConnection)
print(f"Connecting to proxy {proxy_host}:{proxy_port}...")
conn = http.client.HTTPConnection(proxy_host, proxy_port)
# 2. 发送 CONNECT 请求,建立到目标服务器的隧道
# 这是最关键的一步
print(f"Sending CONNECT request to {target_host}:{target_port}...")
conn.request("CONNECT", f"{target_host}:{target_port}")
# 3. 检查代理服务器的响应
response = conn.getresponse()
print(f"Proxy response status: {response.status} {response.reason}")
if response.status != 200:
raise Exception(f"Failed to establish tunnel with proxy: {response.status} {response.reason}")
# 4. 如果隧道建立成功,将连接“升级”为安全连接
# 我们使用 SSLSocket 将 SSL 包装在现有的套接字连接上
# 这个套接字就是 conn.sock
print("Tunnel established. Wrapping with SSL...")
secure_conn = http.client.HTTPSConnection(target_host, port=target_port)
# 关闭新连接的底层套接字,因为我们已经通过代理有了它
secure_conn.sock = conn.sock
# 包装为 SSL 套接字
context = ssl.create_default_context()
secure_conn.sock = context.wrap_socket(secure_conn.sock, server_hostname=target_host)
# 5. secure_conn 就是一个安全的 HTTPS 连接,可以直接使用
print("Sending GET request to /...")
secure_conn.request("GET", "/")
# 6. 获取并打印响应
response = secure_conn.getresponse()
print(f"Final response status: {response.status} {response.reason}")
print("Headers:")
for header, value in response.getheaders():
print(f" {header}: {value}")
print("\nBody:")
body = response.read()
print(body.decode('utf-8'))
except Exception as e:
print(f"An error occurred: {e}")
finally:
# 7. 关闭连接
if 'conn' in locals() and conn:
conn.close()
if 'secure_conn' in locals() and secure_conn:
secure_conn.close()
代码解释:
- 连接代理: 同样,我们先用
HTTPConnection连接到代理服务器。 CONNECT请求:conn.request("CONNECT", f"{target_host}:{target_port}")发送建立隧道的指令。- 检查响应: 必须检查响应状态码是否为
200,否则隧道建立失败。 - 包装 SSL: 这是精髓,我们创建一个
HTTPSConnection对象,但不让它自己创建连接,我们获取其sock属性,并将其设置为代理连接的底层套接字 (conn.sock),我们使用ssl.wrap_socket()将这个普通的套接字包装成一个安全的 SSL 套接字。 - 正常使用:
secure_conn就像一个普通的、已经建立好的 HTTPS 连接,可以直接发送请求和接收响应。
重要提醒:更现代的替代方案
虽然了解 http.client 的工作原理非常有价值,但在实际开发中,强烈建议使用更高级的库,requests。
requests 库内置了对代理的优雅支持,代码简洁得多,并且能自动处理 HTTP 和 HTTPS 代理的复杂性。
使用 requests 设置代理示例
import requests
proxies = {
'http': 'http://192.168.1.100:8080',
'https': 'http://192.168.1.100:8080', # 注意:HTTPS 代理也使用 http:// 协议前缀
}
try:
response = requests.get('https://example.com', proxies=proxies)
response.raise_for_status() # 如果请求失败则抛出异常
print(f"Status: {response.status_code}")
print("Content (first 200 chars):")
print(response.text[:200])
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
| 特性 | http.client |
requests |
|---|---|---|
| 易用性 | 低,需要手动处理连接和隧道 | 高,只需一个 proxies 字典 |
| 功能 | 底层,仅支持 HTTP/HTTPS | 高层,支持会话、Cookie、文件上传、流式下载等 |
| 代理支持 | 需要手动实现 HTTP 和 HTTPS 隧道逻辑 | 开箱即用,自动处理 HTTP/HTTPS 代理 |
| 适用场景 | 学习 HTTP 协议底层、需要精细控制连接、无第三方依赖的环境 | 绝大多数 Web API 交互、爬虫、日常网络请求 |
为了日常开发,请优先使用 requests 库,但如果你想深入理解 HTTP 代理的工作原理,或者你的项目环境不允许安装第三方库,那么掌握 http.client 的用法是非常有益的。
