我想告诉urllib2.urlopen
(或自定义开瓶器)使用127.0.0.1
(或::1
)来解析地址。但是,我不会改变我/etc/resolv.conf
的 .
一种可能的解决方案是使用诸如dnspython
查询地址和httplib
构建自定义 url 打开器之类的工具。不过,我更愿意告诉urlopen
使用自定义名称服务器。有什么建议么?
看起来名称解析最终由socket.create_connection
.
-> urllib2.urlopen
-> httplib.HTTPConnection
-> socket.create_connection
虽然一旦设置了“主机:”标头,您就可以解析主机并将 IP 地址向下传递给开启程序。
我建议你子类httplib.HTTPConnection
化,并在将connect
要修改的方法self.host
传递给socket.create_connection
.
然后子类HTTPHandler
(和HTTPSHandler
)以将http_open
方法替换为将您的HTTPConnection
而不是 httplib 自己的方法传递给do_open
.
像这样:
import urllib2
import httplib
import socket
def MyResolver(host):
if host == 'news.bbc.co.uk':
return '66.102.9.104' # Google IP
else:
return host
class MyHTTPConnection(httplib.HTTPConnection):
def connect(self):
self.sock = socket.create_connection((MyResolver(self.host),self.port),self.timeout)
class MyHTTPSConnection(httplib.HTTPSConnection):
def connect(self):
sock = socket.create_connection((MyResolver(self.host), self.port), self.timeout)
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)
class MyHTTPHandler(urllib2.HTTPHandler):
def http_open(self,req):
return self.do_open(MyHTTPConnection,req)
class MyHTTPSHandler(urllib2.HTTPSHandler):
def https_open(self,req):
return self.do_open(MyHTTPSConnection,req)
opener = urllib2.build_opener(MyHTTPHandler,MyHTTPSHandler)
urllib2.install_opener(opener)
f = urllib2.urlopen('http://news.bbc.co.uk')
data = f.read()
from lxml import etree
doc = etree.HTML(data)
>>> print doc.xpath('//title/text()')
['Google']
显然如果你使用 HTTPS 会有证书问题,你需要填写 MyResolver...
另一种(肮脏的)方式是猴子补丁socket.getaddrinfo
。
例如,此代码为 dns 查找添加了(无限)缓存。
import socket
prv_getaddrinfo = socket.getaddrinfo
dns_cache = {} # or a weakref.WeakValueDictionary()
def new_getaddrinfo(*args):
try:
return dns_cache[args]
except KeyError:
res = prv_getaddrinfo(*args)
dns_cache[args] = res
return res
socket.getaddrinfo = new_getaddrinfo
您将需要实现自己的 dns 查找客户端(或如您所说使用 dnspython)。glibc 中的名称查找过程非常复杂,以确保与其他非 dns 名称系统的兼容性。例如,根本无法在 glibc 库中指定特定的 DNS 服务器。