【问题标题】:Tell urllib2 to use custom DNS告诉 urllib2 使用自定义 DNS
【发布时间】:2011-01-15 05:49:08
【问题描述】:

我想告诉urllib2.urlopen(或自定义开启程序)使用127.0.0.1(或::1)来解析地址。但是,我不会更改我的/etc/resolv.conf

一种可能的解决方案是使用dnspython 之类的工具来查询地址,并使用httplib 来构建自定义网址打开器。不过,我更愿意告诉urlopen 使用自定义名称服务器。有什么建议吗?

【问题讨论】:

    标签: python dns urllib2 dnspython urlopen


    【解决方案1】:

    另一种(肮脏的)方式是猴子补丁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
    

    【讨论】:

    • 这个hack的一个优点是拦截了python中几乎所有的dns查找,不仅仅是通过urlopen
    • 如果主机的范围很小,这是一个更好的解决方案。我的速度提高了 10 倍。 :)
    【解决方案2】:

    看起来名称解析最终由 socket.create_connection 处理。

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

    虽然一旦设置了“Host:”标头,您就可以解析主机并将 IP 地址传递给开启程序。

    我建议您继承httplib.HTTPConnection,并包装connect 方法以修改self.host,然后再将其传递给socket.create_connection

    然后子类HTTPHandler(和HTTPSHandler)以将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...

    【讨论】:

    • 我认为我现在不需要 HTTPS,所以这就足够了!非常感谢!
    • 也可以覆盖HTTPConnection._create_connection,由于bugs.python.org/issue7776,它从Python 2.7.7和3.5开始可用。
    【解决方案3】:

    您需要实现自己的 dns 查找客户端(或使用您所说的 dnspython)。 glibc 中的名称查找过程非常复杂,以确保与其他非 dns 名称系统的兼容性。例如,根本无法在 glibc 库中指定特定的 DNS 服务器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-05
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 2018-03-19
      • 2017-09-30
      • 1970-01-01
      相关资源
      最近更新 更多