2010-02-10 12 views
13

Mi piacerebbe raccontare urllib2.urlopen (o un apri personalizzato) da usare 127.0.0.1 (o ::1) per risolvere gli indirizzi. Non cambierei il mio /etc/resolv.conf, tuttavia.Dillo urllib2 da utilizzare DNS personalizzato

Una possibile soluzione è utilizzare uno strumento come dnspython per interrogare gli indirizzi e httplib per creare un opuscolo personalizzato. Preferirei dire allo urlopen di usare comunque un server dei nomi personalizzato. Eventuali suggerimenti?

risposta

20

Sembra che la risoluzione dei nomi sia in definitiva gestita da socket.create_connection.

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

Anche se una volta il "Host:" intestazione è stato impostato, è possibile risolvere l'host e trasmettere l'indirizzo IP tramite verso il basso per la gara di apertura.

Io suggerirei che si sottoclasse httplib.HTTPConnection, e avvolgere il metodo connect per modificare self.host prima di passarlo a socket.create_connection.

Poi sottoclasse HTTPHandler (e HTTPSHandler) per sostituire il metodo http_open con uno che passa la vostra HTTPConnection invece di httplib proprio a do_open.

Ti piace questa:

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 di certificati se si utilizza il protocollo HTTPS, e avrete bisogno di compilare MyResolver ...

+0

Per ora non credo che avrò bisogno di HTTPS, quindi questo sarà perfettamente sufficiente! Grazie mille! –

+0

È anche possibile eseguire l'override di 'HTTPConnection._create_connection', disponibile da Python 2.7.7 e 3.5 a causa di http://bugs.python.org/issue7776. –

0

Sarà necessario implementare il proprio DNS lookup client (o usando dnspython come hai detto tu). La procedura di ricerca dei nomi in glibc è piuttosto complessa per garantire la compatibilità con altri sistemi di nomi non-dns. Ad esempio, non c'è alcun modo per specificare un particolare server DNS nella libreria glibc.

16

Un altro (sporco) modo è la patch 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 
+2

Un vantaggio di questo hack intercetta anche quasi tutte le ricerche dns in python, non solo tramite 'urlopen' –

+0

questa è una soluzione migliore se gli host hanno un ambito limitato. ho una velocità 10X con esso. :) –