6

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

4

2 回答 2

13

不直接,不。

所以,你可以做什么?


一种可能性是自己将主机名显式解析为 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上述函数进行monkeypatch。


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

于 2013-08-02T02:02:30.403 回答
-1

不是一个正确的答案,而是一个替代方案: call 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)
于 2019-07-29T16:08:47.717 回答