<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>goodreads &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/goodreads/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Tue, 10 Dec 2024 08:40:30 +0000</lastBuildDate>
	<language>en-GB</language>
	<copyright>© Terence Eden. 🄯 CC BY-SA. See https://shkspr.mobi/blog/copyright-and-copyleft/</copyright>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>goodreads &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Add review to Goodreads from Schema markup]]></title>
		<link>https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/</link>
					<comments>https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#respond</comments>
				<dc:creator><![CDATA[Terence Eden]]></dc:creator>
		<pubDate>Tue, 10 Dec 2019 19:40:23 +0000</pubDate>
		<category><![CDATA[api]]></category><category><![CDATA[code]]></category><category><![CDATA[goodreads]]></category><category><![CDATA[hack]]></category><category><![CDATA[python]]></category><category><![CDATA[schema.org]]></category>		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=33209</guid>

					<description><![CDATA[I write book reviews on my blog. I also want to syndicate them to Goodreads.  Sadly, Goodreads doesn&#039;t natively read the Schema.org markup I so carefully craft.  So here&#039;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&#039;ll use the example keys - please…]]></description>
										<content:encoded>&lt;p&gt;I write book reviews on my blog. I also want to syndicate them to Goodreads.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2 id="goodreads-api-keys"&gt;&lt;a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#goodreads-api-keys"&gt;Goodreads API Keys&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Get your Keys from &lt;a href="https://www.goodreads.com/api/keys"&gt;&lt;/a&gt;&lt;a href="https://www.goodreads.com/api/keys"&gt;https://www.goodreads.com/api/keys&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will also need to &lt;a href="https://www.goodreads.com/api/oauth_example#python"&gt;get OAuth tokens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this documentation, I'll use the example keys - please substitute them with your own keys.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python"&gt;from rauth.service import OAuth1Service, OAuth1Session

# Get a real consumer key &amp;amp; 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)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Visit the URL printed out, and authorise the app.  Then run this to get the access tokens you need:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python"&gt;session = goodreads.get_auth_session(OAUTH_TOKEN, OAUTH_SECRET)
ACCESS_TOKEN = session.access_token
ACCESS_TOKEN_SECRET = session.access_token_secret
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Save those tokens, we'll need them later!&lt;/p&gt;

&lt;h2 id="get-json-ld-from-html-page"&gt;&lt;a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#get-json-ld-from-html-page"&gt;Get JSON-LD from HTML page&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;This uses &lt;a href="https://docs.python-requests.org/en/latest/index.html"&gt;Requests&lt;/a&gt; and &lt;a href="https://pypi.org/project/beautifulsoup4/"&gt;BeautifulSoup&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python"&gt;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

&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="isbn-to-goodreads-id"&gt;&lt;a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#isbn-to-goodreads-id"&gt;ISBN to Goodreads ID&lt;/a&gt;&lt;/h2&gt;

&lt;pre&gt;&lt;code class="language-python"&gt;goodreads_id = requests.get("https://www.goodreads.com/book/isbn_to_id/" + isbn + "?key=" + API_KEY).text
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will return a number - the Goodreads ID.&lt;/p&gt;

&lt;h2 id="post-a-review"&gt;&lt;a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#post-a-review"&gt;Post a review&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.goodreads.com/api/index#review.create"&gt;documentation for posting Goodreads reviews&lt;/a&gt; is a bit sparse.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python"&gt;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)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="setting-the-date"&gt;&lt;a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#setting-the-date"&gt;Setting the date&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;The Goodreads API is... crap. &lt;a href="https://www.goodreads.com/topic/show/20997772-review-read-at-doesn-t-work"&gt;Posting the review doesn't actually add a date to the review&lt;/a&gt;. So you need to edit the review to post it &lt;em&gt;again&lt;/em&gt;.  To start with, we need to get the review's ID:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python"&gt;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)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="get-the-code"&gt;&lt;a href="https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/#get-the-code"&gt;Get the code&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gitlab.com/edent/schema-to-goodreads/tree/master"&gt;The Python code is available on my GitLab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;mostly&lt;/em&gt; works, I don't run it that often.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=33209&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2019/12/add-review-to-goodreads-from-schema-markup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
