2010-02-10 89 views
13

我想告訴urllib2.urlopen(或自定義開罐器)使用127.0.0.1(或::1)來解析地址。然而,我不會改變我的/etc/resolv.conf告訴urllib2使用自定義DNS

一個可能的解決方案是使用像dnspython這樣的工具查詢地址,並使用httplib來構建一個自定義url開啓者。我寧願告訴urlopen使用自定義域名服務器。有什麼建議麼?

回答

20

看起來像名稱解析最終由socket.create_connection處理。

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

一度的「主持人:」頭已定,就可以解析主機,並通過倒在揭幕戰上的IP地址通過。

我建議你將子類別httplib.HTTPConnection,幷包裝connect方法修改self.host然後將其傳遞到socket.create_connection

然後繼承HTTPHandler(和HTTPSHandler)與一個通過你的HTTPConnection代替httplib的自己來do_open更換http_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,所以這完全就夠了!非常感謝你! – 2010-02-10 19:03:26

+0

也可以重寫'HTTPConnection._create_connection',由於http://bugs.python.org/issue7776,自Python 2.7.7和3.5開始可用。 – 2016-04-13 08:26:26

0

您需要實現自己的DNS查找客戶端(或者像你說的那樣使用dnspython)。 glibc中的名稱查找過程非常複雜,以確保與其他非DNS名稱系統的兼容性。例如,根本沒有辦法在glibc庫中指定特定的DNS服務器。

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

這個黑客的一個優點是,它也攔截了python中幾乎所有的dns查找,不僅通過'urlopen' – 2013-11-21 08:32:33

+0

這是一個更好的解決方案,如果主機的範圍很小。我有10倍的速度。 :) – 2014-06-30 14:16:22