【问题标题】:Use %20 instead of + for space in python query parameters在 python 查询参数中使用 %20 而不是 + 作为空格
【发布时间】:2014-02-17 08:21:27
【问题描述】:

我编写了以下 python 脚本,使用 python 请求 (http://requests.readthedocs.org/en/latest/):

import requests

payload = {'key1': 'value  1', 'key2': 'value 2'}
headers = {'Content-Type': 'application/json;charset=UTF-8'}
r = requests.get("http://example.com/service", params=payload, headers=headers, 
             auth=("admin", "password"))

如果我查看服务器的访问日志,传入的请求是: /service?key1=value++1&key2=value+2

但是,服务器期望 ...value%20%201&...

我已经读到使用 + 作为空格的占位符是内容类型 application/x-www-form-urlencoded 的一部分,但显然我已经请求了 application/json。

有人知道如何在pythons请求的查询参数中使用%20作为空格吗?

【问题讨论】:

  • 你解决过这个问题吗?你是直接用urllib,还是修改服务器什么的?
  • 我真的不知道了。很久以前,我无法再访问源代码了
  • 我仍然坚持这一点。无赖
  • @Bryce 检查我的答案,简单的解决方案是用 urllib.quote 覆盖 urllib.quote_pluse

标签: python python-requests


【解决方案1】:

为了跟进@WeaselFox 的回答,他们引入了一个补丁,该补丁接受quote_via 关键字参数到urllib.parse.urlencode。现在你可以这样做了:

import requests
import urllib

payload = {'key1': 'value  1', 'key2': 'value 2'}
headers = {'Content-Type': 'application/json;charset=UTF-8'}
params = urllib.parse.urlencode(payload, quote_via=urllib.parse.quote)
r = requests.get("http://example.com/service", params=params, headers=headers,
    auth=("admin", "password"))

【讨论】:

    【解决方案2】:

    我只找到urllib.parse.quote,可以把空格换成%20

    quote 无法转换字典。

    所以,我们必须提前使用quote来转换dict。


    #for python3
    from urllib.parse import quote
    
    payload = {'key1': 'value  1', 'key2': 'value 2'}
    
    newpayload = {}
    for (k, v) in payload.items():
        newpayload[quote(k)] = quote(v)
    print(newpayload)
    #print result: {'key1': 'value%20%201', 'key2': 'value%202'}
    # Now, you can use it in requests
    

    【讨论】:

    • 尝试在我的有效负载中为 %20 交换空间。然后请求解析器对百分比符号进行 URL 编码,因此“this%20that”变成了“this%2520that”——所以 YMMV。
    【解决方案3】:

    我们可以使用urllib2.Request来调用url

    import urllib2
    
    send_params = {'key1': 'value  1', 'key2': 'value 2'}
    new_send_params = []
    for (k, v) in send_params.items():
        new_send_params.append(k + "=" + urllib2.quote(v))
    
    url = 'http://example.com/service?'+ '&'.join(new_send_params)
    req = urllib2.Request(url)
    response = urllib2.urlopen(req)
    print "Request URL: " + url
    #Request URL: http://example.com/service?key1=value%20&key2=value%202
    print response.read()
    #Python Master Request handler 2016-07-04 16:05:19.928132 . Your request path is  /service?key1=value%20&key2=value%202
    

    【讨论】:

      【解决方案4】:

      Python 2.7

      • urllib.quote

      • 覆盖 urllib.quote_pluse
      • urlencoder 使用 urllib.quote_pluse 对数据进行编码。


      代码

      import requests
      import urllib
      urllib.quote_plus=urllib.quote # A fix for urlencoder to give %20 
      payload = {'key1': 'value  1', 'key2': 'value 2'}
      headers = {'Content-Type': 'application/json;charset=UTF-8'}
      param = urllib.urlencode(payload) #encodes the data
      r = requests.get("http://example.com/service", params=param, headers=headers, 
                   auth=("admin", "password"))
      

      输出

      the output for param = urllib.urlencode(payload)
      'key2=value%202&key1=value%20%201' 
      

      【讨论】:

        【解决方案5】:

        试试看。

        import urllib
        urllib.urlencode(params)
        

        http://docs.python.org/2/library/urllib.html#urllib.urlencode

        【讨论】:

        • AttributeError: 模块 'urllib' 没有属性 'urlencode'
        【解决方案6】:

        这似乎是 python 中的一个已知错误/问题:

        http://bugs.python.org/issue13866

        我认为您将不得不使用 urlliburllib2 来解决这个问题并避免请求。查看错误报告以获取有关如何执行此操作的一些提示。

        【讨论】:

          【解决方案7】:
          from urllib.parse import urlencode
          
          def to_query_string(params):
              return urlencode(params, doseq=True).replace('+', '%20')
          

          您可以将字符串传递给params 而不是字典,并手动处理空格。

          大概是这样的

          def to_query_string(p, s=''):
              for k in p:
                  v = p[k]
                  if isinstance(v, str):
                      s += f'{k}={v}&'.replace(' ', '%20')
                  elif isinstance(v, int):
                      s += f'{k}={v}&'
                  elif isinstance(v, list):
                      for i in v:
                          s += f'{k}={i}&'
              return s[:-1]  # remove last '&'
          
          可以用作
          min = 10
          max = 30
          params = {'query': f'score between {min} and {max}', 'limit': 1, 'information': ['name', 'location']}
          response = get('/api/dogs', params=to_query_string(params))
          

          【讨论】:

            【解决方案8】:

            requests 内部使用来自quote()urllib.parse

            import requests
            from requests.utils import quote
            
            url='https://api-adresse.data.gouv.fr/search/'
            requests.get(url, params='q='+quote('rue du Jura')).url
            
            

            https://api-adresse.data.gouv.fr/search/?q=rue%20du%20Jura

            如果你给出一个 str/byte(而不是一个 dict),requests 不要编码你的参数

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-01-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-05-04
              相关资源
              最近更新 更多