2010-02-10 4 views
13
DNS personnalisé

Je voudrais dire urllib2.urlopen (ou un ouvre-personnalisé) pour résoudre les adresses à utiliser 127.0.0.1 (ou ::1). Cependant, je ne changerais pas mon /etc/resolv.conf.Dites-urllib2 à utiliser

Une solution possible consiste à utiliser un outil tel que dnspython pour rechercher des adresses et httplib pour créer un outil d'ouverture d'URL personnalisé. Je préférerais dire à urlopen d'utiliser un serveur de noms personnalisé. Aucune suggestion?

Répondre

20

On dirait que la résolution de noms est finalement traitée par socket.create_connection.

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

Bien que, une fois le « Host: » En-tête a été défini, vous pouvez résoudre l'hôte et de transmettre l'adresse IP par le bas à l'ouvre.

Je vous suggère que vous sous-classe httplib.HTTPConnection, et envelopper la méthode connect de modifier self.host avant de passer à socket.create_connection.

sous-classe ensuite HTTPHandler (et HTTPSHandler) pour remplacer la méthode http_open avec celui qui passe votre HTTPConnection au lieu de son propre httplib à do_open.

Comme ceci:

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

Évidemment, il y a des problèmes de certificat si vous utilisez le protocole HTTPS, et vous devrez remplir MyResolver ...

+0

Je ne pense pas que j'aurai besoin de HTTPS pour le moment, donc ça suffira parfaitement! Merci beaucoup! –

+0

Il est également possible de surcharger 'HTTPConnection._create_connection', qui est disponible depuis Python 2.7.7 et 3.5 à cause de http://bugs.python.org/issue7776. –

0

Vous devrez implémenter votre propre dns lookup client (ou en utilisant dnspython comme vous l'avez dit). La procédure de recherche de nom dans glibc est assez complexe pour assurer la compatibilité avec d'autres systèmes de noms non-DNS. Il n'y a par exemple aucun moyen de spécifier un serveur DNS particulier dans la bibliothèque glibc.

16

Un autre moyen (sale) est de corriger les singes socket.getaddrinfo.

Par exemple, ce code ajoute un cache (illimité) pour les recherches 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

Un avantage de ce hack est également d'intercepter presque toutes les recherches DNS en python, non seulement via 'urlopen' –

+0

c'est une meilleure solution si les hôtes sont limités dans un petit nombre. J'ai une vitesse 10X avec elle. :) –