I am trying to do some automation in a Python script and I have run into a problem. I am trying to do a POST to a server.
url = 'http://www.example.com'
params = {'arg0': 'value', 'arg1': '+value'}
f = urllib.urlopen(url, urllib.urlencode(params))
print f.read()
I have done a wireshark capture of the equivalent browser operation, where the second arg, arg1 is passed as +value, however when I do it with Python the + gets changed to %2B, i.e.
Line-based text data: application/x-www-form-urlencoded
arg0=value&arg1=%2Bvalue
when it should be:
Line-based text data: application/x-www-form-urlencoded
arg0=value&arg1=+value
I have also used the Requests module and it seems to do the same thing.
url = 'http://www.example.com'
params = {'arg0': 'value', 'arg1': '+value'}
f = requests.post(url, params)
Google is not your friend when you have a problem related to '+' as it seems to be a catch all for so much else.
urllib2.quote(' ')     # '%20'
urllib2.unquote('%20') # ' '
So why not just unquote the parameter part:
f = urllib.urlopen(url, urllib.unquote(urllib.urlencode(params)))
                        The + character is the proper encoding for a space when quoting GET or POST data. Thus, a literal + character needs to be escaped as well, lest it be decoded to a space on the other end. See RFC 2396, section 2.2, section 3.4 and the HTML specification, application/x-www-form-urlencoded section:
Control names and values are escaped. Space characters are replaced by `+', and then reserved characters are escaped as described in [RFC1738], section 2.2.
If you are posting data to an application that does not decode a + character to a space but instead treats such data as literal plus signs instead, you need to encode your parameters yourself using the urllib.quote function instead, specifying that the + character is not to be encoded:
import urllib
def urlencode_withoutplus(query):
    if hasattr(query, 'items'):
        query = query.items()
    l = []
    for k, v in query:
        k = urllib.quote(str(k), safe=' /+')
        v = urllib.quote(str(v), safe=' /+')
        l.append(k + '=' + v)
    return '&'.join(l)
Demo:
>>> urlencode_withoutplus({'arg0': 'value', 'arg1': '+value'})
'arg0=value&arg1=+value'
When using requests, you can simply pass in the result of the above function as the data value, but in that case you need to manually set the content type:
requests.post(url, urlencode_withoutplus(query),
    headers={'Content-Type': 'application/x-www-form-urlencoded'})
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With