NASA Astronomy Picture of the Day
Here is my first attempt of building a single HTML page, using the RSS feed taken from NASA Astronomy Picture of the Day (APOD)
The application written in Go carries out the following.
-
makes a HTTP request for the RSS feed
-
process the XML feed data
-
display feed data on a single HTML page
RSS Feed
Here’s what the feed data looks like.
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>APOD</title>
<link>https://apod.nasa.gov/</link>
<description>Astronomy Picture of the Day</description>
<language>en-us</language>
<image>
<title>APOD</title>
<url>https://apod.nasa.gov/favicon.ico</url>
<link>https://apod.nasa.gov/</link>
</image>
<item>
<title>Eclipse under the ISS</title>
<link>https://apod.nasa.gov/apod/astropix.html</link>
<description><p><a href="https://apod.nasa.gov/apod/astropix.html"><img src="https://apod.nasa.gov/apod/calendar/S_200627.jpg" align="left" alt="The dark shadow of the New Moon reached out and touched planet Earth on" border="0" /></a> The dark shadow of the New Moon reached out and touched planet Earth on</p><br clear="all"/></description>
</item>
<item>
<title>Eclipse under the Bamboo</title>
<link>https://apod.nasa.gov/apod/ap200626.html</link>
<description><p><a href="https://apod.nasa.gov/apod/ap200626.html"><img src="https://apod.nasa.gov/apod/calendar/S_200626.jpg" align="left" alt="Want to watch a solar eclipse safely?" border="0" /></a> Want to watch a solar eclipse safely?</p><br clear="all"/></description>
</item>
<item>
<title>Eclipse Street, Hong Kong</title>
<link>https://apod.nasa.gov/apod/ap200625.html</link>
<description><p><a href="https://apod.nasa.gov/apod/ap200625.html"><img src="https://apod.nasa.gov/apod/calendar/S_200625.jpg" align="left" alt="Eclipse Street, Hong Kong" border="0" /></a> Eclipse Street, Hong Kong</p><br clear="all"/></description>
</item>
<item>
<title>Inverted City Beneath Clouds</title>
<link>https://apod.nasa.gov/apod/ap200624.html</link>
<description><p><a href="https://apod.nasa.gov/apod/ap200624.html"><img src="https://apod.nasa.gov/apod/calendar/S_200624.jpg" align="left" alt="How could that city be upside-down?" border="0" /></a> How could that city be upside-down?</p><br clear="all"/></description>
</item>
<item>
<title>The X Ray Sky from eROSITA</title>
<link>https://apod.nasa.gov/apod/ap200623.html</link>
<description><p><a href="https://apod.nasa.gov/apod/ap200623.html"><img src="https://apod.nasa.gov/apod/calendar/S_200623.jpg" align="left" alt="What if you could see X-rays? " border="0" /></a> What if you could see X-rays? </p><br clear="all"/></description>
</item>
<item>
<title>Moon Mountains Magnified during Ring of Fire Eclipse</title>
<link>https://apod.nasa.gov/apod/ap200622.html</link>
<description><p><a href="https://apod.nasa.gov/apod/ap200622.html"><img src="https://apod.nasa.gov/apod/calendar/S_200622.jpg" align="left" alt="What are those dark streaks in this composite image of yesterday's solar eclipse?" border="0" /></a> What are those dark streaks in this composite image of yesterday's solar eclipse?</p><br clear="all"/></description>
</item>
<item>
<title>Moon Occults Venus</title>
<link>https://apod.nasa.gov/apod/ap200621.html</link>
<description><p><a href="https://apod.nasa.gov/apod/ap200621.html"><img src="https://apod.nasa.gov/apod/calendar/S_200621.jpg" align="left" alt="It may look like " border="0" /></a> It may look like </p><br clear="all"/></description>
</item>
<textInput>
<title>Search APOD</title>
<description>Search APOD</description>
<name>query</name>
<link>https://apod.nasa.gov/cgi-bin/apod/apod_search</link>
</textInput>
</channel>
</rss>
Making a HTTP request
func makeRequest(done chan<- nasaRSS) {
client := httpClient()
req, err := http.NewRequest("GET", requestURL, nil)
checkError(err)
resp, err := client.Do(req)
if resp.StatusCode != 200 {
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
log.Fatal("Request status = ", resp.StatusCode)
}
defer resp.Body.Close()
var responseBody nasaRSS
body, err := ioutil.ReadAll(resp.Body)
checkError(err)
xml.Unmarshal(body, &responseBody)
done <- responseBody
}
Unmarshal
Parses the XML-encoded data and stores the result in the value responseBody
which is of a new type created nasaRss
Processing the feed data
A struct was created to encapsulate the data.
Notice the fields map to each element within the RSS XML feed.
The purpose of this struct was to ensure
// nasaRSS declaring struct
type nasaRSS struct {
XMLName xml.Name `xml:"rss"`
Text string `xml:",chardata"`
Version string `xml:"version,attr"`
Channel struct {
Text string `xml:",chardata"`
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Language string `xml:"language"`
Image struct {
Text string `xml:",chardata"`
Title string `xml:"title"`
URL string `xml:"url"`
Link string `xml:"link"`
} `xml:"image"`
Item []struct {
Text string `xml:",chardata"`
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
} `xml:"item"`
TextInput struct {
Text string `xml:",chardata"`
Title string `xml:"title"`
Description string `xml:"description"`
Name string `xml:"name"`
Link string `xml:"link"`
} `xml:"textInput"`
} `xml:"channel"`
}
Description
<description><p><a href="https://apod.nasa.gov/apod/ap200621.html"
><img src="https://apod.nasa.gov/apod/calendar/S_200621.jpg"
align="left" alt="It may look like " border="0" /></a> It may look like </p><br clear="all"/>
</description>
The text inside the <description>
tag contains escaped HTML since it’s XML encoded.
This also contains an <img>
tag.
In order to extract the information out of the description
tag.
A regex was used to pull the img src (thumbnail) and description out.
func parseDescription(str string, opt string) string {
switch opt {
case "img":
r, _ := regexp.Compile(`<img src\s*=\s*"(.+?)"`)
return r.FindStringSubmatch(str)[1]
case "desc":
r, _ := regexp.Compile(`alt\s*=\s*"(.+?)"`)
return r.FindStringSubmatch(str)[1]
default:
panic("Invalid option for parsing description.")
}
}
In the end the extracted values look like.
Description = “It may look like”
Img src = https://apod.nasa.gov/apod/calendar/S_200621.jpg
Template
In order to generate the dynamic content inside an HTML page, the go package html/template
was used.
This package allowed dynamic injection of data to be inserted at runtime.
Struct
Another struct was used to help with the rendering of the HTML page.
type htmlRender struct {
Title string
Link string
ImgSrc string
Description string
}
From the below HTML code example.
Field | Template notation | Description |
---|---|---|
N/A | {{range $items := .}} |
Loop a list of items, in this case these are the entries taken from the XML feed |
Title | {{.Title}} |
Display the title of the individual item taken from the RSS feed |
Description | {{.Description}} |
Display the description content |
ImgSrc | {{.ImgSrc}} |
Used to display the image |
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"
integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI"
crossorigin="anonymous"></script>
<title>APOD</title>
</head>
<body>
<div class="container">
<p class="text-center"><a href="https://apod.nasa.gov/apod/" target="_blank"> Astronomy Picture of the Day</a></p>
<div class="row">
<div class="col-sm" style="width: 40rem;">
{{range $items := .}}
<div class="card">
<div class="card-header">
<b>{{.Title}}</b>
</div>
<div class="card-body">
<img src="{{.ImgSrc}}" class="rounded float-right"/>
<p class="card-text">{{.Description}}</p>
<a href="{{.Link}}" target="_blank" class="btn btn-primary btn-sm">{{.Title}}</a>
</div>
</div>
<p></p>
{{end}}
</div>
</div>
</div>
</body>
</html>
The HTML code also includes references to Bootstrap a library to enhance the UI.