749

I am trying to urlencode this string before I submit.

queryString = 'eventName=' + evt.fields["eventName"] + '&' + 'eventDescription=' + evt.fields["eventDescription"]; 
dreftymac
  • 31,404
  • 26
  • 119
  • 182
James
  • 9,694
  • 5
  • 32
  • 38

15 Answers15

1287

Python 2

What you're looking for is urllib.quote_plus:

safe_string = urllib.quote_plus('string_of_characters_like_these:$#@=?%^Q^$')

#Value: 'string_of_characters_like_these%3A%24%23%40%3D%3F%25%5EQ%5E%24'

Python 3

In Python 3, the urllib package has been broken into smaller components. You'll use urllib.parse.quote_plus (note the parse child module)

import urllib.parse
safe_string = urllib.parse.quote_plus(...)
Pikamander2
  • 7,332
  • 3
  • 48
  • 69
Ricky Sahu
  • 23,455
  • 4
  • 42
  • 32
  • 9
    Thanks! In my case however I need to put: `import urllib.parse ... urllib.parse.quote_plus(query)` – ivkremer Sep 02 '14 at 11:53
  • 3
    very good,but why does not used to Unicode?if the url string is Unicode,I must encode it to UTF-8.Is there any other way to do it? – Karl Doenitz Feb 06 '15 at 06:55
  • 8
    This works great, but I couldn't access some online services (REST) until I added this parameter safe=';/?:@&=+$,' – rovyko May 29 '15 at 14:56
  • I tried that in Python 3 but was not able to: http://stackoverflow.com/questions/40557606/how-to-url-encode-in-python-3 – amphibient Nov 11 '16 at 23:10
  • 1
    `python3 -c "import urllib.parse, sys; print(urllib.parse.quote_plus(sys.argv[1])) "string to encode"` for a one liner on the command line – Amos Joshua May 08 '19 at 12:19
  • This one (the Python 3 version) solved it for me for a telegram bot sending a message with accents and spaces to a channel. – Rusca8 Mar 22 '20 at 15:17
  • 2
    @AmosJoshua I think you missed a double quote `"` just after the double round closing brackets `))`, it should be: `python3 -c "import urllib.parse, sys; print(urllib.parse.quote_plus(sys.argv[1]))" "string to encode"` – lgespee Nov 02 '21 at 19:04
  • @lgespee - good point, thanks for pointing that out. I can't seem to be able to edit the comment... I hope yours will rise further up – Amos Joshua Nov 03 '21 at 20:22
714

You need to pass your parameters into urlencode() as either a mapping (dict), or a sequence of 2-tuples, like:

>>> import urllib
>>> f = { 'eventName' : 'myEvent', 'eventDescription' : 'cool event'}
>>> urllib.urlencode(f)
'eventName=myEvent&eventDescription=cool+event'

Python 3 or above

Use urllib.parse.urlencode:

>>> urllib.parse.urlencode(f)
eventName=myEvent&eventDescription=cool+event

Note that this does not do url encoding in the commonly used sense (look at the output). For that use urllib.parse.quote_plus.

FelixEnescu
  • 4,664
  • 2
  • 33
  • 34
bgporter
  • 35,114
  • 8
  • 59
  • 65
  • I was just about to put solved I came up w/ the same solution. Thanks. – James Apr 09 '11 at 20:41
  • 17
    "Note that the urllib.urlencode does not always do the trick. The problem is that some services care about the order of arguments, which gets lost when you create the dictionary. For such cases, urllib.quote_plus is better, as Ricky suggested." – Blairg23 Aug 17 '15 at 21:35
  • 32
    Technically, that's a bug in the services, isn't it? – holdenweb Aug 19 '15 at 09:52
  • 11
    and how would one do this if you just want to make a string URL safe, without building a full query argument string? – Mike 'Pomax' Kamermans Jun 07 '16 at 02:14
  • 1
    @Mike'Pomax'Kamermans -- see e.g. http://stackoverflow.com/questions/12082314/how-to-convert-a-url-string-to-safe-characters-with-python or Ricky's answer to this question. – bgporter Jun 07 '16 at 12:12
  • You can pass a list of tuples to urlencode and order is preserved: `urllib.urlencode([('name', 'brandon'), ('x', 1000)])` produces `'name=brandon&x=1000'` (as the answer states, though it does not provide example code for it) – Brandon Rhodes Mar 16 '17 at 21:09
  • 1
    @bk0 it seems your method is only valid for dictionaries, and not strings. – JD Gamboa Feb 02 '18 at 22:23
  • @Blairg23 Unless the same keys are repeated multiple times, wouldn't it work to just pass it an [`OrderedDict`](https://docs.python.org/2/library/collections.html#collections.OrderedDict)? – koyae Aug 25 '18 at 03:37
  • @JohnStrood - Python 2.7 and earlier have `urlparse`, 3.0+ uses `urllib.parse` – bgporter Sep 30 '18 at 13:46
  • @holdenweb for signatures and other crypto stuff order is important – max Jan 06 '19 at 22:00
  • In which case why not just work with the original query string, rather than its decomposition by the parser, which is available to allow easy access to individual parameter values. – holdenweb Jan 07 '19 at 05:58
  • note that dictionaries are officially sorted as of python 3.7+ (and cpython 3.6+) – Stefaan Ghysels Jun 18 '21 at 09:47
  • urllib has been split up in Python 3. The urllib.urlencode() function is now urllib.parse.urlencode() – dbz Apr 29 '23 at 17:49
