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