Dile a urllib2 que use DNS personalizado

Me gustaría decirle a urllib2.urlopen (o un abridor personalizado ) que use 127.0.0.1 (o ::1 ) para resolver direcciones. Sin embargo, no cambiaría mi /etc/resolv.conf .

Una posible solución es usar una herramienta como dnspython para consultar direcciones y httplib para crear un abridor de URL personalizado. Sin urlopen , prefiero decirle a urlopen que use un servidor de nombres personalizado. ¿Alguna sugerencia?

Parece que la resolución de nombres es manejada en última instancia por socket.create_connection .

 -> urllib2.urlopen -> httplib.HTTPConnection -> socket.create_connection 

Aunque una vez que se ha establecido el encabezado “Host:”, puede resolver el host y pasar la dirección IP a través del abridor.

Le sugeriría que subclasifique httplib.HTTPConnection y httplib.HTTPConnection el método de connect para modificar self.host antes de pasarlo a socket.create_connection .

Luego, subclase HTTPHandler (y HTTPSHandler ) para reemplazar el método http_open con uno que pase su HTTPConnection lugar del propio do_open para do_open .

Me gusta esto:

 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'] 

Obviamente, hay problemas con el certificado si utiliza el HTTPS, y deberá completar MyResolver …

Otra forma (sucia) es el socket.getaddrinfo parcheo de socket.getaddrinfo .

Por ejemplo, este código agrega un caché (ilimitado) para las búsquedas de 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 

Necesitará implementar su propio cliente de búsqueda dns (o usar dnspython como dijo). El procedimiento de búsqueda de nombres en glibc es bastante complejo para garantizar la compatibilidad con otros sistemas de nombres que no sean DNS. Por ejemplo, no hay forma de especificar un servidor DNS en particular en la biblioteca glibc.