Skip to content

Instantly share code, notes, and snippets.

@steve-kertes
Forked from gpiancastelli/goodreads-oauth-example.py
Last active April 15, 2022 15:41
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save steve-kertes/5862716 to your computer and use it in GitHub Desktop.
Save steve-kertes/5862716 to your computer and use it in GitHub Desktop.
A Python example of how to use OAuth on GoodReads. Includes scripts to pull list of books that are on a shelf and to add all owned books to a shelf.
from string import Template
import oauth2 as oauth
import urlparse
import urllib
import time
import xml.dom.minidom
import sys, getopt
# If you get 'Title Messed Up By Unicode Error' messages try
# export PYTHONIOENCODING=utf-8
url = 'http://www.goodreads.com'
target_list = 'holding'
consumer = oauth.Consumer(key='Your-GoodReads-Key',
secret='Your-GoodReads-Secret')
token = oauth.Token('oauth token key',
'oauth token secret')
client = oauth.Client(consumer, token)
#############################
#
# who are we?
#
def getUserId():
response, content = client.request('%s/api/auth_user' % url,'GET')
if response['status'] != '200':
raise Exception('Cannot fetch resource: %s' % response['status'])
# else:
# print 'User loaded.'
userxml = xml.dom.minidom.parseString(content)
user_id = userxml.getElementsByTagName('user')[0].attributes['id'].value
return str(user_id)
#############################
#
# fetch xml for a page of books on a shelf
#
def getShelfBooks(page, shelf_name):
owned_template = Template('${base}/review/list?format=xml&v=2&id=${user_id}&sort=author&order=a&key=${dev_key}&page=${page}&per_page=100&shelf=${shelf_name}')
body = urllib.urlencode({})
headers = {'content-type': 'application/x-www-form-urlencoded'}
request_url = owned_template.substitute(base=url, user_id=user_id, page=page, dev_key='gYPjS9uMjMC5CnWuSohlw', shelf_name=shelf_name)
response, content = client.request(request_url, 'GET', body, headers)
if response['status'] != '200':
raise Exception('Failure status: %s for page ' % response['status'] + page)
# else:
# print 'Page loaded!'
return content
#############################
#
# grab id and title from a <book> node
#
def getBookInfo(book):
book_id = book.getElementsByTagName('id')[0].firstChild.nodeValue
book_title = book.getElementsByTagName('title')[0].firstChild.nodeValue
return book_id, book_title
#############################
#
# grab id and title from a <book> node
#
def addBookToList(book_id, shelf_name):
body = urllib.urlencode({'name': shelf_name, 'book_id': book_id})
headers = {'content-type': 'application/x-www-form-urlencoded'}
response, content = client.request('%s/shelf/add_to_shelf.xml' % url,'POST', body, headers)
if response['status'] != '201':
raise Exception('Failure status: %s' % response['status'])
# else:
# print 'Book added!'
return True
#############################
#
# loop over each page of owned books
# loop over each book
# add book to list
#
user_id = getUserId()
print 'User id is: ' + user_id
current_page = 0
total_books = 0
while True:
current_page = current_page + 1
content = getShelfBooks(current_page, target_list)
xmldoc = xml.dom.minidom.parseString(content)
page_books = 0
for book in xmldoc.getElementsByTagName('book'):
book_id , book_title = getBookInfo(book)
try:
print 'Book %10s : %s' % (str(book_id), book_title)
except UnicodeEncodeError:
print 'Book %10s : %s' % (str(book_id), 'Title Messed Up By Unicode Error')
page_books += 1
total_books += 1
# addBookToList(book_id, 'holding')
# time.sleep(1)
# addBookToList(book_id, target_list)
# time.sleep(1)
print 'Found ' + str(page_books) + ' books on page ' + str(current_page) + ' (total = ' + str(total_books) + ')'
time.sleep(1)
if (page_books == 0):
break;
#############################
#
# so much fun
#
print 'Found ' + str(total_books)
print 'Done.'
## END ##
# oauth example for goodreads
#
# based on code found in https://gist.github.com/gpiancastelli/537923 by Giulio Piancastelli
#
# edit script with your dev key and secret
# run it
# visit the url
# confirm that you have accepted
# write down token!
#
import oauth2 as oauth
import urllib
import urlparse
url = 'http://www.goodreads.com'
request_token_url = '%s/oauth/request_token' % url
authorize_url = '%s/oauth/authorize' % url
access_token_url = '%s/oauth/access_token' % url
consumer = oauth.Consumer(key='Your-GoodReads-Key',
secret='Your-GoodReads-Secret')
client = oauth.Client(consumer)
response, content = client.request(request_token_url, 'GET')
if response['status'] != '200':
raise Exception('Invalid response: %s, content: ' % response['status'] + content)
request_token = dict(urlparse.parse_qsl(content))
authorize_link = '%s?oauth_token=%s' % (authorize_url,
request_token['oauth_token'])
print "Use a browser to visit this link and accept your application:"
print authorize_link
accepted = 'n'
while accepted.lower() == 'n':
# you need to access the authorize_link via a browser,
# and proceed to manually authorize the consumer
accepted = raw_input('Have you authorized me? (y/n) ')
token = oauth.Token(request_token['oauth_token'],
request_token['oauth_token_secret'])
client = oauth.Client(consumer, token)
response, content = client.request(access_token_url, 'POST')
if response['status'] != '200':
raise Exception('Invalid response: %s' % response['status'])
access_token = dict(urlparse.parse_qsl(content))
# this is the token you should save for future uses
print 'Save this for later: '
print 'oauth token key: ' + access_token['oauth_token']
print 'oauth token secret: ' + access_token['oauth_token_secret']
token = oauth.Token(access_token['oauth_token'],
access_token['oauth_token_secret'])
#
# As an example, let's add a book to one of the user's shelves
#
add_to_list = False
def addABook():
client = oauth.Client(consumer, token)
# the book is: "Generation A" by Douglas Coupland
body = urllib.urlencode({'name': 'to-read', 'book_id': 6801825})
headers = {'content-type': 'application/x-www-form-urlencoded'}
response, content = client.request('%s/shelf/add_to_shelf.xml' % url,
'POST', body, headers)
# check that the new resource has been created
if response['status'] != '201':
raise Exception('Cannot create resource: %s' % response['status'])
else:
print 'Book added!'
if add_to_list:
addABook()
## END ##
#
# I addded all my books to 'owned books' with the Quick Add.
# Since none of those books were on an actual shelf I could not export them.
# This script will move _all_ owned books to a shelf.
#
# Use goodreads-oauth-example.py to get an oauth token for your application.
# Fill in the consumer and token strings.
# Be sure to set target_list to the list you want to move _all_ your owned books onto.
#
from string import Template
import oauth2 as oauth
import urlparse
import urllib
import time
import xml.dom.minidom
import sys, getopt
# If you get 'Title Messed Up By Unicode Error' messages try
# export PYTHONIOENCODING=utf-8
target_list = 'own'
consumer = oauth.Consumer(key='Your-GoodReads-Key',
secret='Your-GoodReads-Secret')
token = oauth.Token('oauth token key',
'oauth token secret')
client = oauth.Client(consumer, token)
url = 'http://www.goodreads.com'
#############################
#
# who are we?
#
def getUserId():
response, content = client.request('%s/api/auth_user' % url,'GET')
if response['status'] != '200':
raise Exception('Cannot fetch resource: %s' % response['status'])
# else:
# print 'User loaded.'
userxml = xml.dom.minidom.parseString(content)
user_id = userxml.getElementsByTagName('user')[0].attributes['id'].value
return str(user_id)
#############################
#
# fetch xml for a page of owned books
#
def getOwnedBooks(page):
owned_template = Template('${base}/owned_books/user?format=xml&id=${user_id}&page=${page}&per_page=100')
body = urllib.urlencode({})
headers = {'content-type': 'application/x-www-form-urlencoded'}
request_url = owned_template.substitute(base=url, user_id=user_id, page=page)
response, content = client.request(request_url, 'GET', body, headers)
if response['status'] != '200':
raise Exception('Failure status: %s for page ' % response['status'] + page)
# else:
# print 'Page loaded!'
return content
#############################
#
# grab id and title from a <book> node
#
def getBookInfo(book):
book_id = book.getElementsByTagName('id')[0].firstChild.nodeValue
book_title = book.getElementsByTagName('title')[0].firstChild.nodeValue
return book_id, book_title
#############################
#
# ask api to add a book to a shelf
#
def addBookToList(book_id, shelf_name):
body = urllib.urlencode({'name': shelf_name, 'book_id': book_id})
headers = {'content-type': 'application/x-www-form-urlencoded'}
response, content = client.request('%s/shelf/add_to_shelf.xml' % url,'POST', body, headers)
if response['status'] != '201':
raise Exception('Failure status: %s' % response['status'])
# else:
# print 'Book added!'
return True
#############################
#
# loop over each page of owned books
# loop over each book
# add book to list
#
user_id = getUserId()
print 'User id is: ' + user_id
current_page = 0
total_books = 0
while True:
current_page = current_page + 1
content = getOwnedBooks(current_page)
xmldoc = xml.dom.minidom.parseString(content)
page_books = 0
for book in xmldoc.getElementsByTagName('book'):
book_id , book_title = getBookInfo(book)
try:
print 'Book %10s : %s' % (str(book_id), book_title)
except UnicodeEncodeError:
print 'Book %10s : %s' % (str(book_id), 'Title Messed Up By Unicode Error')
page_books += 1
total_books += 1
addBookToList(book_id, target_list)
time.sleep(1)
print 'Found ' + str(page_books) + ' books on page ' + str(current_page) + ' (total = ' + str(total_books) + ')'
time.sleep(1)
if (page_books == 0):
break;
#############################
#
# so much fun
#
print 'Found ' + str(total_books)
print 'Done.'
## END ##
@aulon
Copy link

aulon commented Aug 3, 2020

For those of you who are getting a 301 response when running the file goodreads-oauth-example.py , replace http with https in the url of Goodreads :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment