I write book reviews on my blog. I also want to syndicate them to Goodreads.
Sadly, Goodreads doesn't natively read the Schema.org markup I so carefully craft. So here's the scrap of code I use to syndicate my reviews.
Goodreads API Keys
Get your Keys from https://www.goodreads.com/api/keys
You will also need to get OAuth tokens
For this documentation, I'll use the example keys - please substitute them with your own keys.
Python 3
from rauth.service import OAuth1Service, OAuth1Session # Get a real consumer key & secret from: https://www.goodreads.com/api/keys API_KEY = 'ABC123' API_SECRET = 'XYZ789' goodreads = OAuth1Service( consumer_key = API_KEY, consumer_secret = API_SECRET, name='goodreads', request_token_url = 'https://www.goodreads.com/oauth/request_token', authorize_url = 'https://www.goodreads.com/oauth/authorize', access_token_url = 'https://www.goodreads.com/oauth/access_token', base_url = 'https://www.goodreads.com/' ) OAUTH_TOKEN, OAUTH_SECRET = goodreads.get_request_token(header_auth=True) authorize_url = goodreads.get_authorize_url(request_token) print(authorize_url)
Visit the URL printed out, and authorise the app. Then run this to get the access tokens you need:
Python 3
session = goodreads.get_auth_session(OAUTH_TOKEN, OAUTH_SECRET) ACCESS_TOKEN = session.access_token ACCESS_TOKEN_SECRET = session.access_token_secret
Save those tokens, we'll need them later!
Get JSON-LD from HTML page
This uses Requests and BeautifulSoup.
Python 3
import requests from bs4 import BeautifulSoup import json import re review_url = 'https://shkspr.mobi/blog/2019/11/review-because-internet-by-gretchen-mcculloch/' r = requests.get(review_url) soup = BeautifulSoup(r.text) pattern = re.compile(r"\"@type\":\"Review\"") script = soup.find("script", text=pattern) review = script.text review_json = json.loads(review) isbn = review_json["itemReviewed"]["isbn"] rating = review_json["reviewRating"]["ratingValue"] date = review_json["datePublished"]:date[0:10] description = review_json["description"] + " " + review_url
ISBN to Goodreads ID
Python 3
goodreads_id = requests.get("https://www.goodreads.com/book/isbn_to_id/" + isbn + "?key=" + API_KEY).text
This will return a number - the Goodreads ID.
Post a review
The documentation for posting Goodreads reviews is a bit sparse.
Python 3
goodreads_api = OAuth1Session( consumer_key = API_KEY, consumer_secret = API_SECRET, access_token = ACCESS_TOKEN, access_token_secret = ACCESS_TOKEN_SECRET, ) # Post the review to the API # https://www.goodreads.com/api/index#review.create review_data = {"book_id": goodreads_id, "review[review]":description, "review[rating]":rating, "review[read_at]":date} response = goodreads_api.post('https://www.goodreads.com/review.xml', review_data) print(response.text)
Setting the date
The Goodreads API is... crap. Posting the review doesn't actually add a date to the review. So you need to edit the review to post it again. To start with, we need to get the review's ID:
Python 3
import xml.etree.ElementTree as ET tree = ET.fromstring(response.text) review_id = tree.find("id").text review_data = {"review[read_at]":date[0:10]} response = goodreads_api.post('https://www.goodreads.com/review/'+review_id+'.xml', review_data) print(response.text)
Get the code
The Python code is available on my GitLab.
But... The Goodreads API is a bit picky. ISBN lookup doesn't work very well, and bits of the API are flaky. So while this code mostly works, I don't run it that often.
