Di' a urllib2 di utilizzare il DNS personalizzato
Domanda
Vorrei dirlo urllib2.urlopen
(o a apri personalizzato) usare 127.0.0.1
(O ::1
) per risolvere gli indirizzi.Non cambierei il mio /etc/resolv.conf
, Tuttavia.
Una possibile soluzione è utilizzare uno strumento come dnspython
per interrogare indirizzi e httplib
per creare un apri URL personalizzato.Preferirei raccontarlo urlopen
per utilizzare un server dei nomi personalizzato però.Eventuali suggerimenti?
Soluzione
Sembra che la risoluzione dei nomi alla fine sia gestita da socket.create_connection
.
-> urllib2.urlopen
-> httplib.HTTPConnection
-> socket.create_connection
Tuttavia, una volta impostata l'intestazione "Host:", è possibile risolvere l'host e trasmettere l'indirizzo IP all'apriporta.
Suggerirei di creare una sottoclasse httplib.HTTPConnection
, e avvolgi il connect
metodo da modificare self.host
prima di passarlo a socket.create_connection
.
Quindi sottoclasse HTTPHandler
(E HTTPSHandler
) per sostituire il http_open
metodo con uno che supera il tuo HTTPConnection
invece di quello di httplib to do_open
.
Come questo:
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']
Ovviamente ci sono problemi con i certificati se usi HTTPS e dovrai compilare MyResolver...
Altri suggerimenti
Un altro modo (sporco) è il patching delle scimmie socket.getaddrinfo
.
Ad esempio, questo codice aggiunge una cache (illimitata) per le ricerche 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
Dovrai implementare il tuo client di ricerca DNS (o utilizzare dnspython come hai detto).La procedura di ricerca dei nomi in glibc è piuttosto complessa per garantire la compatibilità con altri sistemi di nomi non DNS.Ad esempio, non esiste alcun modo per specificare un particolare server DNS nella libreria glibc.