如何在Requests会话中强制POST请求使用IPv6地址
强制Requests会话使用IPv6发送POST请求
先确认请求的IP版本
你之前用socket.gethostbyname()只能返回IPv4地址,这是因为该方法仅支持IPv4。要同时获取IPv4和IPv6地址,改用socket.getaddrinfo():
import socket from urllib.parse import urlparse url = "sample_ipv6_host.com/path/to/something" hostname = urlparse(url).hostname # 获取主机所有地址信息 addr_info = socket.getaddrinfo(hostname, None) for info in addr_info: addr_family = "IPv4" if info[0] == socket.AF_INET else "IPv6" print(f"{addr_family} 地址: {info[4][0]}")
返回结果里,AF_INET对应IPv4,AF_INET6对应IPv6。
强制使用IPv6的三种方法
方法1:全局修改Socket地址优先级
在程序开头添加以下代码,让Python默认仅解析IPv6地址:
import socket # 强制socket仅返回IPv6地址 socket.getaddrinfo = lambda host, port, family=0, type=0, proto=0, flags=0: \ [(socket.AF_INET6, type, proto, '', (host, port))]
这种方法简单粗暴,会影响整个程序的socket操作,适合仅需IPv6的场景。
方法2:自定义适配器(推荐)
通过自定义Requests的HTTPAdapter,仅让目标会话强制使用IPv6,不影响其他操作:
import requests import socket from urllib3.connection import HTTPConnection, HTTPSConnection from urllib3.connectionpool import HTTPConnectionPool from urllib3.poolmanager import PoolManager # 定义IPv6专用HTTP连接类 class IPv6HTTPConnection(HTTPConnection): def _new_conn(self): # 仅解析IPv6地址 addrs = socket.getaddrinfo(self.host, self.port, socket.AF_INET6) if not addrs: raise socket.gaierror("未找到目标主机的IPv6地址") sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) if self.timeout: sock.settimeout(self.timeout) sock.connect(addrs[0][4]) return sock # 定义IPv6专用HTTPS连接类 class IPv6HTTPSConnection(HTTPSConnection): def _new_conn(self): addrs = socket.getaddrinfo(self.host, self.port, socket.AF_INET6) if not addrs: raise socket.gaierror("未找到目标主机的IPv6地址") sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) if self.timeout: sock.settimeout(self.timeout) sock.connect(addrs[0][4]) return self._prepare_conn(sock) # 定义对应连接池 class IPv6HTTPConnectionPool(HTTPConnectionPool): ConnectionCls = IPv6HTTPConnection class IPv6HTTPSConnectionPool(HTTPConnectionPool): ConnectionCls = IPv6HTTPSConnection # 定义池管理器 class IPv6PoolManager(PoolManager): def _new_pool(self, scheme, host, port, **kwargs): if scheme == "http": return IPv6HTTPConnectionPool(host, port, **kwargs) elif scheme == "https": return IPv6HTTPSConnectionPool(host, port, **kwargs) return super()._new_pool(scheme, host, port, **kwargs) # 定义Requests适配器 class IPv6Adapter(requests.adapters.HTTPAdapter): def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs): self.poolmanager = IPv6PoolManager(num_pools=connections, maxsize=maxsize, block=block, **pool_kwargs) # 使用会话挂载适配器 s = requests.Session() # 为http和https请求挂载IPv6适配器 s.mount("http://", IPv6Adapter()) s.mount("https://", IPv6Adapter()) # 发送POST请求 url = "sample_ipv6_host.com/path/to/something" r = s.post(url, data=data) # 验证使用的IP地址 print(f"请求使用的IPv6地址: {r.raw._original_response.fp.raw._sock.getpeername()[0]}")
方法3:直接使用IPv6地址访问
如果你已经知道目标主机的IPv6地址,可直接在URL中用方括号包裹IPv6地址,例如:
url = "https://[2001:db8::1]/path/to/something" r = s.post(url, data=data)
这种方法无需修改代码,前提是你已获取到目标的IPv6地址。
内容的提问来源于stack exchange,提问作者rachana acharya
相关产品推荐
相关产品推荐

