I am working on a discord bot that gets information from an API. For this command I want to return some information, and display it nicely for the user. This is the code (Python 3.8.3, discord.py):
import json
import requests
import discord
#bot.command(name="clan")
async def clan_command(ctx, clan_id:int):
response = requests.get(f'https://api.worldoftanks.eu/wot/clans/info/?application_id=0a833f3e275be2c9b458c61d6cedf644&clan_id={clan_id}&fields=leader_name%2C+members_count%2C+tag%2C+motto%2C+name%2C+emblems.x256%2C+color', params={'q': 'requests+language:python'})
json_response = response.json()
repository = json_response["data"]
clan_name =
clan_colour =
clan_url = f"https://eu.wargaming.net/clans/wot/{clan_id}/"
clan_logo =
clan_commander =
clan_member_count =
clan_tag =
clan_motto =
embed = discord.Embed(title=clan_name (clan_tag), colour=clan_colour, url=clan_url)
embed.set_thumbnail(url=clan_logo)
embed.add_field(name="Commander:", value=clan_commander, inline=True)
embed.add_field(name="Member count:", value=clan_member_count, inline=True)
embed.add_field(name="Clan motto:", value=clan_motto, inline=True)
await ctx.channel.send(embed=embed)
Screenshot of the API response with input if I write it to a file *clan 500075680: https://imgur.com/a/BzkctRP
When I run this code:
for key, value in repository.items():
print(f"key {key} has value {value}")
I get this in console:
key 500075680 has value {'members_count': 81, 'name': 'Reloading', 'color': '#B80909', 'leader_name': 'Joc666', 'emblems': {'x256': {'wowp': 'https://eu.wargaming.net/clans/media/clans/emblems/cl_680/500075680/emblem_256x256.png'}}, 'tag': '-RLD-', 'motto': "In the end, we only regret the chances we didn't take."}
So the key clan_id (which is a input argument that changes) has multiple values.
My question is, if I take the clan_id = 500075680 for example, how do I display from value 'name': 'Reloading', Reloading individually?
How can I define the variables in my code?
Fixed it thanks to you guys:
I had the clan_id argument specified as an integer, removing that makes it work:
clan_name = repository[clan_id]['name']
Assuming value is dictionary
to acess the value of name from value dictionary use value['name']
for key, value in repository.items():
print(f"key {key} has value {value['name']}")
from the image you shared, you can get the value of name by using this repository['data'][key]['name']
if there are list of responses
example:
repository=[{key1:{'name':name1}},{key2:{'name':name2}}
for item in repository:
key=next(iter(item))
print(item[key]['name'])
repository is a list holding all responses
Try using json.loads().
For example to get the value of name you could parse your json into a python dict and retrieve it from there.
for key, value in repository.items():
value_dict = json.loads(value)
print(value_dict["name"])
Here is another nice way to do it.
json_object = json.loads(json_response)
formatted_data = json.dumps(json_object, indent=2)
print(formatted_data)
json.dumps method with indent will formate it and you can controle the formatting with indent value.
It is equivalent to PHP array formatting after converting JSON to an array.
echo "<pre>";
print_r()
Related
I have this one problem, where I print out a message response from a website(JSON response), and the response I get is this.
Here is my model with fake data:
{"token": "MTAxOTAwNjM4NjEyMzg0OTkwMQ.8hkyLV.n0ir2UA4qFE5pXen9YnPtFzgn4xP8tHmVmmkrl", "user_settings": {"locale": "en-US", "theme": "dark"}, "user_id": "101900638614857883"}
And, if I only want the value of "token" data which are this (MTAxOTAwNjM4NjEyMzg0OTkwMQ.8hkyLV.n0ir2UA4qFE5pXen9YnPtFzgn4xP8tHmVmmkrl) and I want to store it into a txt file, is there any good way to do it?
Thank you, guys!
I tried print(r.text('token')) but it did not work, since it only works on printing the category of the data's (like : Category : {"token" : 'daefafa', "user-id" : 'er121231231', more})
In python, JSON is treated as a dictionary.
To filter it use dictionary comprehension
tokenData = {key: val for key,val in data_json.items() if key == 'token'}
Full Code Snippet :
from urllib.request import urlopen
import json
url = "enter-your-url"
response = urlopen(url)
data_json = json.loads(response.read())
print(type(data_json)) # <class 'dict'>
#use dict comprehension
jsonToken = {key: val for key,val in data_json.items() if key == 'result'}
strToken = json.dumps(jsonToken)
# Only string json can be written to files
with open('data.txt','w') as file:
file.write(strToken)
file.close()
You need to parse the JSON into a dictionary using json.loads(). Like this:
import json
# ...
# request-getting code
# ...
data = json.loads(r.text)
print(data['token'])
Hi so im having a problem where when I try to use this command its bringing the USERID instead of what im trying to get heres the error
File "goonie.py", line 421, in spam
spammers = users[str(ctx.author.id)]['text_max']
KeyError: '932428536176848938'
The numbers are the "ctx.author.id" and im trying to get the "text_max"
async def spam(ctx, message,amount):
users = await get_paid_data()
spammers = users[str(ctx.author.id)]['text_max']
if amount > spammers:
if str(ctx.author.id) in users:
if users[str(ctx.author.id)]['vip'] == 'Y':
timeleft = datetime.strptime(users[str(ctx.author.id)]['expDate'],'%Y-%m-%d') - datetime.now()
if timeleft.days <= 0:
em = discord.Embed(color=0x000000, description="Sorry your plan on this tool has expired")
await ctx.send(embed=em) ```
users is a dictionary.
You first access the str(ctx.author.id) key in the dictionary.
The error is telling you the dictionary does not contain a key corresponding to the value of string value of ctx.author.id. Therefore, you cannot access text_max because the first part of your dict navigation is incorrect.
You should print users and see what it contains:
print(users)
My initial guess is that you're converting the id to a string when it in-fact doesn't need to be converted, but it's impossible to say without seeing the contents of the dictionary.
I'm fairly new to Python so bear with me please.
I have a function that takes two parameters, an api response and an output object, i need to assign some values from the api response to the output object:
def map_data(output, response):
try:
output['car']['name'] = response['name']
output['car']['color'] = response['color']
output['car']['date'] = response['date']
#other mapping
.
.
.
.
#other mapping
except KeyError as e:
logging.error("Key Missing in api Response: %s", str(e))
pass
return output
Now sometimes, the api response is missing some keys i'm using to generate my output object, so i used the KeyError exception to handle this case.
Now my question is, in a case where the 'color' key is missing from the api response, how can i catch the exception and continue to the line after it output['car']['date'] = response['date'] and the rest of the instructions.
i tried the pass instruction but it didn't have any affect.
Ps: i know i can check the existence of the key using:
if response.get('color') is not None:
output['car']['color'] = response['color']
and then assign the values but seeing that i have about 30 values i need to map, is there any other way i can implement ? Thank you
A few immediate ideas
(FYI - I'm not going to explain everything in detail - you can check out the python docs for more info, examples etc - that will help you learn more, rather than trying to explain everything here)
Google 'python handling dict missing keys' for a million methods/ideas/approaches - it's a common use case!
Convert your response dict to a defaultdict. In that case you can have a default value returned (eg None, '', 'N/A' ...whatever you like) if there is no actual value returned.
In this case you could do away with the try and every line would be executed.
from collections import defaultdict
resp=defaultdict(lambda: 'NA', response)
output['car']['date'] = response['date'] # will have value 'NA' if 'date' isnt in response
Use the in syntax, perhaps in combination with a ternary else
output['car']['color'] = response['color'] if 'color' in response
output['car']['date'] = response['date'] if 'date' in response else 'NA'
Again you can do away with the try block and every line will execute.
Use the dictionary get function, which allows you to specify a default if there is no value for that key:
output['car']['color'] = response.get('car', 'no car specified')
You can create a utility function that gets the value from the response and if the value is not found, it returns an empty string. See example below:
def get_value_from_response_or_null(response, key):
try:
value = response[key]
return value
except KeyError as e:
logging.error("Key Missing in api Response: %s", str(e))
return ""
def map_data(output, response):
output['car']['name'] = get_value_from_response_or_null(response, 'name')
output['car']['color'] = get_value_from_response_or_null(response, 'color')
output['car']['date'] = get_value_from_response_or_null(response, 'date')
# other mapping
# other mapping
return output
Here's the code I'm trying to run. Works when executed from PyCharm. I set up a cronjob and it worked wonders for weeks. It's now giving a KeyError out of the bloom. Can't find what's wrong since I haven't touched anything in the cronjob.
import csv
import json
import os
import random
import time
from urllib.parse import urlencode
import requests
API_URL = "https://community.koodomobile.com/widget/pointsLeaderboard?"
LEADERBOARD_FILE = "leaderboard_data.json"
def get_leaderboard(period: str = "allTime", max_results: int = 20) -> list:
payload = {"period": period, "maxResults": max_results}
return requests.get(f"{API_URL}{urlencode(payload)}").json()
def dump_leaderboard_data(leaderboard_data: dict) -> None:
with open("leaderboard_data.json", "w") as jf:
json.dump(leaderboard_data, jf, indent=4, sort_keys=True)
def read_leaderboard_data(data_file: str) -> dict:
with open(data_file) as f:
return json.load(f)
def parse_leaderboard(leaderboard: list) -> dict:
return {
item["name"]: {
"id": item["id"],
"score_data": {
time.strftime("%Y-%m-%d %H:%M"): item["points"],
},
"rank": item["leaderboardPosition"],
} for item in leaderboard
}
def update_leaderboard_data(target: dict, new_data: dict) -> dict:
for player, stats in new_data.items():
target[player]["rank"] = stats["rank"]
target[player]["score_data"].update(stats["score_data"])
return target
def leaderboard_to_csv(leaderboard: dict) -> None:
data_rows = [
[player] + list(stats["score_data"].values())
for player, stats in leaderboard.items()
]
random_player = random.choice(list(leaderboard.keys()))
dates = list(leaderboard[random_player]["score_data"])
with open("the_data.csv", "w") as output:
w = csv.writer(output)
w.writerow([""] + dates)
w.writerows(data_rows)
def script_runner():
if os.path.isfile(LEADERBOARD_FILE):
fresh_data = update_leaderboard_data(
target=read_leaderboard_data(LEADERBOARD_FILE),
new_data=parse_leaderboard(get_leaderboard()),
)
leaderboard_to_csv(fresh_data)
dump_leaderboard_data(fresh_data)
else:
dump_leaderboard_data(parse_leaderboard(get_leaderboard()))
if __name__ == "__main__":
script_runner()
Here's the error taht it gives me. Seems like there's a problem with dictionary hence the KeyError.
File "/Users/Rob/PycharmProjects/Koodo/TEST.Json.py", line 75, in <module>
script_runner()
File "/Users/Rob/PycharmProjects/Koodo/TEST.Json.py", line 64, in script_runner
fresh_data = update_leaderboard_data(
File "/Users/Rob/PycharmProjects/Koodo/TEST.Json.py", line 44, in update_leaderboard_data
target[player]["rank"] = stats["rank"]
KeyError: 'triggered123'
Here's the data in JSON file : https://pastebin.com/HQyL4Kyx
It looks to me like the first time your code is run, it caches the leaderboard.
On subsequent runs, it will update the existing leaderboard with the new values, by looping over each of the new entries, finding their username, and looking up that key in the older dictionary. However, if there's a new user, that key will not exist in the old dictionary.
You're getting the error because the player triggered123 is a new user to the leaderboard, and was listed after you first ran the script.
You need to update update_leaderboard_data to handle this case (by checking if the key exists in the dictionary before attempting to access it).
That's because the key named triggered123 is NOT present in the dictionary target.
Source: https://wiki.python.org/moin/KeyError
The problem is that in update_leaderboard_data you iterate over the new data and use them to lookup in the old data. If a new key does not already exists in the old data, you'll get the KeyError.
Try to print the dict target to see if the key triggered123 is in it.
Or use the get method of the dict with a default value: so no error wil be raised, and you'll be able to search this default value in your output.
I am getting JIRA data using the following python code,
how do I store the response for more than one key (my example shows only one KEY but in general I get lot of data) and print only the values corresponding to total,key, customfield_12830, summary
import requests
import json
import logging
import datetime
import base64
import urllib
serverURL = 'https://jira-stability-tools.company.com/jira'
user = 'username'
password = 'password'
query = 'project = PROJECTNAME AND "Build Info" ~ BUILDNAME AND assignee=ASSIGNEENAME'
jql = '/rest/api/2/search?jql=%s' % urllib.quote(query)
response = requests.get(serverURL + jql,verify=False,auth=(user, password))
print response.json()
response.json() OUTPUT:-
http://pastebin.com/h8R4QMgB
From the the link you pasted to pastebin and from the json that I saw, its a you issues as list containing key, fields(which holds custom fields), self, id, expand.
You can simply iterate through this response and extract values for keys you want. You can go like.
data = response.json()
issues = data.get('issues', list())
x = list()
for issue in issues:
temp = {
'key': issue['key'],
'customfield': issue['fields']['customfield_12830'],
'total': issue['fields']['progress']['total']
}
x.append(temp)
print(x)
x is list of dictionaries containing the data for fields you mentioned. Let me know if I have been unclear somewhere or what I have given is not what you are looking for.
PS: It is always advisable to use dict.get('keyname', None) to get values as you can always put a default value if key is not found. For this solution I didn't do it as I just wanted to provide approach.
Update: In the comments you(OP) mentioned that it gives attributerror.Try this code
data = response.json()
issues = data.get('issues', list())
x = list()
for issue in issues:
temp = dict()
key = issue.get('key', None)
if key:
temp['key'] = key
fields = issue.get('fields', None)
if fields:
customfield = fields.get('customfield_12830', None)
temp['customfield'] = customfield
progress = fields.get('progress', None)
if progress:
total = progress.get('total', None)
temp['total'] = total
x.append(temp)
print(x)