连接urllib2后如何确定服务器的IP地址?

时间:2012-01-19 00:41:05

标签: python ip-address urllib2

我正在使用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地址负载均衡服务器

4 个答案:

答案 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

中提取IP:端口

这是他答案的精简版本:

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)

假设上面的rhttplib.HTTPResponse个实例,我们会做出以下额外假设:

  • 其属性fpr.fp)是class sock._fileobject的一个实例,通过sock.makefile()
  • 中的httplib.HTTPResponse创建
  • 属性_sockr.fp._sock)是传递给 class socket._fileobject ctor的“套接字”实例,它的类型为
  • 属性fpr.fp._sock.fp)是包装真实套接字的另一个socket._filetype
  • 属性_sockr.fp._sock.fp._sock)是真正的socket object

粗略地r.fpsocket._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)