119

I have a cURL call that I use in PHP:

curl -i -H 'Accept: application/xml' -u login:key "https://app.streamsend.com/emails"

I need a way to do the same thing in Python. Is there an alternative to cURL in Python? I know of urllib but I have no idea how to use it.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Gaurav Sharma
  • 4,032
  • 14
  • 46
  • 72
  • 3
    You can try [pycurl](http://pycurl.sourceforge.net) – ghostdog74 Apr 19 '10 at 12:58
  • 2
    urllib2 is a widely used package for this kind of work. – Saurav Nov 21 '11 at 23:36
  • 7
    Even better: http://docs.python-requests.org/en/latest/index.html – Saurav Nov 21 '11 at 23:42
  • 3
    The above is a link for a great library to do simple http `requests` in python (available to install via easy_install or pip in PyPi). The name/URL is slightly confusing -- at first I almost thought this was a wishlist request for a better `urllib2`, instead `requests` a very intuitive easy to use pythonic library `sudo easy_install requests` or `sudo pip install requests`. – dr jimbob Feb 29 '12 at 20:58
  • [Python Requests vs PyCurl Performance](https://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance) You decent on your requirement – Santhosh Apr 03 '18 at 12:26
  • Paste your command into https://curlconverter.com/python/ – Boris Verkhovskiy Sep 19 '22 at 09:00

7 Answers7

136

You can use the Requests library. Install it with

pip install requests

You can find its documentation at https://requests.readthedocs.io/en/latest/

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Gudbergur
  • 2,684
  • 2
  • 16
  • 5
  • 2
    Requests is the latest and greatest! It smokes and burns clumsy urllib2, i wish requests becomes standard HTTP Client for python incoming 3.x versions – Phyo Arkar Lwin Oct 15 '13 at 16:09
  • 1
    When I switched to using requests I never looked back to using urllib2 directly anymore. The built-in JSON decoding is handy as well. No need to manually load the body with json if the appropriate content-type is set. – Thomas Farvour Dec 17 '13 at 21:36
  • Requests is so simple. I was even able to create a custom authentication scheme in a few minutes. urllib2 is super-annoying. – Doug Nov 29 '16 at 14:56
  • 1
    For those in 2023 looking for a modern alternative to the aging `requests` (same human-friendly API, but not feature-frozen, and keeping up with modern developments like `async`, HTTP 2, and so on) I currently recommend `httpx`. – mtraceur Jun 26 '23 at 18:24
68
import urllib2

manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
manager.add_password(None, 'https://app.streamsend.com/emails', 'login', 'key')
handler = urllib2.HTTPBasicAuthHandler(manager)

director = urllib2.OpenerDirector()
director.add_handler(handler)

req = urllib2.Request('https://app.streamsend.com/emails', headers = {'Accept' : 'application/xml'})

result = director.open(req)
# result.read() will contain the data
# result.info() will contain the HTTP headers

# To get say the content-length header
length = result.info()['Content-Length']

Your cURL call using urllib2 instead. Completely untested.

blwy10
  • 4,862
  • 2
  • 24
  • 23
  • 4
    It's nice to compare this with the answer right below and see how far Python has progressed over the past four years – Razi Shaban May 22 '15 at 21:08
36

Here's a simple example using urllib2 that does a basic authentication against GitHub's API.

import urllib2

u='username'
p='userpass'
url='https://api.github.com/users/username'

# simple wrapper function to encode the username & pass
def encodeUserData(user, password):
    return "Basic " + (user + ":" + password).encode("base64").rstrip()

# create the request object and set some headers
req = urllib2.Request(url)
req.add_header('Accept', 'application/json')
req.add_header("Content-type", "application/x-www-form-urlencoded")
req.add_header('Authorization', encodeUserData(u, p))
# make the request and print the results
res = urllib2.urlopen(req)
print res.read()

Furthermore if you wrap this in a script and run it from a terminal you can pipe the response string to 'mjson.tool' to enable pretty printing.

>> basicAuth.py | python -mjson.tool

One last thing to note, urllib2 only supports GET & POST requests.
If you need to use other HTTP verbs like DELETE, PUT, etc you'll probably want to take a look at PYCURL

braitsch
  • 14,906
  • 5
  • 42
  • 37
20

If you are using a command to just call curl like that, you can do the same thing in Python with subprocess. Example:

subprocess.call(['curl', '-i', '-H', '"Accept: application/xml"', '-u', 'login:key', '"https://app.streamsend.com/emails"'])

Or you could try PycURL if you want to have it as a more structured api like what PHP has.

unholysampler
  • 17,141
  • 7
  • 47
  • 64
  • No. The cURL call is part of a program. If you could post the code that does the exact same thing being done in the curl call above, that would be great. – Gaurav Sharma Apr 19 '10 at 13:39
  • Added an example of what I meant by using subprocess based on your question, but I'm guessing you're looking for something more like PycURL. – unholysampler Apr 19 '10 at 13:58
  • I know this is older, but PycURL is pretty low level for most usages of cURL in my opinion. Even the PHP implementation of cURL is fairly low level. – Thomas Farvour Dec 17 '13 at 21:41
  • I get "name error, name subprocess not defined" after calling "python" from cmd and therefore being in the python env. – Timo Jun 07 '14 at 14:20
  • 1
    @Timo Did you `import subprocess`? The python repl environment is just like a python file, you have to import the other modules. – unholysampler Jun 07 '14 at 15:21
13
import requests

url = 'https://example.tld/'
auth = ('username', 'password')

r = requests.get(url, auth=auth)
print(r.content)

This is the simplest I've been able to get it.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
tadamhicks
  • 905
  • 1
  • 14
  • 34
7

Some example, how to use urllib for that things, with some sugar syntax. I know about requests and other libraries, but urllib is standard lib for python and doesn't require anything to be installed separately.

Python 2/3 compatible.

import sys
if sys.version_info.major == 3:
  from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, Request, build_opener
  from urllib.parse import urlencode
else:
  from urllib2 import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, Request, build_opener
  from urllib import urlencode


def curl(url, params=None, auth=None, req_type="GET", data=None, headers=None):
  post_req = ["POST", "PUT"]
  get_req = ["GET", "DELETE"]

  if params is not None:
    url += "?" + urlencode(params)

  if req_type not in post_req + get_req:
    raise IOError("Wrong request type \"%s\" passed" % req_type)

  _headers = {}
  handler_chain = []

  if auth is not None:
    manager = HTTPPasswordMgrWithDefaultRealm()
    manager.add_password(None, url, auth["user"], auth["pass"])
    handler_chain.append(HTTPBasicAuthHandler(manager))

  if req_type in post_req and data is not None:
    _headers["Content-Length"] = len(data)

  if headers is not None:
    _headers.update(headers)

  director = build_opener(*handler_chain)

  if req_type in post_req:
    if sys.version_info.major == 3:
      _data = bytes(data, encoding='utf8')
    else:
      _data = bytes(data)

    req = Request(url, headers=_headers, data=_data)
  else:
    req = Request(url, headers=_headers)

  req.get_method = lambda: req_type
  result = director.open(req)

  return {
    "httpcode": result.code,
    "headers": result.info(),
    "content": result.read()
  }


"""
Usage example:
"""

Post data:
  curl("http://127.0.0.1/", req_type="POST", data='cascac')

Pass arguments (http://127.0.0.1/?q=show):
  curl("http://127.0.0.1/", params={'q': 'show'}, req_type="POST", data='cascac')

HTTP Authorization:
  curl("http://127.0.0.1/secure_data.txt", auth={"user": "username", "pass": "password"})

Function is not complete and possibly is not ideal, but shows a basic representation and concept to use. Additional things could be added or changed by taste.

12/08 update

Here is a GitHub link to live updated source. Currently supporting:

  • authorization

  • CRUD compatible

  • automatic charset detection

  • automatic encoding(compression) detection

Reishin
  • 1,854
  • 18
  • 21
4

If it's running all of the above from the command line that you're looking for, then I'd recommend HTTPie. It is a fantastic cURL alternative and is super easy and convenient to use (and customize).

Here's is its (succinct and precise) description from GitHub;

HTTPie (pronounced aych-tee-tee-pie) is a command line HTTP client. Its goal is to make CLI interaction with web services as human-friendly as possible.

It provides a simple http command that allows for sending arbitrary HTTP requests using a simple and natural syntax, and displays colorized output. HTTPie can be used for testing, debugging, and generally interacting with HTTP servers.


The documentation around authentication should give you enough pointers to solve your problem(s). Of course, all of the answers above are accurate as well, and provide different ways of accomplishing the same task.


Just so you do NOT have to move away from Stack Overflow, here's what it offers in a nutshell.

Basic auth:

$ http -a username:password example.org
Digest auth:

$ http --auth-type=digest -a username:password example.org
With password prompt:

$ http -a username example.org
stuxnetting
  • 648
  • 9
  • 17
  • maybe I didn't get it at all, but is it a Python module ? I guess it a shell/CLI tool, and I'm disapointed :'( it seemed to be so easy to use – Alex Jun 02 '16 at 14:53
  • @Alex - It is a Python module. The README on Github (https://github.com/jkbrzt/httpie) contains everything you need. – stuxnetting Jun 23 '16 at 00:18
  • 1
    HTTPie is for making HTTP requests from Bash, not from Python. It might be implemented in Python, but the library it uses to actually make the requests [is Requests](https://httpie.io/docs/cli/community-and-support). – Boris Verkhovskiy Sep 19 '22 at 08:59