Python urllib2 强制使用IPv4

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

13

不能直接做到,不行。

那么,你能做些什么呢?


一个可能性是自己显式地将主机名解析成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
@TheDoctor:不,socket是一个模块,而socket.socketsocket模块的一个类。 - abarnert
我已经使用socket使其工作,但现在我遇到了HTTP错误596:REST服务不可用。我不知道如何解决这个问题,但无论如何还是谢谢。 - TheDoctor

-1

并不是一个正式的答案,但是有个替代方案:调用 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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接