Hack 85 Syndicate a List of Books with RSS
Someday all data will be available as RSS. Get
a head start by syndicating Amazon search results.
RSS is an XML format that's widely used
to syndicate content. Depending on who you ask, it stands for
Rich Site Summary or Really Simple
Syndication and has become a standard way for publishers
to make their content available. News Readers
are programs that gather RSS feeds together and transform them into
human-readable form. They've become a popular way to read news sites
online.
With the wide audience for RSS data, just about everything is being
turned into an RSS feed. This hack by Sean Nolan turns any AWS query
result into an RSS feed.
85.1 What You Need
This hack uses ASP, which runs on Windows servers running IIS. The
logic is straightforward, though, and could be translated to any
scripting language.
85.2 The Code
Create a file called
amazon_rss.asp
and include the following code. Be sure to change the
Const declarations to match
your setup.
<%
'' AMAZON-RSS.ASP
'' Sean P. Nolan
'' http://www.yaywastaken.com/
''
'' This code is free for you to use as you see fit. Copy it, rewrite it,
'' run it yourself, whatever. But no warranties or guarantees either. Who
'' knows what the hell it does. Not me, that's for sure!
''
'' Generates an RSS 0.91 feed from an Amazon book query
''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Const MAX_PAGES_DEFAULT = 10
Const DEV_TOKEN = "insert developer token"
Const AFFILIATE_CODE = "insert associate tag"
Const XSL_FILE = "amazon_lite.xsl" 'change to heavy for more info.
Dim szTitle, szMaxPages, nMaxPages
Response.ContentType = "text/xml"
Server.ScriptTimeout = 60 * 4 ' 4-minute maximum
Response.Expires = 0
szMaxPages = Request.QueryString("maxpages")
If (szMaxPages = "") Then
nMaxPages = MAX_PAGES_DEFAULT
Else
nMaxPages = CLng(szMaxPages)
End If
szTitle = "Amazon Books: " & XMLify(Request.QueryString("keywords"))
%><?xml version="1.0" encoding="ISO-8859-1" ?>
<rss version="0.91">
<channel>
<link>http://www.yaywastaken.com/amazon/</link>
<title><%= szTitle %></title>
<description>Create your own custom Amazon RSS feed!</description>
<language>en-us</language>
<%
RenderItems Request.QueryString("keywords"), _
Request.QueryString("browse") , _
Request.QueryString("author") , _
Request.QueryString("shortdesc"), _
nMaxPages
%>
</channel>
</rss>
<%
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' RenderItems
Sub RenderItems(szKeywords, szBrowseNode, szAuthor, szShortDesc, [RETURN]
nMaxPages)
Dim szURLFmt, szURL, xmlDoc, ipage, http, xmlErr
Dim fParsed, xslDoc, szXSLPath, szOutput
'On Error Resume Next
If (szShortDesc <> "") Then
szXSLPath = "amazon-lite.xsl"
Else
szXSLPath = "amazon-heavy.xsl"
End If
Set xslDoc = Server.CreateObject("Msxml2.DOMDocument")
xslDoc.async = False
xslDoc.load(Server.MapPath(szXSLPath))
If (szBrowseNode <> "") Then
szURLFmt = "http://xml.amazon.com/onca/xml?v=1.0&" & _
"t=" & AFFILIATE_CODE & _
"&dev-t=" & DEV_TOKEN & "&BrowseNodeSearch=" & _
Server.URLEncode(szBrowseNode) & _
"&mode=books&type=heavy&page=%%%PAGE%%%&f=xml"
ElseIf (szAuthor <> "") Then
szURLFmt = "http://xml.amazon.com/onca/xml?v=1.0&" & _
"t=" & AFFILIATE_CODE & _
"&dev-t=" & DEV_TOKEN & "&AuthorSearch=" & _
Server.URLEncode(szAuthor) & _
"&mode=books&type=heavy&page=%%%PAGE%%%&f=xml"
Else
szURLFmt = "http://xml.amazon.com/onca/xml?v=1.0&" & _
"t=" & AFFILIATE_CODE & _
"&dev-t=" & DEV_TOKEN & "&KeywordSearch=" & _
Server.URLEncode(szKeywords) & _
"&mode=books&type=heavy&page=%%%PAGE%%%&f=xml"
End If
ipage = 1
Do
szURL = Replace(szURLFmt, "%%%PAGE%%%", ipage)
Set http = Server.CreateObject("Msxml2.ServerXMLHTTP")
http.open "GET", szURL, False
http.send ""
If (http.status <> 200) Then
Exit Do
End If
Set xmlDoc = Server.CreateObject("Msxml2.DOMDocument")
xmlDoc.async = False
xmlDoc.validateOnParse = False
xmlDoc.resolveExternals = False
fParsed = xmlDoc.loadXML(http.responseText)
If (Not fParsed) Then
Exit Do
End If
Set xmlErr = Nothing
Set xmlErr = xmlDoc.selectSingleNode("ProductInfo/ErrorMsg")
If (Not xmlErr Is Nothing) Then
Exit Do
End If
Set xslDoc = Nothing
Set xslDoc = Server.CreateObject("Msxml2.DOMDocument")
xslDoc.async = False
xslDoc.validateOnParse = False
xslDoc.resolveExternals = False
xslDoc.load(Server.MapPath(XSL_FILE))
szOutput = xmlDoc.transformNode(xslDoc)
Response.Write szOutput
ipage = ipage + 1
If (ipage > nMaxPages) Then
Exit Do
End If
Loop
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Helpers
Function XMLify(sz)
XMLify = Replace(sz, "&", "&")
XMLify = Replace(XMLify, "<", "<")
XMLify = Replace(XMLify, ">", ">")
XMLify = Replace(XMLify, """""", """")
XMLify = Replace(XMLify, "'", "'")
End Function
%>
This file makes an Amazon Web Services XML/HTTP request based on
querystring variables passed in the URL, and transforms the XML
locally with an XSL stylesheet. Depending on how much detail you want
in the RSS feed, there are two different
stylesheets. The first, for light data,
is called amazon_lite.xsl.
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<xsl:template match="ProductInfo/Details">
<item><link>
<xsl:value-of select="@url" />
</link><title>
<xsl:value-of select="ProductName" />
</title><description>
<xsl:text>Author: </xsl:text>
<xsl:value-of select="Authors/Author[1]" />
<xsl:text>; </xsl:text>
<xsl:value-of select="OurPrice" />
<xsl:if test="Availability">
<xsl:text> (</xsl:text>
<xsl:value-of select="Availability" />
<xsl:if test="Availability = 'Pre Order'">
<xsl:text>: release date </xsl:text>
<xsl:value-of select="ReleaseDate" />
</xsl:if>
<xsl:text>)</xsl:text>
</xsl:if>
</description></item>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="ProductInfo/Details" />
</xsl:template>
</xsl:stylesheet>
Another stylesheet, appropriately titled
amazon_heavy.xsl, provides more detailed
information in the feed.
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<xsl:template match="ProductInfo/Details">
<item><link>
<xsl:value-of select="@url" />
</link><title>
<xsl:value-of select="ProductName" />
</title><description>
<br />
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="@url" />
</xsl:attribute>
<xsl:element name="img">
<xsl:attribute name="src">
<xsl:value-of select="ImageUrlMedium" />
</xsl:attribute>
<xsl:attribute name="border">0</xsl:attribute>
<xsl:attribute name="hspace">4</xsl:attribute>
<xsl:attribute name="vspace">4</xsl:attribute>
<xsl:attribute name="align">left</xsl:attribute>
</xsl:element>
</xsl:element>
<font size="+1">
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="@url" />
</xsl:attribute>
<xsl:value-of select="ProductName" />
</xsl:element>
</font>
<br />
<xsl:text>Author: </xsl:text>
<xsl:value-of select="Authors/Author[1]" />
<xsl:text>; </xsl:text>
<xsl:value-of select="OurPrice" />
<xsl:if test="Availability">
<xsl:text> (</xsl:text>
<xsl:value-of select="Availability" />
<xsl:if test="Availability = 'Pre Order'">
<xsl:text>, release date </xsl:text>
<xsl:value-of select="ReleaseDate" />
</xsl:if>
<xsl:text>)</xsl:text>
</xsl:if>
<br clear="all" />
<xsl:for-each select="Reviews/CustomerReview">
<xsl:choose>
<xsl:when test="Rating = 1">
<img src="http://g-images.amazon.com/images/G/01/[RETURN]
detail/stars-1-0.gif" border="0" hspace="2" vspace="2" />
</xsl:when>
<xsl:when test="Rating = 2">
<img src="http://g-images.amazon.com/images/G/01/[RETURN]
detail/stars-2-0.gif" border="0" hspace="2" vspace="2" />
</xsl:when>
<xsl:when test="Rating = 3">
<img src="http://g-images.amazon.com/images/G/01/[RETURN]
detail/stars-3-0.gif" border="0" hspace="2" vspace="2" />
</xsl:when>
<xsl:when test="Rating = 4">
<img src="http://g-images.amazon.com/images/G/01/[RETURN]
detail/stars-4-0.gif" border="0" hspace="2" vspace="2" />
</xsl:when>
<xsl:when test="Rating = 5">
<img src="http://g-images.amazon.com/images/G/01/[RETURN]
detail/stars-5-0.gif" border="0" hspace="2" vspace="2" />
</xsl:when>
</xsl:choose>
<b><xsl:value-of select="Summary" /></b>
<br />
<xsl:value-of select="Comment" />
<br /><br />
</xsl:for-each>
</description></item>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="ProductInfo/Details" />
</xsl:template>
</xsl:stylesheet>
85.3 Running the Hack
The main file amazon_rss.asp accepts a few
querystring variables. One of these is required each time you run the
script.
- keywords
-
The subject of the books in the RSS feed.
- browse
-
Use this if you know the browse node code you'd like to syndicate.
- author
-
Set to the author's name to syndicate a list of their books.
Make all three files available on a web server, and then browse to
amazon_rss.asp with a variable included. For
example:
http://example.com/amazon_rss.asp?keywords=hacks
Now anyone can add this URL to their RSS news reader!
—Sean Nolan
|