They say there are a few things that bring people together. Among these are food and trivia games.
Why not make a trivia game of your own? You can! And guess what? You don’t have to create your own questions. There are APIs out there with questions and answers that you can pull in to your own apps. In this lab, we’ll see how that’s done.
APIs
JSON
HTTP Requests
The requests package
As always, make sure you are in your virtual environment. Install the requests library with:
pip install requests
Create the file ~/cmsi1010/lab18/trivia.py. Prime it with the following skeleton code that fetches a random multiple-choice question from the Open Trivia Database:
import requests url = "https://opentdb.com/api.php?amount=1&type=multiple&encode=url3986" response = requests.get(url) if response.status_code != 200: raise ValueError(f'API error: {response.status_code}') body = response.json() print(body)
Here we used the get function from the requests module to programmatically do the same thing your web browser would do if you’d typed that url in to the browser yourself. When you make that GET request, the server (yep, it’s called a server) responds with a status code. You want a 200 because that means OK. (If you are interested, check out this full list of response status codes.)
When running this program, you’ll either get an error response or a response containing a random question such as this one:
{
'response_code': 0,
'results': [
{
'type': 'multiple',
'difficulty': 'medium',
'category': 'History',
'question': 'What%20was%20the%20total%20length%20of%20the%20Titanic%3F',
'correct_answer': '882%20ft%20%7C%20268.8%20m',
'incorrect_answers': [
'759%20ft%20%7C%20231.3%20m',
'1042%20ft%20%7C%20317.6%20m',
'825%20ft%20%7C%20251.5%20m'
]
}
]
}
Hey, that is a Python dictionary! The server actually responded with a big string in JSON format, which we turned into a dictionary when we called response.json(). So what’s in this dictionary? That’s explained in the Open Trivia Database’s API documentation, but here’s a quick summary:
response_code: 0 means OK, other values indicate different sorts of errors.results: a list of questions, each represented as a dictionary. In our request, we only asked for one result, and that’s all we got.type: the type of question, e.g., multiple choice.difficulty: the difficulty level of the question, e.g., easy, medium, hard.category: the category of the question, e.g., History, Science, Art, Mythology. See the documentation for the complete list—there are quite a few!question: the question text, encoded.correct_answer: the correct answer, encoded.incorrect_answers: a list of incorrect answers, encoded.That looks kinda ugly and weirdThe Open Trivia Database API is convenient and completely free! But ngl, its weirder and uglier than most public APIs. First it requires some encoding that most APIs do not—like why the heck to we have to specify url3986 and why are there so many weird characters in the response? Furthermore the fact there is a
response_codein the response itself is super confusing because the web protocol (HTTPS) has its own response code.I’m thinking of finding a better API for the future. But maybe this quirkiness builds character. I dunno.
Unfortunately, this API encodes its text responses, something that is not necessary in modern APIs, 99% of which use the worldwide standard UTF-8. The encoding scheme looks really messy, so we have to clean it up. And that inner response code is annoying! Let’s fix things up so that our program checks for errors in the response code and returns only the question data in its clean decoded form:
import requests from urllib.parse import unquote url = "https://opentdb.com/api.php?amount=1&type=multiple&encode=url3986" response = requests.get(url) if response.status_code != 200: raise ValueError(f'API error: {response.status_code}') body = response.json() if body['response_code'] != 0: raise ValueError(f'OpenTDB error: {body["response_code"]}') question = body['results'][0] question['category'] = unquote(question['category']) question['question'] = unquote(question['question']) question['correct_answer'] = unquote(question['correct_answer']) question['incorrect_answers'] = [unquote(ans) for ans in question['incorrect_answers']] print(question)
Here’s an example run with the new code:
{
'type': 'multiple',
'difficulty': 'medium',
'category': 'History',
'question': 'What was the total length of the Titanic?',
'correct_answer': '882 ft | 268.8 m',
'incorrect_answers': [
'759 ft | 231.3 m',
'1042 ft | 317.6 m',
'825 ft | 251.5 m'
]
}
Remember you don’t have to do these steps for most APIs.
Believe it or not, that’s it for the work we do together! As the last lab in the course, it’s up to you, during your time, to put everything together into an app that is interesting! Ideas are in the Challenges below. Have fun! Work Hard! Don’t Give Up! And importantly, don’t flail: if you get stuck work with your classmates and ask the instructor or TA for help when needed.
Now it’s your turn. Here are some ideas for you to extend the activities above:
APIs, especially those that work over the web, are an important part of modern software systems. Although this was our last lab of the course, don’t stop learning! Continue your studies with:
We’ve covered:
requests packageHere are some questions useful for your spaced repetition learning. Many of the answers are not found on this page. Some will have popped up in lecture. Others will require you to do your own research.
requests package? response_code in the response itself is confusing, as the web protocol (HTTPS) has its own response codes.