我正在使用 python 运行一个脚本,该脚本使用 urllib2 从天气 api 获取数据并将其显示在屏幕上。我遇到的问题是,当我查询服务器时,我收到“没有与主机名关联的地址”错误。我可以使用网络浏览器查看 api 的输出,也可以使用 wget 下载文件,但我必须强制 IPv4 才能使其工作。使用 urllib2.urlopen 时是否可以在 urllib2 中强制 IPv4?
2 回答
不直接,不。
所以,你可以做什么?
一种可能性是自己将主机名显式解析为 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)
.
不是一个正确的答案,而是一个替代方案: 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)