python requests get request debugging - python

/api/stats
?fields=["clkCnt","impCnt"]
&ids=nkw0001,nkw0002,nkw0003,nkw0004
&timeRange={"since":"2019-05-25","until":"2019-06-17"}
I'm currently working on a API called naver_searchad_api
link to github of the api If you want to check it out. but i don't think you need to
the final url should be a baseurl + /api/stats
and on fields and ids and timeRange, the url should be like that
the requests I wrote is like below
r = requests.get(BASE_URL + uri, params={'ids': ['nkw0001','nkw0002','nkw0003','nkw0004'], 'timeRange': {"since": "2019-05-25", "until": "2019-06-17"}}, headers=get_header(method,uri,API_KEY,SECRET_KEY,CUSTOMER_ID))
final_result = r.json()
print(final_result)
as I did below instead
print(r.url)
it returns as below
https://api.naver.com/stats?ids=nkw0001&ids=nkw0002&ids=nkw0002&ids=nkw0002&fields=clkCnt&fields=impCnt&timeRange=since&timeRange=until
the 'ids' is repeated and doesn't have dates that I put.
how would I make my code to fit with the right url?

Query strings are key-value pairs. All keys and all values are strings. Anything that is not trivially convertible to string depends on convention. In other words, there is no standard for these things, so it depends on the expectations of the API.
For example, the API could define that lists of values are to be given as comma-separated strings, or it could say that anything complex should be JSON-encoded.
In fact, that's exactly what the API documentation says:
fields string
Fields to be retrieved (JSON format string).
For example, ["impCnt","clkCnt","salesAmt","crto"]
The same goes for timeRange. The other values can be left alone. Therefore we JSON-encode those two values only.
We can do that inline with a dict comprehension.
import json
import requests
params = {
'fields': ["clkCnt", "impCnt"],
'ids': 'nkw0001,nkw0002,nkw0003,nkw0004',
'timeRange': {"since":"2019-05-25","until":"2019-06-17"},
}
resp = requests.get('https://api.naver.com/api/stats', {
key: json.dumps(value) if key in ['fields', 'timeRange'] else value for key, value in params.items()
})
On top of complying with the API's expectations, all keys and values that go into the query string need to be URL-encoded. Luckily the requests module takes care of that part, so all we need to do is pass a dict to requests.get.

Related

How can I prepend each value of a list with either a loop or a function?

I'm currently making API calls to an application and then parsing the JSON data for certain values. The values I'm parsing are suffixes of a full URL. I need to take this list of suffixes and prepend them with my variable which completes each URL. This list can vary in size and so I will not always know the exact list length nor the exact values of the suffixes.
The list (worker_uri_list) will consist of the following format: ['/api/admin/configuration/v1/vm/***/', '/api/admin/configuration/v1/vm/***/'], '/api/admin/configuration/v1/vm/***/']. The asterisk are characters that can vary at any given time when the API call is made. I need to then prepend each one of these suffixes with my variable (url_prefix).
I'm having a hard time trying to understand the best method and how to do this correctly. Very new to Python here so I hope I've explained this well enough. It seems a simple for or while loop could accomplish this, but a lot of all the loop examples I find are for simple integer values.
Any help is appreciated.
url_prefix = "https://website.com"
url1 = url_prefix + "/api/admin/configuration/v1/config"
payload={}
header = {
'Authorization': 'Basic ********'
}
#Parse response
response = requests.get(url1, headers=header).json()
worker_uri_list = nested_lookup("resource_uri", response)
loop?
function?
You could iterate the worker_uri_list in a loop:
for uri in worker_uri_list:
url = url_prefix + uri
# do something with the url
print(url)
Output:
https://website.com/api/admin/configuration/v1/vm/***/
https://website.com/api/admin/configuration/v1/vm/***/
https://website.com/api/admin/configuration/v1/vm/***/
Or if you want a list of URLs, use a list comprehension:
urls = [url_prefix + uri for uri in worker_uri_list]
print(urls)
Output:
[
'https://website.com/api/admin/configuration/v1/vm/***/',
'https://website.com/api/admin/configuration/v1/vm/***/',
'https://website.com/api/admin/configuration/v1/vm/***/'
]

python urllib: build urls including parameters with and without keyword

I am building grafana links in python with urllib like the following:
from urllib.parse import urlencode, urlunsplit
parameters = {
"parameter1":"value1",
"parameter2":"value2"
}
query = urlencode(
query = parameters,
doseq = True
)
link = urlunsplit((
"https",
"my_grafana.com",
"/graph",
query,
""
))
link will be in this case 'https://my_grafana.com/graph?parameter1=value1&parameter2=value2'. I now want to add parameters with no keyword for example "kiosk". The link should look like 'https://my_grafana.com/graph?parameter1=value1&parameter2=value2&kiosk&other_parameter'
As urlencode returns a string with the parameters I could manipulate the string like in the following example before I give it to urlunsplit:
no_keyword_parameters = ["kiosk","other_parameter"]
query = "&".join([query, *no_keyword_parameters])
I wonder if you can put parameters with and without keyword directly with urlencode together. I tried giving "kiosk" as a dictionary entry with None as content ({"kiosk": None}) but it includes the None in the url. Approaches, where I give a list of tuples instead of a dictionary for the parameters, were also unsuccessful.
Thank you for any help.
As mentioned by Ondrej, urlencode builds the query using k + '=' + v.
You could add non value parameters manually:
from urllib.parse import urlencode, urlunsplit, quote_plus
parameters = {"parameter1": "value1", "parameter2": "value2"}
no_value_parameters = ["kiosk", "other_parameter"]
no_value_parameters_quoted = [quote_plus(p) for p in no_value_parameters]
query = urlencode(query=parameters, doseq=True)
link = urlunsplit(("https", "my_grafana.com", "/graph", query, ""))
link = f"{link}&{'&'.join(no_value_parameters_quoted)}"
print(link)
Out:
https://my_grafana.com/graph?parameter1=value1&parameter2=value2&kiosk&other_parameter
What you've done seems sound and you could either do it like that or formalize it a bit more in your own encoding function, but urllib.parse.urlencode does not seem to understand the notion of parameters without value. If you look at the implementation (with doseq you get a variation of the same for the part relevant to your question):
for k, v in query:
...
l.append(k + '=' + v)
I.e. you have to have a key, value pair (to unpack two values) and whatever they are quoted to (that happens in the ellipses) will be a str joined over =. So even using custom qoute_via you cannot really change its function.
That linked implementation is the one provided with CPython, but also the documentation expects: key/value pairs, so that behavior really is as specified / documented:
The resulting string is a series of key=value pairs separated by '&' characters...

