如何使用Python和urllib2设置源IP /接口?
答案 0 :(得分:47)
不幸的是,正在使用的标准库模块堆栈(urllib2,httplib,socket)在某种程度上设计得很糟糕 - 在操作的关键点,HTTPConnection.connect
(在httplib中)委托给{{1在创建套接字实例socket.create_connection
和sock
调用之间没有任何“钩子”,让你在{{1}之前插入sock.connect
这就是你需要设置源IP(我正在广泛传播,因为没有以这种密不透风,过度封装的方式设计抽象 - 我将在本周四的OSCON上以“Zen and the抽象维护艺术“ - 但在这里,你的问题是如何处理一堆以这种方式设计的抽象,感叹。”
当你遇到这样的问题时,你只有两个不太好的解决方案:复制,粘贴和编辑错误设计的代码,你需要放置一个原始设计师不能满足的“钩子”;或者,“猴子补丁”代码。既不是好的,但两者都可以工作,所以至少让我们感谢我们有这样的选择(通过使用开源和动态语言)。在这种情况下,我想我会选择猴子修补(这很糟糕,但复制和粘贴编码更糟糕) - 代码片段如:
sock.bind
根据您的确切需求(您是否需要将所有套接字绑定到相同的源IP,或者......?),您可以在正常使用sock.connect
之前简单地运行它,或者(以更复杂的方式)当然)只需为那些你需要以某种方式绑定的传出套接字运行它(然后每次恢复import socket
true_socket = socket.socket
def bound_socket(*a, **k):
sock = true_socket(*a, **k)
sock.bind((sourceIP, 0))
return sock
socket.socket = bound_socket
以避免未来的套接字被创建)。第二种选择会增加其自身的复杂性,以便妥善协调,所以我等着你在解释之前澄清你是否确实需要这些并发症。
AKX的好答案是“复制/粘贴/编辑”替代方案的变体,因此我不需要对此进行大量扩展 - 请注意,它并未在其urllib2
中完全重现socket.socket = true_socket
1}}方法,请参阅源代码here(在页面的最后),并确定您可能希望在复制/粘贴/编辑的版本中体现的socket.create_connection
函数的其他功能,如果您决定走那条路。
答案 1 :(得分:24)
这似乎有效。
import urllib2, httplib, socket
class BindableHTTPConnection(httplib.HTTPConnection):
def connect(self):
"""Connect to the host and port specified in __init__."""
self.sock = socket.socket()
self.sock.bind((self.source_ip, 0))
if isinstance(self.timeout, float):
self.sock.settimeout(self.timeout)
self.sock.connect((self.host,self.port))
def BindableHTTPConnectionFactory(source_ip):
def _get(host, port=None, strict=None, timeout=0):
bhc=BindableHTTPConnection(host, port=port, strict=strict, timeout=timeout)
bhc.source_ip=source_ip
return bhc
return _get
class BindableHTTPHandler(urllib2.HTTPHandler):
def http_open(self, req):
return self.do_open(BindableHTTPConnectionFactory('127.0.0.1'), req)
opener = urllib2.build_opener(BindableHTTPHandler)
opener.open("http://google.com/").read() # Will fail, 127.0.0.1 can't reach google.com.
你需要找出一些参数化“127.0.0.1”的方法。
答案 2 :(得分:11)
这是使用HTTPConnection's source_address argument(在Python 2.7中引入)的进一步改进:
import functools
import httplib
import urllib2
class BoundHTTPHandler(urllib2.HTTPHandler):
def __init__(self, source_address=None, debuglevel=0):
urllib2.HTTPHandler.__init__(self, debuglevel)
self.http_class = functools.partial(httplib.HTTPConnection,
source_address=source_address)
def http_open(self, req):
return self.do_open(self.http_class, req)
这为我们提供了一个知道source_address的自定义urllib2.HTTPHandler实现。我们可以将其添加到新的urllib2.OpenerDirector并使用以下代码将其安装为默认的开启者(对于将来的urlopen()调用):
handler = BoundHTTPHandler(source_address=("192.168.1.10", 0))
opener = urllib2.build_opener(handler)
urllib2.install_opener(opener)
答案 3 :(得分:2)
答案 4 :(得分:1)
推理我应该在可用的最高级别进行修补,这是Alex的答案的替代方案,其中httplib
代替socket
,利用httplib.HTTPSConnection.__init__()
的{{1}关键字参数(source_address
未公开,AFAICT)。测试并使用Python 2.7.2。
urllib2
答案 5 :(得分:1)
从Python 2.7开始,httplib.HTTPConnection添加了source_address,允许您提供要绑定的IP端口对。
请参阅:http://docs.python.org/2/library/httplib.html#httplib.HTTPConnection