Скажите urllib2 использовать собственный DNS - PullRequest
14 голосов
/ 10 февраля 2010

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

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

Ответы [ 3 ]

21 голосов
/ 10 февраля 2010

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

17 голосов
/ 25 февраля 2013

Другим (грязным) способом является исправление обезьян 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
0 голосов
/ 10 февраля 2010

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

...