2010-02-10 85 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