2010-02-10 20 views
13

Me gustaría decirle urllib2.urlopen (o un abridor de encargo) para utilizar 127.0.0.1 (o ::1) para resolver las direcciones. Sin embargo, no cambiaría mi /etc/resolv.conf.Dile urllib2 de usar DNS personalizado

Una posible solución es utilizar una herramienta como dnspython para consultar las direcciones y httplib para crear un abridor de url personalizado. Preferiría decirle urlopen para usar un servidor de nombres personalizado. ¿Alguna sugerencia?

Respuesta

20

Parece que la resolución del nombre se maneja finalmente por socket.create_connection.

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

Aunque una vez que el "Anfitrión:" encabezado se ha establecido, puede resolver el anfitrión y transmitir la dirección IP a través hasta el primer partido.

me gustaría sugerir que la subclase httplib.HTTPConnection, y envolver el método connect modificar self.host antes de pasarla a socket.create_connection.

Entonces subclase HTTPHandler (y HTTPSHandler) para reemplazar el método http_open con uno que pasa a su HTTPConnection en lugar de la propia httplib a do_open.

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 se utiliza el HTTPS, y usted tiene que llenar MyResolver ...

+0

No creo que necesite HTTPS por ahora, ¡así que esto será perfectamente suficiente! ¡Muchas gracias! –

+0

También es posible anular 'HTTPConnection._create_connection', que está disponible desde Python 2.7.7 y 3.5 debido a http://bugs.python.org/issue7776. –

0

Usted tendrá que poner en práctica su propia búsqueda de DNS cliente (o usando dnspython como dijiste). El procedimiento de búsqueda de nombre en glibc es bastante complejo para garantizar la compatibilidad con otros sistemas de nombre no DNS. Por ejemplo, no hay forma de especificar un servidor DNS particular en la biblioteca glibc.

16

Otra manera (sucia) es parchear mono socket.getaddrinfo.

Por ejemplo, este código agrega un 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 
+2

Una de las ventajas de este truco también es la interceptación de casi todas las búsquedas DNS en Python, no solo a través de 'urlopen' –

+0

esta es una solución mejor si los hosts tienen un pequeño alcance. Tengo una velocidad 10 veces mayor. :) –