Sagen urllib2 individuelle Verwendung von DNS
Frage
Ich möchte urllib2.urlopen
sagen (oder ein benutzerdefinierte Opener ) verwenden 127.0.0.1
(oder ::1
) zu lösen Adressen. Ich würde nicht meinen /etc/resolv.conf
jedoch ändern.
Eine mögliche Lösung ist ein Tool wie dnspython
zu Abfrage-Adressen zu verwenden und httplib
eine benutzerdefinierte URL Opener zu bauen. Ich würde es vorziehen urlopen
sagte wenn einen benutzerdefinierten Name-Server zu verwenden. Irgendwelche Vorschläge?
Lösung
Sieht aus wie Namensauflösung letztlich von socket.create_connection
behandelt wird.
-> urllib2.urlopen
-> httplib.HTTPConnection
-> socket.create_connection
Auch wenn einmal der „Host“. Header gesetzt wurde, können Sie den Host beheben und geben die IP-Adresse durch bis auf den Öffner
Ich würde vorschlagen, dass Sie Unterklasse httplib.HTTPConnection
und wickeln Sie die connect
Methode self.host
zu ändern, bevor es zu socket.create_connection
vorbei.
Dann Unterklasse HTTPHandler
(und HTTPSHandler
) die http_open
Methode mit einer ersetzen, die Ihren HTTPConnection
gehen statt httplib eigenen zu do_open
.
Wie folgt aus:
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']
Natürlich gibt es Zertifikat Fragen, wenn Sie die HTTPS verwenden, und Sie werden MyResolver füllen müssen aus ...
Andere Tipps
Ein weiterer (dirty) Weg ist affen Patchen socket.getaddrinfo
.
Zum Beispiel dieses Code fügt eine (unbegrenzt) Cache für DNS-Lookups.
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
Sie müssen Ihre eigenen DNS-Lookup-Client (oder mit dnspython wie Sie gesagt haben) implementieren. Die Namenssuche Verfahren in glibc sind ziemlich komplex Kompatibilität mit anderen Nicht-DNS-Namen-Systemen zu gewährleisten. Es gibt zum Beispiel keine Möglichkeit, einen bestimmten DNS-Server in der glibc-Bibliothek überhaupt angeben.