Dites-urllib2 utiliser DNS personnalisé
Question
Je voudrais dire urllib2.urlopen
(ou un ouvre personnalisée ) pour résoudre les adresses à utiliser 127.0.0.1
(ou ::1
). Je ne changerai pas /etc/resolv.conf
, cependant.
Une solution possible est d'utiliser un outil comme dnspython
pour interroger les adresses et httplib
pour construire un ouvre-url personnalisé. Je préfère dire urlopen
utiliser un serveur de noms personnalisé bien. Toutes les suggestions?
La solution
On dirait que la résolution de noms est finalement pris en charge par socket.create_connection
.
-> urllib2.urlopen
-> httplib.HTTPConnection
-> socket.create_connection
Bien qu'une fois le « hôte ». En-tête a été défini, vous pouvez résoudre l'hôte et de transmettre l'adresse IP par le bas à l'ouverture
Je vous suggère que vous sous-classe httplib.HTTPConnection
, et envelopper la méthode connect
pour modifier self.host
avant de passer à socket.create_connection
.
Ensuite, la sous-classe HTTPHandler
(et HTTPSHandler
) pour remplacer la méthode http_open
avec celui qui passe votre HTTPConnection
au lieu de httplib propre à 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 ...
Autres conseils
Une autre façon (sale) est socket.getaddrinfo
singe ragréage.
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
Vous devrez implémenter votre propre client de recherche dns (ou en utilisant dnspython comme vous le dites). 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 y a par exemple aucun moyen de spécifier un serveur DNS particulier dans la bibliothèque glibc du tout.