Сообщите urllib2, чтобы использовать пользовательские DNS

Я хотел бы сказать urllib2.urlopen (или пользовательский открыватель ), чтобы использовать 127.0.0.1 (или ::1 ) для разрешения адресов. Однако я бы не изменил свой /etc/resolv.conf .

Одним из возможных решений является использование инструмента, такого как dnspython для запроса адресов и httplib для создания пользовательского открывателя url. Я бы предпочел сказать urlopen чтобы использовать собственный urlopen имен. Какие-либо предложения?

Похоже, что разрешение имен в конечном счете обрабатывается socket.create_connection .

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

Хотя после того, как заголовок «Host:» был настроен, вы можете разрешить хост и передать IP-адрес до новичка.

Я предлагаю вам подкласс httplib.HTTPConnection и обернуть метод connect для изменения self.host перед его передачей в socket.create_connection .

Затем подкласс HTTPHandlerHTTPSHandler ) заменит метод http_open тем, который передает ваш HTTPConnection а не httplib для do_open .

Как это:

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

Очевидно, что есть проблемы с сертификатами, если вы используете HTTPS, и вам нужно будет заполнить MyResolver …

Еще один (грязный) способ – socket.getaddrinfo .

Например, этот код добавляет (неограниченный) кэш для поиска 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 

Вам нужно будет реализовать свой собственный клиент поиска dns (или используя dnspython, как вы сказали). Процедура поиска имени в glibc довольно сложна для обеспечения совместимости с другими системами имен не-DNS. Например, нет способа указать конкретный DNS-сервер в библиотеке glibc вообще.