1

I am trying to get a book's review data by using Goodread's api while building an app with Flask. This is part of a project that I am doing from a tutorial series called CS50 Web Programming Series.

So I have been trying to pull the data by using Python's Request module and stored that data in a variable and later Jsonify it. So that when a user types in /api/<a book's isbn number> then it should return a Jsonified data of a book.

@app.route('/api/<isbn>', methods = ['GET'])
def isbn(isbn):
    #import api from Goodreads (stats) 
    r = requests.get("https://www.goodreads.com/book/review_counts.json", params={"key": "L3FHyOR3IhCo3kctcUz3zg", "isbns": "isbn"}).json()
    res = db.execute("SELECT * FROM books WHERE isbn = :isbn", {"isbn": isbn}).fetchone()
    print ('res')
    if res is None: 
        return "404 error"
    return jsonify({
        "title": res.title,
        "author": res.author,
        "year": res.year,
        "isbn": res.isbn,
        "review_count": r.reviews_count,
        "average_score": r.average_rating
    })

The error that I am getting when I type in an isbn looks like this. http://prntscr.com/ojnwbw

It seems so that I am making a major issue on the line where I define r. Could anyone help troubleshoot this problem?

3
  • Have you tried printing or logging your variable res so you see its format? It could be a numeric issue with average_rating for being decimal. Have you tried using sqlalchemy objects instead of direct SQL? (also be careful with SQL injection the way you coded it) Commented Jul 25, 2019 at 1:11
  • params={... "isbns": "isbn"} -- it looks like you are passing the "isbn" string here instead of the ISBN number. Commented Jul 25, 2019 at 1:56
  • Please include the error in the question itself, not a link to an external paste service or such. Commented Jul 25, 2019 at 6:20

1 Answer 1

3

Remove the quotes from around "isbn" as such:

bad:

>>> requests.get('https://www.goodreads.com/book/review_counts.json', params={"key": "[MY_API_KEY]", "isbns": "isbn"}).json()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/requests/models.py", line 897, in json
    return complexjson.loads(self.text, **kwargs)
  File "/usr/lib64/python3.6/json/__init__.py", line 354, in loads
    return _default_decoder.decode(s)
  File "/usr/lib64/python3.6/json/decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib64/python3.6/json/decoder.py", line 357, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

good, sending the value of the variable instead of the variable:

>>> isbn="0441172717"
>>> requests.get('https://www.goodreads.com/book/review_counts.json', params={"key": "[MY_API_KEY]", "isbns": isbn}).json()
{'books': [{'id': 47173379, 'isbn': '0441172717', 'isbn13': '9780441172719', 'ratings_count': 3, 'reviews_count': 8, 'text_reviews_count': 0, 'work_ratings_count': 625868, 'work_reviews_count': 1034615, 'work_text_reviews_count': 17000, 'average_rating': '4.22'}]}

better:

...
r = requests.get("https://www.goodreads.com/book/review_counts.json", params={"key": "[MY_API_KEY]", "isbns": isbn})
if r.status_code != 200:
    raise ValueError
...

best: check isbn for validity first and put your db.execute() first. Why bother goodreads.com if your DB doesn't have a result? Unless, you're counting on goodreads.com for making sure user input is correct and is protecting you from injection...?

Next, don't use your your real API key in a public question ;)

And finally, requests.get(...).json() wont work if there's an error:

>>> requests.get('https://www.goodreads.com/book/review_counts.json')
<Response [422]>

From goodreads.com/api: "You can mix ISBN10s and ISBN13s, but you'll receive a 422 error if you don't specify any, and you'll receive a 404 if none are found." You were sending "isbns=isbn" i.e. no real valid isbn.

Hope this helps!

Answering your question from your further comment:

Reformatting the response above into "pretty JSON" (google JSON viewer for an online way of copy+pasting JSON string to beautify) we see:

{  
   'books':[  
      {  
         'id':47173379,
         'isbn':'0441172717',
         'isbn13':'9780441172719',
         'ratings_count':3,
         'reviews_count':8,
         'text_reviews_count':0,
         'work_ratings_count':625902,
         'work_reviews_count':1034690,
         'work_text_reviews_count':17002,
         'average_rating':'4.22'
      }
   ]
}

You might now be able to see that the response is a Dict(Response)->Array(books)->Dicts so..

>>> r = requests.get('https://www.goodreads.com/book/review_counts.json', params={"key": "L3FHyOR3IhCo3kctcUz3zg", "isbns": isbn}).json()
>>> print(r)
{'books': [{'id': 47173379, 'isbn': '0441172717', 'isbn13': '9780441172719', 'ratings_count': 3, 'reviews_count': 8, 'text_reviews_count': 0, 'work_ratings_count': 625905, 'work_reviews_count': 1034694, 'work_text_reviews_count': 17002, 'average_rating': '4.22'}]}
>>> print(r['books'])
[{'id': 47173379, 'isbn': '0441172717', 'isbn13': '9780441172719', 'ratings_count': 3, 'reviews_count': 8, 'text_reviews_count': 0, 'work_ratings_count': 625905, 'work_reviews_count': 1034694, 'work_text_reviews_count': 17002, 'average_rating': '4.22'}]
>>> print(r['books'][0])
{'id': 47173379, 'isbn': '0441172717', 'isbn13': '9780441172719', 'ratings_count': 3, 'reviews_count': 8, 'text_reviews_count': 0, 'work_ratings_count': 625905, 'work_reviews_count': 1034694, 'work_text_reviews_count': 17002, 'average_rating': '4.22'}
>>> print(r['books'][0]['reviews_count'])
8

So, your answer to the attribute error is to use: "r['books'][0]['reviews_count']" not "r.reviews_count" as well as "r['books'][0]['?????']" format for any other attributes. You could also handle multiple ISBNs by the format "########,########,########,..." and handling multiple responses in your code according to the API.

Sign up to request clarification or add additional context in comments.

4 Comments

I could have put a lot more detail into my answer, and given even more suggestions. While writing apps for the web is fundamentally easy, and there are a lot of tutorials on how to do everything, they don't teach you everything you should know. Try to get some best practices down, like protecting your client, your server, and yourself first, foremost, and not always in that order or limited to... I think it's great that anyone can do anything these days and they're out there learning to do so. I just wish I seen more of them building up from the basics instead of blindly copy+pasting.
Fantastic advice and thank you so much for taking the time to answer.
Sorry to bother you but I am getting AttributeError: 'Response' object has no attribute 'reviews_count' error now that I have fixed the problem. Where do you think the problem is? from their api sample, I think i've assigned the value correctly...
I edited my answer to better explain my answer to your new question without comment size restrictions, but in brief use: r['books'][0]['reviews_count']

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.