This is how you do it.
from urllib import request, parse
data = parse.urlencode(<your data dict>).encode()
req = request.Request(<your url>, data=data) # this will make the method "POST"
resp = request.urlopen(req)
Answer from C Panda on Stack OverflowThis is how you do it.
from urllib import request, parse
data = parse.urlencode(<your data dict>).encode()
req = request.Request(<your url>, data=data) # this will make the method "POST"
resp = request.urlopen(req)
Thank you C Panda. You really made it easy for me to learn this module.
I released the dictionary that we pass does not encode for me. I had to do a minor change -
from urllib import request, parse
import json
# Data dict
data = { 'test1': 10, 'test2': 20 }
# Dict to Json
# Difference is { "test":10, "test2":20 }
data = json.dumps(data)
# Convert to String
data = str(data)
# Convert string to byte
data = data.encode('utf-8')
# Post Method is invoked if data != None
req = request.Request(<your url>, data=data)
# Response
resp = request.urlopen(req)
Videos
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/.
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.
HTTP POST works as expected:
#!/usr/bin/env python
from contextlib import closing
try:
from urllib.parse import urlencode
from urllib.request import urlopen
except ImportError: # Python 2
from urllib import urlencode
from urllib2 import urlopen
url = 'http://httpbin.org/post'
data = urlencode({"field1" : "value", "Submit": "Save"}).encode()
with closing(urlopen(url, data)) as response:
print(response.read().decode())
You may see GET only after an http redirect (as the rfc recommends -- no data should be posted on redirect without prompting the user).
For example, here's an http server that redirects POST / requests:
#!/usr/bin/env python
from flask import Flask, redirect, request, url_for # $ pip install flask
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
return redirect(url_for('post'))
return '<form method="POST"><input type="submit">'
@app.route('/post', methods=['GET', 'POST'])
def post():
return 'Hello redirected %s!' % request.method
if __name__ == '__main__':
import sys
port = int(sys.argv[1]) if len(sys.argv) > 1 else None
app.run(host='localhost', port=port)
Making an HTTP POST request using the same code (urlopen(url, data)) leads to the redirection and the second request is GET:
"POST / HTTP/1.1" 302 -
"GET /post HTTP/1.1" 200 -
Again, the first request is POST, not GET. The behavior is exactly the same if you visit / and click submit button (the browser makes POST request and then GET request).
Python issue: "Document how to forward POST data on redirects" contains a link to HTTPRedirectHandler's subclass that posts data on redirect:
#!/usr/bin/env python
from contextlib import closing
try:
from urllib.parse import urlencode
from urllib.request import (HTTPError, HTTPRedirectHandler, Request,
build_opener, urlopen)
except ImportError: # Python 2
from urllib import urlencode
from urllib2 import (HTTPError, HTTPRedirectHandler, Request,
build_opener, urlopen)
class PostHTTPRedirectHandler(HTTPRedirectHandler):
"""Post data on redirect unlike urrlib2.HTTPRedirectHandler."""
def redirect_request(self, req, fp, code, msg, headers, newurl):
m = req.get_method()
if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
or code in (301, 302, 303) and m == "POST"):
newurl = newurl.replace(' ', '%20')
CONTENT_HEADERS = ("content-length", "content-type")
newheaders = dict((k, v) for k, v in req.headers.items()
if k.lower() not in CONTENT_HEADERS)
return Request(newurl,
data=req.data,
headers=newheaders,
origin_req_host=req.origin_req_host,
unverifiable=True)
else:
raise HTTPError(req.get_full_url(), code, msg, headers, fp)
urlopen = build_opener(PostHTTPRedirectHandler).open
url = 'http://localhost:5000'
data = urlencode({"field1" : "value", "Submit": "Save"}).encode()
with closing(urlopen(url, data)) as response:
print(response.read().decode())
The access log shows two POST requests in this case (the second request is POST):
"POST / HTTP/1.1" 302 -
"POST /post HTTP/1.1" 200 -
Note: you could customize the HTTPRedirectHandler to follow the rfc 2616 behavior.
OK so i figured out what was wrong. The python module "requests.post" will not perform a post if the url is one that redirects. So I had to put the actual url in for it to work and not a url that would direct me to my desired url.
THis is the same for those using urllib
This would be the equivalent request using the python requests library.
url = "https://api.infermedica.com/dev/parse"
headers = {
'App_Id': '4c177c',
'App_Key': '6852599182ba85d70066986ca2b3',
'Content-Type': 'application/json',
}
data = {'text': 'i feel stomach pain but no coughing today'}
r = requests.post(url, headers=headers, data=json.dumps(data))
print r.status_code
print r.json()
But your real problem is that you're using the wrong header keys for their api. It's App-Id and App-key, not App_Id and App_key. It would look like this:
headers = {
'App-Id': 'xx',
'App-key': 'xxxx',
'Accept': 'application/json',
'Content-Type': 'application/json',
'Dev-Mode': 'true'}
data = {'text': 'i feel stomach pain but no coughing today'}
r = requests.post(url, headers=headers, data=json.dumps(data))
Also worth noting, they have a python api that does all this for you.
json_data = json.dumps(data) is not the correct way to prepare POST data.
You should use urllib.urlencode() to do the job:
import urllib
data = { "text": text }
req = urllib2.Request(self.url, urllib.urlencode(data), self.headers)
response = urllib2.urlopen(req).read()
Docs:
class urllib2.Request(url[, data][, headers][, origin_req_host][, unverifiable]) This class is an abstraction of a URL request.
data may be a string specifying additional data to send to the server, or None if no such data is needed. Currently HTTP requests are the only ones that use data; the HTTP request will be a POST instead of a GET when the data parameter is provided. data should be a buffer in the standard application/x-www-form-urlencoded format. The urllib.urlencode() function takes a mapping or sequence of 2-tuples and returns a string in this format.
details = urllib.parse.urlencode({'IDToken1': 'USERNAME', 'IDToken2': 'PASSWORD'})
Add the following line:
details = details.encode('UTF-8')
That could be by design. What happens if you do it in a browser? The fact that it works with correct data means that you're doing it right.