94

Try requests instead of urllib and you don't need to bother with urlencode!

import requests
requests.get('http://youraddress.com', params=evt.fields)

EDIT:

If you need ordered name-value pairs or multiple values for a name then set params like so:

params=[('name1','value11'), ('name1','value12'), ('name2','value21'), ...]

instead of using a dictionary.

Tim Tisdall
  • 9,914
  • 3
  • 52
  • 82
Barney Szabolcs
  • 11,846
  • 12
  • 66
  • 91
  • 7
    This does not address the issue of ordering the name value pairs, also this requires the permission to install external libraries which might not be doable for the project. – dreftymac Sep 06 '13 at 02:10
  • 1
    I posted the minimal code that would work for the OP. The OP did not request ordered pairs however it is also doable, see my update. – Barney Szabolcs Sep 10 '13 at 08:02
  • @dreftymac: this does address ordering (although it was not part of the question), please read my updated answer. – Barney Szabolcs Nov 25 '13 at 20:26
41

Context

  • Python (version 2.7.2 )

Problem

  • You want to generate a urlencoded query string.
  • You have a dictionary or object containing the name-value pairs.
  • You want to be able to control the output ordering of the name-value pairs.

Solution

  • urllib.urlencode
  • urllib.quote_plus

Pitfalls

Example

The following is a complete solution, including how to deal with some pitfalls.

### ********************
## init python (version 2.7.2 )
import urllib

### ********************
## first setup a dictionary of name-value pairs
dict_name_value_pairs = {
  "bravo"   : "True != False",
  "alpha"   : "http://www.example.com",
  "charlie" : "hello world",
  "delta"   : "1234567 !@#$%^&*",
  "echo"    : "user@example.com",
  }

### ********************
## setup an exact ordering for the name-value pairs
ary_ordered_names = []
ary_ordered_names.append('alpha')
ary_ordered_names.append('bravo')
ary_ordered_names.append('charlie')
ary_ordered_names.append('delta')
ary_ordered_names.append('echo')

### ********************
## show the output results
if('NO we DO NOT care about the ordering of name-value pairs'):
  queryString  = urllib.urlencode(dict_name_value_pairs)
  print queryString 
  """
  echo=user%40example.com&bravo=True+%21%3D+False&delta=1234567+%21%40%23%24%25%5E%26%2A&charlie=hello+world&alpha=http%3A%2F%2Fwww.example.com
  """

if('YES we DO care about the ordering of name-value pairs'):
  queryString  = "&".join( [ item+'='+urllib.quote_plus(dict_name_value_pairs[item]) for item in ary_ordered_names ] )
  print queryString
  """
  alpha=http%3A%2F%2Fwww.example.com&bravo=True+%21%3D+False&charlie=hello+world&delta=1234567+%21%40%23%24%25%5E%26%2A&echo=user%40example.com
  """ 
Barney Szabolcs
  • 11,846
  • 12
  • 66
  • 91
dreftymac
  • 31,404
  • 26
  • 119
  • 182
35

Python 3:

urllib.parse.quote_plus(string, safe='', encoding=None, errors=None)

mousetail
  • 7,009
  • 4
  • 25
  • 45
Janus Troelsen
  • 20,267
  • 14
  • 135
  • 196
27

Try this:

urllib.pathname2url(stringToURLEncode)

urlencode won't work because it only works on dictionaries. quote_plus didn't produce the correct output.

Charlie
  • 8,530
  • 2
  • 55
  • 53
22

Note that the urllib.urlencode does not always do the trick. The problem is that some services care about the order of arguments, which gets lost when you create the dictionary. For such cases, urllib.quote_plus is better, as Ricky suggested.

