杰瑞科技汇

Python httplib如何设置代理?

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

Python httplib如何设置代理?-图1
(图片来源网络,侵删)

核心概念

使用代理的核心思想是:不直接连接目标服务器,而是先连接到代理服务器,然后通过代理服务器去请求目标资源

在 HTTP 协议中,这通常通过 CONNECT 方法实现(用于 HTTPS)或在请求的 URL 中明确指定代理(用于 HTTP)。


为 HTTP 请求设置代理

对于 HTTP 请求,设置代理相对简单,你只需要构造一个完整的 URL,格式为 http://代理服务器地址:端口/目标URL

示例代码

假设我们要通过代理 168.1.100:8080 访问 http://example.com

Python httplib如何设置代理?-图2
(图片来源网络,侵删)
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()

代码解释:

  1. 构造代理路径: 我们创建了一个特殊的路径 http://example.com/,并将其编码后作为请求路径发送给代理,代理服务器看到这个路径就知道它需要去连接 example.com 并获取资源。
  2. 连接代理: http.client.HTTPConnection(proxy_host, proxy_port) 建立到代理服务器的 TCP 连接。
  3. 发送请求: conn.request(...) 将请求发送给代理。Host 头部设置为目标服务器的地址,这是代理服务器所需要的。
  4. 处理响应: 后续的步骤与直接连接服务器没有区别。

为 HTTPS 请求设置代理(SOCKS 代理除外)

HTTPS 请求更复杂,因为连接一开始就是加密的,代理服务器无法直接解析 https://example.com 这样的路径。

标准做法是使用 CONNECT 方法:

  1. 客户端首先与代理服务器建立一个未加密的 TCP 连接。
  2. 客户端向代理服务器发送一个 CONNECT example.com:443 HTTP/1.1 请求。
  3. 代理服务器与目标服务器的 443 端口(HTTPS 默认端口)建立一个 TCP 连接。
  4. 代理服务器返回 200 Connection established 响应。
  5. 从此时起,客户端和目标服务器之间的所有通信都通过这个已建立的 TCP 隧道进行,客户端会直接在该连接上发送加密的 HTTPS 请求,代理服务器只是简单地转发数据包,不再解析内容。

http.client 提供了便捷的方式来处理这个过程。

Python httplib如何设置代理?-图3
(图片来源网络,侵删)

示例代码

假设我们要通过代理 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()

代码解释:

  1. 连接代理: 同样,我们先用 HTTPConnection 连接到代理服务器。
  2. CONNECT 请求: conn.request("CONNECT", f"{target_host}:{target_port}") 发送建立隧道的指令。
  3. 检查响应: 必须检查响应状态码是否为 200,否则隧道建立失败。
  4. 包装 SSL: 这是精髓,我们创建一个 HTTPSConnection 对象,但让它自己创建连接,我们获取其 sock 属性,并将其设置为代理连接的底层套接字 (conn.sock),我们使用 ssl.wrap_socket() 将这个普通的套接字包装成一个安全的 SSL 套接字。
  5. 正常使用: 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 的用法是非常有益的。

分享:
扫描分享到社交APP
上一篇
下一篇