Hack 90 Syndicate a List of Books from Amazon with RSS and ASP

figs/expert.gif figs/hack90.gif

With the wide audience for RSS data, just about everything is being turned into an RSS feed. This hack turns any Amazon Web Services (AWS) query result into an RSS feed.

6.12.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. You also need an Amazon developer token and an Amazon associates tag, both available from http://www.amazon.com/associates.

6.12.2 The Code

The file amazon_rss.asp is in the file archive for the book. 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, 



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, "&", "&amp;")

        XMLify = Replace(XMLify, "<", "<")

        XMLify = Replace(XMLify, ">", ">")

        XMLify = Replace(XMLify, """""", """")

        XMLify = Replace(XMLify, "'", "&apos;")

    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/



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/



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/



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/



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/



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>

6.12.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 that author's 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