【问题标题】:Python urllib2 force IPv4Python urllib2 强制 IPv4
【发布时间】:2013-08-02 01:10:16
【问题描述】:

我正在使用 python 运行一个脚本,该脚本使用 urllib2 从天气 api 获取数据并将其显示在屏幕上。我遇到的问题是,当我查询服务器时,我收到“没有与主机名关联的地址”错误。我可以使用网络浏览器查看 api 的输出,也可以使用 wget 下载文件,但我必须强制 IPv4 才能使其工作。使用 urllib2.urlopen 时是否可以在 urllib2 中强制 IPv4?

【问题讨论】:

    标签: python urllib2 ipv4


    【解决方案1】:

    不直接,不。

    那么,你能做什么呢?


    一种可能性是自己将主机名显式解析为 IPv4,然后使用 IPv4 地址而不是名称作为主机。例如:

    host = socket.gethostbyname('example.com')
    page = urllib2.urlopen('http://{}/path'.format(host))
    

    但是,某些虚拟服务器站点可能需要 Host: example.com 标头,而它们将获得 Host: 93.184.216.119。您可以通过覆盖标题来解决这个问题:

    host = socket.gethostbyname('example.com')
    request = urllib2.Request('http://{}/path'.format(host),
                              headers = {'Host': 'example.com'})
    page = urllib2.urlopen(request)
    

    或者,您可以提供自己的处理程序来代替标准处理程序。但标准处理程序大多只是httplib.HTTPConnection 的包装,真正的问题在于HTTPConnection.connect

    因此,干净的方法是创建您自己的httplib.HTTPConnection 子类,它会像这样覆盖connect

    def connect(self):
        host = socket.gethostbyname(self.host)
        self.sock = socket.create_connection((host, self.post),
                                             self.timeout, self.source_address)
        if self._tunnel_host:
            self._tunnel()
    

    然后创建您自己的 urllib2.HTTPHandler 子类,覆盖 http_open 以使用您的子类:

    def http_open(self, req):
        return self.do_open(my wrapper.MyHTTPConnection, req)
    

    ...HTTPSHandler 也是如此,然后按照urllib2 文档中所示正确连接所有内容。

    做同样事情的快速而肮脏的方法是只猴子补丁httplib.HTTPConnection.connect到上面的函数。


    最后,您可以使用不同的库来代替urllib2。据我记得,requests 并没有让这变得更容易(最终,您必须覆盖或猴子补丁略有不同的方法,但实际上是相同的)。但是,任何libcurl 包装器都将允许您执行与curl_easy_setopt(h, CURLOPT_IPRESOLVE, CURLOPT_IPRESOLVE_V4) 等效的操作。

    【讨论】:

    • socket是urllib2的一个类吗?
    • @TheDoctor:不,socket 是一个模块,socket.socketsocket 模块的一个类。
    • 我已经使用套接字让它工作,但现在我收到 HTTP 错误 596:REST 服务不可用。我不知道如何解决这个问题,但还是谢谢
    【解决方案2】:

    不是一个正确的答案,而是一个替代方案:致电curl?

    import subprocess
    import sys
    
    def log_error(msg):
        sys.stderr.write(msg + '\n')
    
    def curl(url):
        process = subprocess.Popen(
            ["curl", "-fsSkL4", url],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        stdout, stderr = process.communicate()
        if process.returncode == 0:
            return stdout
        else:
            log_error("Failed to fetch: %s" % url)
            log_error(stderr)
            exit(3)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-02
      • 1970-01-01
      • 1970-01-01
      • 2016-01-07
      • 2011-11-28
      • 1970-01-01
      • 2021-06-30
      • 2013-09-18
      相关资源
      最近更新 更多