Making a POST call instead of GET using urllib2
Asked Answered
I

7

61

There's a lot of stuff out there on urllib2 and POST calls, but I'm stuck on a problem.

I'm trying to do a simple POST call to a service:

url = 'http://myserver/post_service'
data = urllib.urlencode({'name' : 'joe',
                         'age'  : '10'})
content = urllib2.urlopen(url=url, data=data).read()
print content

I can see the server logs and it says that I'm doing GET calls, when I'm sending the data argument to urlopen.

The library is raising an 404 error (not found), which is correct for a GET call, POST calls are processed well (I'm also trying with a POST within a HTML form).

Isometrics answered 14/6, 2011 at 18:50 Comment(2)
As an aside, the server should be returning a 405.Magi
@IgnacioVazquez-Abrams This doc says 405 is not appropriate for GET and HEAD requests...developer.mozilla.org/en-US/docs/Web/HTTP/…Quechua
P
48

This may have been answered before: Python URLLib / URLLib2 POST.

Your server is likely performing a 302 redirect from http://myserver/post_service to http://myserver/post_service/. When the 302 redirect is performed, the request changes from POST to GET (see Issue 1401). Try changing url to http://myserver/post_service/.

Profant answered 14/6, 2011 at 19:13 Comment(1)
Yep! I was missing a trailing slash in the URL. I'm voting up and marking as acepted. Thanks!!Isometrics
N
49

Do it in stages, and modify the object, like this:

# make a string with the request type in it:
method = "POST"
# create a handler. you can specify different handlers here (file uploads etc)
# but we go for the default
handler = urllib2.HTTPHandler()
# create an openerdirector instance
opener = urllib2.build_opener(handler)
# build a request
data = urllib.urlencode(dictionary_of_POST_fields_or_None)
request = urllib2.Request(url, data=data)
# add any other information you want
request.add_header("Content-Type",'application/json')
# overload the get method function with a small anonymous function...
request.get_method = lambda: method
# try it; don't forget to catch the result
try:
    connection = opener.open(request)
except urllib2.HTTPError,e:
    connection = e

# check. Substitute with appropriate HTTP code.
if connection.code == 200:
    data = connection.read()
else:
    # handle the error case. connection.read() will still contain data
    # if any was returned, but it probably won't be of any use

This way allows you to extend to making PUT, DELETE, HEAD and OPTIONS requests too, simply by substituting the value of method or even wrapping it up in a function. Depending on what you're trying to do, you may also need a different HTTP handler, e.g. for multi file upload.

Nikianikita answered 14/6, 2011 at 19:11 Comment(2)
This is so horrible to read; it's so hard finding just the code.Dolora
I think it's useful to see the verbose way, perhaps for not the majority of cases, but sometimes when something special should be tuned.Firsthand
P
48

This may have been answered before: Python URLLib / URLLib2 POST.

Your server is likely performing a 302 redirect from http://myserver/post_service to http://myserver/post_service/. When the 302 redirect is performed, the request changes from POST to GET (see Issue 1401). Try changing url to http://myserver/post_service/.

Profant answered 14/6, 2011 at 19:13 Comment(1)
Yep! I was missing a trailing slash in the URL. I'm voting up and marking as acepted. Thanks!!Isometrics
F
11

Have a read of the urllib Missing Manual. Pulled from there is the following simple example of a POST request.

url = 'http://myserver/post_service'
data = urllib.urlencode({'name' : 'joe', 'age'  : '10'})
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
print response.read()

As suggested by @Michael Kent do consider requests, it's great.

EDIT: This said, I do not know why passing data to urlopen() does not result in a POST request; It should. I suspect your server is redirecting, or misbehaving.

Formality answered 14/6, 2011 at 19:11 Comment(4)
Thank you @Rob, I tried your suggestion but it didn't worked either. It turned out that I was missing a trailing slash in the URL.Isometrics
@Akim no problem. Good thinking @Profant :)Formality
You can also pass data to a GET request, there is no way for urllib2.urlopen to know what you want from this context, that is why.Acetate
Just tested this, it works great as is. Thanks, @RobCowie!Angulation
C
6

The requests module may ease your pain.

url = 'http://myserver/post_service'
data = dict(name='joe', age='10')

r = requests.post(url, data=data, allow_redirects=True)
print r.content
Caravansary answered 14/6, 2011 at 19:1 Comment(5)
OP asked "using urllib2" 🙄Confluence
Although it's works but OP said want to use urllib2 instead of requests, which is a 3rd party library. Using a 3rd party library may be helpful but also make the codes not portable, which may be the OP's original concern.Doubleton
Indeed, this is absolutely useless for my situation.Acetate
one word: PoratbilityCognomen
a very interesting choice for your one wordTourcoing
S
5

it should be sending a POST if you provide a data parameter (like you are doing):

from the docs: "the HTTP request will be a POST instead of a GET when the data parameter is provided"

so.. add some debug output to see what's up from the client side.

you can modify your code to this and try again:

import urllib
import urllib2

url = 'http://myserver/post_service'
opener = urllib2.build_opener(urllib2.HTTPHandler(debuglevel=1))
data = urllib.urlencode({'name' : 'joe',
                         'age'  : '10'})
content = opener.open(url, data=data).read()
Selima answered 14/6, 2011 at 19:17 Comment(0)
C
1

Try this instead:

url = 'http://myserver/post_service'
data = urllib.urlencode({'name' : 'joe',
                         'age'  : '10'})
req = urllib2.Request(url=url,data=data)
content = urllib2.urlopen(req).read()
print content
Confidence answered 14/6, 2011 at 19:12 Comment(0)
E
-2
url="https://myserver/post_service"
data["name"] = "joe"
data["age"] = "20"
data_encoded = urllib2.urlencode(data)
print urllib2.urlopen(url + "?" + data_encoded).read()

May be this can help

Exploit answered 16/4, 2015 at 4:25 Comment(1)
This is for a GET request, not a POSTBotryoidal

© 2022 - 2024 — McMap. All rights reserved.