Dile a urllib2 utilizar DNS personalizado
Pregunta
Me gustaría decirle urllib2.urlopen
(o un costumbre abridor ) para utilizar 127.0.0.1
(o ::1
) para resolver las direcciones. No cambiaría mi /etc/resolv.conf
, sin embargo.
Una posible solución es utilizar una herramienta como dnspython
para consultar direcciones y httplib
para construir un abridor de URL personalizada. Yo prefiero decir urlopen
utilizar un servidor de nombres de encargo sin embargo. ¿Alguna sugerencia?
Solución
Parece que la resolución de nombres es en última instancia a cargo de socket.create_connection
.
-> urllib2.urlopen
-> httplib.HTTPConnection
-> socket.create_connection
Aunque una vez que el "Anfitrión:". Cabecera se ha establecido, puede resolver el anfitrión y transmitir la dirección IP a través hasta el abridor
Me gustaría sugerir que httplib.HTTPConnection
subclase, y envolver el método connect
modificar self.host
antes de pasarla a socket.create_connection
.
A continuación, HTTPHandler
subclase (y HTTPSHandler
) para reemplazar el método http_open
con uno que pasa a su HTTPConnection
en lugar de la propia a do_open
httplib.
De esta manera:
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']
Es obvio que hay problemas de certificado si utiliza las HTTPS, y usted necesitará llenar MyResolver ...
Otros consejos
Otra forma (sucio) es socket.getaddrinfo
mono-parches.
Por ejemplo, este código añade una memoria caché (ilimitado) para las búsquedas 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
Usted tendrá que implementar su propio cliente de búsqueda de DNS (o usando dnspython como usted ha dicho). El procedimiento de búsqueda de nombre en glibc es bastante complejo para asegurar la compatibilidad con otros sistemas de nombres no DNS. Hay, por ejemplo, no hay manera de especificar un servidor DNS en particular en la biblioteca glibc en absoluto.