4
\$\begingroup\$

I am learning Python (about 6 months in, sporadically) and today I decided to write a script to download JSON information from an API.

To start, I wrote a one-page script with a few functions.

The script does the following:

  1. The user enters the script on the terminal with a search value.
  2. The script gets the response information from the Star Wars API (https://swapi.co). This is an open API, so I don't need any keys or tokens.
  3. If the API returns one value, the information is displayed immediately.
  4. If the API returns several values, the script prompts the user to make a choice. Then the information is displayed.

Right now, the script only works for people in the SWAPI (e.g., Luke Skywalker).

Any feedback on my code would be helpful.

#!/usr/bin/python3

'''Script to get information from https://swapi.co/'''

import requests
import json
import sys

#TODO: Print useful information such as movie titles or names of worlds instead of showing links
#Break up URL to allow for more dynamic searching in the future. Right now, script only handles the people parameter when getting info from the API.

UNDERLINE_LEN = 30

def show(data, index=0):
    '''
        Displays an individual Swapi object.
        data = data retrieved from api
        index = which entry to select. Default is 0 for single item lists.
    '''
    info = data['results'][index]
    print()
    print('-' * UNDERLINE_LEN)
    print("Name : {0}\n{1}".format(data['results'][index]['name'], '-' * UNDERLINE_LEN))
    for k, v in info.items():
        if type(v) is list:
            counter = 0
            print(k, ':')
            for j in v:
                print("\t{0}".format(v[counter]))
                counter += 1
        else: 
            if k != 'name':
                print(k, ':', v)


def show_choices(data):
    '''Shows a list of Swapi objects and prompts users to select an object [integer]'''
    info = data['results']
    counter = 0
    for i in info:
        print("[{0}] {1}".format(counter + 1, data['results'][counter]['name']))
        counter += 1
    print()
    choice = int(input("Which item would you like to view? "))
    try:
        show(data, choice - 1)
    except IndexError as ie:
        print(ie)


def grab():
    '''Main function.'''
    try:
        search = sys.argv[1]
        url = 'https://swapi.co/api/people/?search={0}'.format(search)      #need to divide url to make search process more dynamic
        r = requests.get(url)
        data = json.loads(r.text)
        if data is not None:
            numResults = int(data['count'])
            txt = "results"
            if numResults == 1:
                txt = "result"    
            resText = "{0} {1} found for search parameter '{2}'".format(numResults, txt, search)
            print(resText)
            if numResults == 1:
                show(data)
            else:
                show_choices(data)      
    except Exception as e:
        print(e)

grab()
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

You should never compose URLs using this technique!

search = sys.argv[1]
url = 'https://swapi.co/api/people/?search={0}'.format(search)      #need to divide url to make search process more dynamic
r = requests.get(url)

Query string parameters need to be percent-encoded. The Python Requests library provides a correct way to do it:

search = sys.argv[1]
url = 'https://swapi.co/api/people/'
r = requests.get(url, params={'search': search})

Going further, I would caution you to never concatenate any user-supplied string to form another string that will be interpreted by some computer system. You must first considering the escaping mechanism. SQL injections, cross-site scripting, LDAP injection, etc. — they're all vulnerabilities that result from this same kind of carelessness.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for the feedback. I saw a few params examples but didn't understand them. Well noted on the concatenation. \$\endgroup\$ Jan 27, 2018 at 0:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.