2010-02-10 3 views
13

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

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

ответ

20

Похоже, что разрешение имени в конечном итоге обрабатывается 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 ...

+0

Я не думаю, что мне понадобится HTTPS, так что этого вполне хватит! Большое спасибо! –

+0

Также возможно переопределить 'HTTPConnection._create_connection', который доступен с Python 2.7.7 и 3.5 из-за http://bugs.python.org/issue7776. –

0

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

16

Другой (грязный) способ - обезглавливание обезьян 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 
+2

Одно из преимуществ этого взлома также перехватывает почти все запросы dns в python, причем не только через 'urlopen' –

+0

, это лучшее решение, если хосты ограничены небольшим числом. У меня 10-кратное ускорение. :) –

 Смежные вопросы

  • Нет связанных вопросов^_^