我正在使用urllib2从服务器下载数据。但我需要确定我连接的服务器的IP地址。
import urllib2
STD_HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,
*/*;q=0.8',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept-Language': 'en-us,en;q=0.5',
'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64;en-US;rv:1.9.2.12)
Gecko/20101028 Firefox/3.6.12'}
request = urllib2.Request(url, None, STD_HEADERS)
data = urllib2.urlopen(request)
请不要让我使用URL找到IP地址,因为这不能保证从中下载数据的服务器和IP地址查询在“HTTPRedirects”或者“HTTPRedirects”的情况下解析为相同的IP地址负载均衡服务器
答案 0 :(得分:5)
以下是适用于Python 2.7的内容:
>>> from urllib2 import urlopen
>>> from socket import fromfd
>>> from socket import AF_INET
>>> from socket import SOCK_STREAM
>>> r = urlopen('http://stackoverflow.com/')
>>> mysockno = r.fileno()
>>> mysock = fromfd( mysockno, AF_INET, SOCK_STREAM)
>>> (ip, port) = mysock.getpeername()
>>> print "got IP %s port %d" % (ip, port)
got IP 198.252.206.140 port 80
答案 1 :(得分:3)
import urllib2, socket, urlparse
# set up your request as before, then:
data = urllib2.urlopen(request)
addr = socket.gethostbyname(urlparse.urlparse(data.geturl()).hostname)
在重定向之后, data.geturl()
返回用于实际检索资源的URL。然后使用urlparse
将主机名取出并发送到socket.gethostbyname
以获取IP地址。
某些主机可能有一个给定主机名的IP地址,因此该请求仍有可能由另一台服务器完成,但这与您将要获得的一样接近。在URL请求之后的gethostbyname
无论如何都将使用您的DNS缓存,除非您正在处理1秒的生存时间,否则您将获得相同的服务器刚刚使用过。
如果这还不够,你可以分离一个线程并在仍然连接到远程服务器时执行lsof
。我确信你可以说服urllib2
让连接保持打开一段时间,这样就可以成功。不过,这似乎比它的价值还要多。
答案 2 :(得分:3)
我知道这是一个老问题,但我发现urllib2返回的响应对象包含ip。这看起来有点像黑客,但它确实有效。
import urllib2
STD_HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,
*/*;q=0.8',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept-Language': 'en-us,en;q=0.5',
'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64;en-US;rv:1.9.2.12)
Gecko/20101028 Firefox/3.6.12'}
request = urllib2.Request(url, None, STD_HEADERS)
data = urllib2.urlopen(request)
data.fp._sock.fp._sock.getpeername()
答案 3 :(得分:1)
荣誉应该to gawry for his answer。但是,我不想用我的补充来破坏他的答案,这似乎比他的完整答案要长一些。所以请将此答案作为他答案的补充。
这只适用于带有urllib2
的Python 2.x. Python 3.x中类的结构发生了变化,因此即使是偶然的兼容性技巧:
尝试: 将urllib.request导入为urllib2 除了ImportError: import urllib2
不会救你。我想这就是你不应该依赖类的内部的原因,特别是当属性以下划线开头时,因此惯例不是公共接口的一部分,尽管可以访问。
结论:以下技巧不适用于Python 3.x。
HTTPResponse
这是他答案的精简版本:
import urllib2
r = urllib2.urlopen("http://google.com")
peer = r.fp._sock.fp._sock.getpeername()
print("%s connected\n\tIP and port: %s:%d\n\tpeer = %r" % (r.geturl(), peer[0], peer[1], peer))
输出将是这样的(出于隐私原因,修剪ei
参数):
http://www.google.co.jp/?gfe_rd=cr&ei=_... connected
IP and port: 173.194.120.95:80
peer = ('173.194.120.95', 80)
假设上面的r
是httplib.HTTPResponse
个实例,我们会做出以下额外假设:
fp
(r.fp
)是class sock._fileobject
的一个实例,通过sock.makefile()
httplib.HTTPResponse
创建
_sock
(r.fp._sock
)是传递给 class socket._fileobject
ctor的“套接字”实例,它的类型为fp
(r.fp._sock.fp
)是包装真实套接字的另一个socket._filetype
_sock
(r.fp._sock.fp._sock
)是真正的socket object 粗略地r.fp
是socket._fileobject
,而r.fp._sock.fp._sock
是由_socket.socket
包裹另一个socket._fileobject
包裹的实际套接字实例(类型socket._fileobject
) (两层深)。这就是为什么我们在中间有一些不同寻常的.fp._sock.fp._sock.
。
上面getpeername()
返回的变量是IPv4的元组。元素0是字符串形式的IP,元素1是在该IP上建立连接的端口。 注意:文档说明此格式取决于实际的套接字类型。
HTTPError
另一方面,由于urllib2.HTTPError
派生自URLError
以及addinfourl
,并将fp
存储在同名的属性中,我们甚至可以提取该信息从HTTPError
例外(不是来自URLError
),通过在混合中添加另一个fp
,如下所示:
import urllib2
try:
r = urllib2.urlopen("https://stackoverflow.com/doesnotexist/url")
peer = r.fp._sock.fp._sock.getpeername()
print("%s connected\n\tIP and port: %s:%d\n\tpeer = %r" % (r.geturl(), peer[0], peer[1], peer))
except urllib2.HTTPError, e:
if e.fp is not None:
peer = e.fp.fp._sock.fp._sock.getpeername()
print("%s: %s\n\tIP and port: %s:%d\n\tpeer = %r" % (str(e), e.geturl(), peer[0], peer[1], peer))
else:
print("%s: %s\n\tIP and port: <could not be retrieved>" % (str(e), e.geturl()))
输出将是这样的(除非StackOverflow的某人添加了该URL;)):
HTTP Error 404: Not Found: https://stackoverflow.com/doesnotexist/url
IP and port: 198.252.206.16:80
peer = ('198.252.206.16', 80)