Python urllib2强制IPv4

时间:2013-08-02 01:10:16

标签: python urllib2 ipv4

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

2 个答案:

答案 0 :(得分:12)

不直接,没有。

那么,你能做什么?


一种可能性是自己明确地将主机名解析为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文档中所示。

快速&做同样事情的脏方法就是将monkeypatch httplib.HTTPConnection.connect添加到上面的函数中。


最后,您可以使用其他库而不是urllib2。从我记忆中来看,requests并没有使这更容易(最终,你必须覆盖或monkeypatch稍微不同的方法,但它实际上是相同的)。但是,任何libcurl包装器都允许您执行等效的curl_easy_setopt(h, CURLOPT_IPRESOLVE, CURLOPT_IPRESOLVE_V4)

答案 1 :(得分:0)

不是一个正确的答案,而是一个替代方案:致电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)