Adam Cataldo
  • 703
  • 8
  • 10
  • 2
    It works fine and preserves order if you pass a list of tuples: `>>> import urllib >>> urllib.urlencode([('name', 'brandon'), ('uid', 1000)]) 'name=brandon&uid=1000' ` – Brandon Rhodes Mar 16 '17 at 21:07
14

In Python 3, this worked with me

import urllib

urllib.parse.quote(query)
Yan Foto
  • 10,850
  • 6
  • 57
  • 88
Mazen Aly
  • 5,695
  • 1
  • 15
  • 12
6

for future references (ex: for python3)

>>> import urllib.request as req
>>> query = 'eventName=theEvent&eventDescription=testDesc'
>>> req.pathname2url(query)
>>> 'eventName%3DtheEvent%26eventDescription%3DtestDesc'
nickanor
  • 637
  • 2
  • 12
  • 18
  • 1
    usually you only want to url encode the values, what you have done here would make an invalid GET query – Codewithcheese Feb 24 '15 at 14:38
  • 1
    The output for `'c:/2 < 3'` on Windows is `'///C://2%20%3C%203'`. I want something that would just output `'c:/2%20%3C%203'`. – binki Sep 29 '15 at 20:42
5

If the urllib.parse.urlencode( ) is giving you errors , then Try the urllib3 module .

The syntax is as follows :

import urllib3
urllib3.request.urlencode({"user" : "john" }) 
Community
  • 1
  • 1
Natesh bhat
  • 12,274
  • 10
  • 84
  • 125
5

For use in scripts/programs which need to support both python 2 and 3, the six module provides quote and urlencode functions:

>>> from six.moves.urllib.parse import urlencode, quote
>>> data = {'some': 'query', 'for': 'encoding'}
>>> urlencode(data)
'some=query&for=encoding'
>>> url = '/some/url/with spaces and %;!<>&'
>>> quote(url)
'/some/url/with%20spaces%20and%20%25%3B%21%3C%3E%26'
bschlueter
  • 3,817
  • 1
  • 30
  • 48
3
import urllib.parse
query = 'Hellö Wörld@Python'
urllib.parse.quote(query) // returns Hell%C3%B6%20W%C3%B6rld%40Python

Luqman
  • 127
  • 8
  • `urllib.parse.quote` is already mentioned in [this earlier answer](https://stackoverflow.com/a/33266288/2745495). – Gino Mempin Nov 19 '21 at 03:38
1

Another thing that might not have been mentioned already is that urllib.urlencode() will encode empty values in the dictionary as the string None instead of having that parameter as absent. I don't know if this is typically desired or not, but does not fit my use case, hence I have to use quote_plus.

Joseph
  • 45
  • 8
0

For Python 3 urllib3 works properly, you can use as follow as per its official docs :

import urllib3

http = urllib3.PoolManager()
response = http.request(
     'GET',
     'https://api.prylabs.net/eth/v1alpha1/beacon/attestations',
     fields={  # here fields are the query params
          'epoch': 1234,
          'pageSize': pageSize 
      } 
 )
response = attestations.data.decode('UTF-8')
Georgy
  • 12,464
  • 7
  • 65
  • 73
cryptoKTM
  • 2,593
  • 22
  • 21
-2

If you don't want to use urllib.

https://github.com/wayne931121/Python_URL_Decode

URL_RFC_3986 = {
"!": "%21", "#": "%23", "$": "%24", "&": "%26", "'": "%27", "(": "%28", ")": "%29", "*": "%2A", "+": "%2B", 
",": "%2C", "/": "%2F", ":": "%3A", ";": "%3B", "=": "%3D", "?": "%3F", "@": "%40", "[": "%5B", "]": "%5D",
}

def url_encoder(b):
    # https://zh.wikipedia.org/wiki/%E7%99%BE%E5%88%86%E5%8F%B7%E7%BC%96%E7%A0%81
    if type(b)==bytes:
        b = b.decode(encoding="utf-8") #byte can't insert many utf8 charaters
    result = bytearray() #bytearray: rw, bytes: read-only
    for i in b:
        if i in URL_RFC_3986:
            for j in URL_RFC_3986[i]:
                result.append(ord(j))
            continue
        i = bytes(i, encoding="utf-8")
        if len(i)==1:
            result.append(ord(i))
        else:
            for c in i:
                c = hex(c)[2:].upper()
                result.append(ord("%"))
                result.append(ord(c[0:1]))
                result.append(ord(c[1:2]))
    result = result.decode(encoding="ascii")
    return result

#print(url_encoder("我好棒==%%0.0:)")) ==> '%E6%88%91%E5%A5%BD%E6%A3%92%3D%3D%%0.0%3A%29'
謝咏辰
  • 37
  • 4