Python request containing parameters that have values and parameters that do not have values

I am writing a Python wrapper for an API that supports the query parameters that have values (e.g. param1 below) and query parameters that do not have values value (e.g. param2 below) i.e.
https://example.com/service?param1=value1&param2
The HTTP requests library famously supports parameters with values and parameters without values. However I need to be able to specify both types of parameters.
You have two options with Requests in terms of the query string: 1) provide key/value pairs by way of a dictionary, or 2) provide a string. If you provide #1, you'll always get a '=' for each key/value pair...not what you want. So you have to use #2, which will let you do whatever you want, since it will just include what you give it as the entire query string. The downside is that you have to construct the query string yourself. There are many ways of doing that, of course. Here's one way:
params = {'param1': 'value1', 'param2': None}
params = '&'.join([k if v is None else f"{k}={v}" for k, v in params.items()])
r = requests.get('https://example.com/service', params=params)
print(r.url)
This way lets you supply a dictionary, just like if you were letting Requests build the query string, but it allows you to specify a value of Null to indicate that you want just the key name, with no '='. Requests will normally not include the parameter at all if its value is None in the dictionary.
The result of this code is exactly what you gave as an example of what you want:
https://example.com/service?param1=value1&param2

Parsing JSON object in python

I am trying to make some (JSON) API calls to our Wi-Fi controller and obtain some info. When I store the JSON response into a dict somehow it only see's a few keys, namely:
dict_keys(['totalCount', 'hasMore', 'firstIndex', 'list'])
and items:
dict_items([('totalCount', 32), ('hasMore', False), ('firstIndex', 0),
('list', [{'id': 'ehgfhafgf', 'name': 'fasdfsd
xxxx'}, {'id': 'efasfsfas',
'name': 'zxcva'}])])
I removed a lot of items so It would make some sense otherwise it would be too much text.
So as you can see the dict recognizes the wrong variables as keys. Because as keys I need id and name. Is there a way to manually assign dict keys or a trick to simulate this?
My piece of code:
#Method that retrieves all zones
def getZones():
r = requests.get("url..", verify=False, cookies=cookie)
print(type(r.json()))
jsonResponse = r.json()
print("items: \n")
print(jsonResponse.items())
print("\nkeys: \n")
print(jsonResponse.keys())
print(jsonResponse.get('id'))
return r
doing a lot of prints for debugging reasons.
Your question would have been clearer if you had shown the actual JSON response.
However, it is clear from what you have posted that id and name are indeed not top-level keys, but keys inside nested dictionaries inside a list assigned to the list key. So you should get them from there:
for item in jsonResponse['list']:
print(item['id'], item['name'])

json change dictionary item to a list with one dictionary

I'm working with a Rest Api for finding address details. I pass it an address and it passes back details for that address: lat/long, suburb etc. I'm using the requests library with the json() method on the response and adding the json response to a list to analyse later.
What I'm finding is that when there is a single match for an address the 'FoundAddress' key in the json response contains a dictionary but when more than one match is found the 'FoundAddress' key contains a list of dictionaries.
The returned json looks something like:
For a single match:
{
'FoundAddress': {AddressDetails...}
}
For multiple matches:
{
'FoundAddress': [{Address1Details...}, {Address2Details...}]
}
I don't want to write code to handle a single match and then multiple matches.
How can I modify the 'FoundAddress' so that when there is a single match it changes it to a list with a single dictionary entry? Such that I get something like this:
{
'FoundAddress': [{AddressDetails...}]
}
If it's the external API sending responses in that format then you can't really change FoundAddress itself, since it will always arrive in that format.
You can change the response if you want to, since you have full control over what you've received:
r = json.parse(response)
fixed = r['FoundAddress'] if (type(r['FoundAddress']) is list) else [r['FoundAddress']]
r['FoundAddress'] = fixed
Alternatively you can do the distinction at address usage time:
def func(foundAddress):
# work with a single dictionary instance here
then:
result = map(func, r['FoundAddress']) if (type(r['FoundAddress']) is list) else [func(r['FoundAddress'])]
But honestly I'd take a clear:
if type(r['FoundAddress']) is list:
result = map(func, r['FoundAddress'])
else:
result = func(r['FoundAddress'])
or the response fix-up over the a if b else c one-liner any day.
If you can, I would just change the API. If you can't there's nothing magical you can do. You just have to handle the special case. You could probably do this in one place in your code with a function like:
def handle_found_addresses(found_addresses):
if not isinstance(found_addresses, list):
found_addresses = [found_addreses]
...
and then proceed from there to do whatever you do with found addresses as if the value is always a list with one or more items.

Categories

Resources