They are both correct and will work the same.
The best way to clear up this confusion is to look at the requests source code.
Here is the code for request.get (as of 2.25.1):
def get(url, params=None, **kwargs):
r"""Sends a GET request.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary, list of tuples or bytes to send
in the query string for the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', True)
return request('get', url, params=params, **kwargs)
...which shows that requests.get just calls requests.request with a hardcoded 'get' for the 1st argument. All the other parameters (url, params, **kwargs) are all just passed through.
Basically, it is just a convenience method or a shorthand or a shortcut so you don't have to manually remember which string to pass for the method parameter. It's easier especially when using an IDE because your IDE's IntelliSense can help you select .get or .post or .delete, etc. but not the raw strings "GET", "POST", or "DELETE", etc.
The requests docs can also offer some clarity.
requests.request(method, url, **kwargs)It says "Constructs and sends a Request.". So this one is for ANY type of request, and you need to pass in 2 required arguments: the
methodand the URL. All the possiblekwargsare listed in the doc, including theparamsfrom your example.requests.get(url, params=None, **kwargs)It says "Sends a GET request.". So this one is more specific in that it is only for a GET request. It only has 1 required argument, the URL. No need to pass
"GET". And thenkwargsis "Optional arguments thatrequesttakes." which just points back to the mainrequests.requestmethod.
I would say it's a matter of opinion and coding style which one to use. A use-case where it makes sense to prefer requests.request is when wrapping different API calls and providing a Python interface for them.
For example, I have these APIs:
- GET /api/v1/user/[user-id]
- PATCH /api/v1/user/[user-id]
- DELETE /api/v1/user/[user-id]
When implementing get_user and update_user and delete_user, I could just call the .get, .post, .delete, etc. methods. But if calling these APIs required passing in many and/or complicated kwargs (headers, auth, timeout, etc.), then that would lead to a lot of duplicated code.
Instead, you can do it all in one method then use requests.request:
def get_user(user_id):
return call_api("GET", user_id)
def update_user(user_id, user):
return call_api("PATCH", user_id, user=user)
def delete_user(user_id):
return call_api("DELETE", user_id)
def call_api(method, user_id, user=None):
# Lots of preparation code to make a request
headers = {
"header1": "value1",
"header2": "value2",
# There can be lots of headers...
"headerN": "valueN",
}
timeout = (1, 30)
payload = user.json() if user else {}
url = f"/api/v1/user/{user_id}"
return requests.request(
method,
url,
headers=headers,
timeout=timeout,
json=payload,
)
There are probably other ways to refactor the code above to reduce duplication. That's just an example, where if you called .get, .patch, .delete directly, then you might end up repeating listing all those headers every time, setting up the URL, doing validations, etc.
Videos
If the response is in json you could do something like (python3):
Copyimport json
import requests as reqs
# Make the HTTP request.
response = reqs.get('https://demo.ckan.org/api/3/action/group_list')
# Use the json module to load CKAN's response into a dictionary.
response_dict = json.loads(response.text)
for i in response_dict:
print("key: ", i, "val: ", response_dict[i])
To see everything in the response you can use .__dict__:
Copyprint(response.__dict__)
Edit in May 2024 to add a suggestion on how to address if objects in the response dict is not JSON serializable.
Copyimport json
...
print(json.dumps(response.text, indent=4, sort_keys=True, default=lambda o:'<not serializable>'))
If you push, for example image, to some API and want the result address(response) back you could do:
Copyimport requests
url = 'https://uguu.se/api.php?d=upload-tool'
data = {"name": filename}
files = {'file': open(full_file_path, 'rb')}
response = requests.post(url, data=data, files=files)
current_url = response.text
print(response.text)
It looks to me from the source code for the ProxyError exception object in Requests like that object should have the request in it. I know you said you think it doesn't, but this shows that it is at least putting a response field in there, even if it were to end up null.
I would have maybe put this in comments, but you can't format code there. This sure seems though like it would give you what you want:
class RequestException(IOError):
"""There was an ambiguous exception that occurred while handling your
request.
"""
def __init__(self, *args, **kwargs):
"""Initialize RequestException with `request` and `response` objects."""
response = kwargs.pop('response', None)
self.response = response
self.request = kwargs.pop('request', None)
if (response is not None and not self.request and
hasattr(response, 'request')):
self.request = self.response.request
super(RequestException, self).__init__(*args, **kwargs)
class ConnectionError(RequestException):
"""A Connection error occurred."""
class ProxyError(ConnectionError):
"""A proxy error occurred."""
So seeing this code, it seems like something like this would work:
try:
...
session.get(url=url, headers=req_headers, verify=False, allow_redirects=True, timeout=30)
...
except ProxyError as ex:
the_response = ex.response
.. do something with the response ..
I think you may be able to use Request's history functionality to access that object, and Request's standard APIs to go from there:
https://2.python-requests.org//en/latest/user/quickstart/#redirection-and-history
By default Requests will perform location redirection for all verbs except HEAD.
We can use the history property of the Response object to track redirection.
The Response.history list contains the Response objects that were created in order to complete the request. The list is sorted from the oldest to the most recent response.
r = requests.get('http://github.com/')
r.url 'https://github.com/'
r.status_code 200
r.history [<Response [301]>]
Will headers then not give you what you need?
https://2.python-requests.org//en/latest/user/advanced/#advanced
>>> r.headers
{'content-length': '56170', 'x-content-type-options': 'nosniff', 'x-cache':
'HIT from cp1006.eqiad.wmnet, MISS from cp1010.eqiad.wmnet', 'content-encoding':
'gzip', 'age': '3080', 'content-language': 'en', 'vary': 'Accept-Encoding,Cookie',
'server': 'Apache', 'last-modified': 'Wed, 13 Jun 2012 01:33:50 GMT',
'connection': 'close', 'cache-control': 'private, s-maxage=0, max-age=0,
must-revalidate', 'date': 'Thu, 14 Jun 2012 12:59:39 GMT', 'content-type':
'text/html; charset=UTF-8', 'x-cache-lookup': 'HIT from cp1006.eqiad.wmnet:3128,
MISS from cp1010.eqiad.wmnet:80'}