generating an RSS feed with python bottle

march 18, 2021
#bottle #rss #webdev

After a few months of quietly running my blog as practice, I want to start sharing my articles with other people. I looked over my favorite gamedev communities and saw that GameDev.net apparently allows you to syndicate a blog through RSS. I never thought about making an RSS feed, so why not?

what is RSS?


Before the massive centralized content platforms came into the mainstream, the internet was more like a constellation of individual websites. In lieue of algorithm-driven feeds and push notifications from major social media, RSS was designed to bring content from scattered websites into one place.

RSS and its predecessors have been around since the 90s. RSS 2.0 (what blessfrey.me uses) was published in 2002. Even through it's old and falling in popularity, it's still used by some large aggregators today like Google News and Spotify.

Here's a few examples from around the internet, a mix of large + small news websites and forums:

what goes into an RSS feed?


RSS files themselves are written in XML. They should contain the latest 10-15 entries along with things like their title, link, summary, date, and author.

Blogging platforms like WordPress already take care of the RSS feed, but there's no shortage of third-party RSS creators on the internet. Since I have already written code to display + summarize my articles on the diary page, the 'latest' box in the diary's sidebar, and the 'news' box on the index page, I figure I can format the data one more time into an XML file.

Here's truncated version of blessfrey.me's feed as an example (also available on Pastebin:

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>blessfrey.me</title>
<link>https://www.blessfrey.me/</link>
<description>chimchooree's dev space</description>
<language>en-us</language>
<webMaster>chimchooree@mail.com (chimchooree)</webMaster>
<item>
<title>making an rss feed</title>
<link>https://www.blessfrey.me/diary/entries//diary/entries/210318</link>
<description>After a few months of quietly running my blog as practice, I want to start sharing my articles with ... </description>
<pubDate>Thu, 18 Mar 2021 8:05 CST</pubDate>
<guid>https://www.blessfrey.me/diary/entries//diary/entries/210318</guid>
</item>
</channel>
</rss>

I'll explain each tag, but they are also described on the RSS Advisory Board's Best Practices Profile. There are more tags, too, so research documentation + examples to see what suits your website.

XML declaration


Identifies the document as XML and defines the version + character encoding. It's required and must be the first line.

RSS


The top-level element that defines version number. It's required.

channel


Nested within the RSS tags and describes the RSS feed. It's required.

There's some tags nested within the channel. Title, Link, and Description are required.


item


Nested within the channel. Each article will be defined with the item.

There's some tags nested within the item. Title, Link, and Description are required.


how to make an RSS feed


I'm generating the RSS every time I update my website. That way, it always exists on the server and can be served as a static file. If I used a template, the RSS file would not exist unless it was accessed. I'm not sure if that would be an issue and haven't experimented.

Since Bottle is just Python, I can generate the file similar to how I format my articles into diary snippets, pages, and headlines.

I'll share the main code, but the full code can be viewed on Pastebin.

code example


def make_rss():
loc = 'diary/entries/'
info = {'items': list_items(gather_and_sort(loc)[0:15])}

# Return list of items
def list_items(articles):
f_name = "static/xml/blessfrey.xml" # the RSS file
loc2 = 'https://www.blessfrey.me/'
loc = 'diary/entries/'
loc3 = loc2 + loc
result = []

for article in articles:
path = loc + article
text = []
a = []
length = 0
text = article2list(article, loc)
a.append(find_title(text))
a.append(find_url(path))
a.append(clean_tags(prepare_rss_summary(text, path)))
a.append(find_timestamp(text))
result.append(a)

clear_file(f_name)
f = open(f_name, 'w')
f.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + '\n')
f.write("<rss version=\"2.0\">" + '\n')
f.write("<channel>" + '\n')
f.write("<title>blessfrey.me</title>" + '\n')
f.write("<link>https://www.blessfrey.me/</link>" + '\n')
f.write("<description>chimchooree's dev space</description>" + '\n')
f.write("<language>en-us</language>" + '\n')
f.write("<webMaster>chimchooree@mail.com (chimchooree)</webMaster>" + '\n')

for r in result:
f.write("<item>" + '\n')
f.write("<title>" + r[0] + "</title>" + '\n')
f.write("<link>" + loc3 + r[1] + "</link>" + '\n')
f.write("<description>" + r[2] + "</description>" + '\n')
code = r[1].replace(loc,'')
code = code.replace('/','')
f.write("<pubDate>" + format_rss_time(code) + "</pubDate>" + '\n')
f.write("<guid>" + loc3 + r[1] + "</guid>" + '\n')
f.write("</item>" + '\n')

f.write("</channel>" + '\n')
f.write("</rss>" + '\n')
f.close()

return result

# Serve XML
@route('/static/xml/<filename:path>')
def serve_xml(filename):
return static_file(filename, root='static/xml', mimetype='text/xml')

## Main ##

if __name__ == '__main__':
make_rss()
run(host='127.0.0.1', port=9001)

double-check


The RSS Advisory Board and W3C have feed validation services that can check the syntax of Atom or RSS feeds. It's nice to check but don't feel pressured to meet all the recommendations if they don't suit your needs.

double-check


Now I have an RSS feed, available at https:/blessfrey.me/static/xml/blessfrey.xml. Feel free to use it if you prefer to read through an aggregator and contact me if there's technical problems.

Last updated March 19, 2021