You have added that you want it to load fast. This means that you cannot do retries in python as any retry you do in python would mean longer page loading times.
This means that you have to do the loading in the browser. You can use the same method as for the pure python method. At first, you just use all the images in the request and make additional requests for all the volumes that did not have an image. This means that you have 2 endpoints, one for the volume_information. And another endpoint to just get the data for one volume.
Note that I am using the term volume instead of book, as that is what the Google API also uses.
Now, JavaScript is not my strong suit so the solution I am providing here has a lot of room for improvement.
I've made this example using flask. This example should help you implement your solution that fits your specific application.
Extra Note: In my testing I've noticed that, some regions more often respond with all thumbnails than others. The API sends different responses based on your IP address. If I set my IP to be in the US I often get all the thumbnails without retries. I am using a VPN to do this, but there may be other solutions.
app.py
import time
from flask import Flask, render_template, request, jsonify
import requests
app = Flask(__name__)
@app.route('/')
def landing():
return render_template('index.html', volumes=get_volumes('Harry Potter'))
@app.route('/get_volume_info')
def get_volume_info_endpoint():
volume_id = request.args.get('volume_id')
if volume_id is None:
# Return an error if no volume id was provided
return jsonify({'error': 'must provide argument'}), 400
# To stop spamming the API
time.sleep(0.250)
# Request volume data
url = f'https://www.googleapis.com/books/v1/volumes/{volume_id}'
response = requests.get(url)
volume = response.json()
# Get the info using the helper function
volume_info = get_volume_info(volume, volume_id)
# Return json object with the info
return jsonify(volume_info), 200
def get_volumes(search):
# Make request
url = f'https://www.googleapis.com/books/v1/volumes?q={search}'
response = requests.get(url)
data = response.json()
# Get the volumes
volumes = data['items']
# Add list to store results
volume_info_collection = []
# Loop over the volumes
for volume in volumes:
volume_id = volume['id']
# Get volume info using helper function
volume_info = get_volume_info(volume, volume_id)
# Add it to the result
volume_info_collection.append(volume_info)
return volume_info_collection
def get_volume_info(volume, volume_id):
# Get basic information
volume_title = volume['volumeInfo']['title']
volume_authors = volume['volumeInfo']['authors']
# Set default value for thumbnail
volume_thumbnail = None
try:
volume_thumbnail = volume['volumeInfo']['imageLinks']['thumbnail']
except KeyError:
# Failed we keep the None value
print('Failed to get thumbnail')
# Fill in the dict
volume_info = {
'volume_id': volume_id,
'volume_title': volume_title,
'volume_authors': volume_authors,
'volume_thumbnail': volume_thumbnail
}
# Return volume info
return volume_info
if __name__ == '__main__':
app.run()
Template index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
let tracker = {}
function get_thumbnail(id) {
let url = '/get_volume_info?volume_id=' + id
fetch(url).then(function (response) {
return response.json();
}).then(function (data) {
console.log(data);
return data['volume_thumbnail']
}).catch(function () {
console.log("Error");
});
}
function image_load_failed(id) {
let element = document.getElementById(id)
if (isNaN(tracker[id])) {
tracker[id] = 0
}
console.log(tracker[id])
if (tracker[id] >= 5) {
element.src = 'https://via.placeholder.com/128x196C/O%20https://placeholder.com/'
return
}
element.src = get_thumbnail(id)
tracker[id]++
}
</script>
</head>
<body>
<table>
<tr>
<th>ID</th>
<th>Title</th>
<th>Authors</th>
<th>Thumbnail</th>
</tr>
{% for volume in volumes %}
<tr>
<td>{{ volume['volume_id'] }}</td>
<td>{{ volume['volume_title'] }}</td>
<td>{{ volume['volume_authors'] }}</td>
<td><img id="{{ volume['volume_id'] }}" src="{{ volume['volume_thumbnail'] }}"
onerror="image_load_failed('{{ volume['volume_id'] }}')"></td>
</tr>
{% endfor %}
</table>
</body>
</html>