请求中的大写URL返回“名称无法解析”

时间:2019-04-10 08:13:46

标签: python url python-requests

我想从带有大写字符的URL中获取数据。该URL基于泊坞窗主机名。请求总是返回Name does not resolve,因为它降低了网址。

URL为http://gateway.Niedersachsen/api/bundeslaender

ping gateway.Niedersachsen有效,但ping gateway.niedersachsen无效。

我的Python请求代码:

url = f'http://gateway.Niedersachsen/api/wfs/insertGeometry'
r = requests.get(url)

发生以下错误:

requests.exceptions.ConnectionError: HTTPConnectionPool(host='gateway.niedersachsen', port=80): Max retries exceeded with url: /api/wfs/insertGeometry (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f5f5eb5a3c8>: Failed to establish a new connection: [Errno -2] Name does not resolve'))

我的版本:

$ python --version
Python 3.7.3

> requests.__version__
'2.21.0'

1 个答案:

答案 0 :(得分:2)

RFC 3986第6.2.2.1节介绍了有关URI的信息:

  

[...]方案和主机不区分大小写的,因此应该标准化为小写 [...]。

恕我直言,您的名称解析行为不正确,并且似乎有一个开放的issue与Docker的网络区分大小写有关,我认为此处已在使用。

requests,分别。 urllib3至少在HTTP方案连接方面遵守RFC建议。就requests而言,似乎在四个相关的位置将主机名转换为小写。

  1. urllib3的实用工具类Url,当requests'PreparedRequest实例执行prepare_url方法时起作用。
  2. _default_key_normalizer通过PoolManager映射调用的key_fn_by_scheme函数
  3. 如果您的主机名包含非ASCII字符,则它也是passed through IDNA编码,但是在您的示例中不是这种情况。
  4. urllib3 1.22版还对ConnectionPool基类初始化程序中的主机名进行了lower()调用。显然,从1.23版开始,该规范已移至_ipv6_host函数。

使用Monkeypatching,我似乎能够强制requestsurllib3,保留网址的主机名部分不变:

import functools
import urllib3

def _custom_key_normalizer(key_class, request_context):
    # basically a 1:1 copy of urllib3.poolmanager._default_key_normalizer
    # commenting out 
    # https://github.com/urllib3/urllib3/blob/master/src/urllib3/poolmanager.py#L84
    #context['host'] = context['host'].lower()

class ConnectionPool(object):
    def __init__(self, host, port=None):
        # complete copy of urllib3.connectionpool.ConnectionPool base class
        # I needed this due to my urllib3 version 1.22. 
        # If you have urllib3 >= 1.23 this is not necessary
        # remove the .lower() from 
        # https://github.com/urllib3/urllib3/blob/1.22/urllib3/connectionpool.py#L71
        self.host = urllib3.connectionpool._ipv6_host(host)

urllib3.util.url.NORMALIZABLE_SCHEMES = (None,)
# This is needed for urllib3 >= 1.23. The connectionpool module imports
# NORMALIZABLE_SCHEMES before we can patch it, so we have to explicitly patch it again
urllib3.connectionpool.NORMALIZABLE_SCHEMES = (None,)
urllib3.poolmanager.key_fn_by_scheme['http'] = functools.partial(_custom_key_normalizer, 
                                                                 urllib3.poolmanager.PoolKey)
# just for urllib3 < 1.23
urllib3.connectionpool.ConnectionPool = ConnectionPool

# do not use anything that would import urllib3 before this point    
import requests
url = f'http://gateway.Niedersachsen/api/wfs/insertGeometry'
r = requests.get(url)

我通过显示连接池中使用的主机的错误消息仍使用首字母大写来表示成功:

requests.exceptions.ConnectionError: HTTPConnectionPool(host='gateway.Niedersachsen', port=80): [...]

注意:
直接使用urllib3可能会更简单;
如果有人知道使用requests保留主机大小写的更直接的方法,请 告诉我。