Generating an Atom feed using FeedGenerator
See Atom feeds for a summary of the Atom specification.
The FeedGenerator library describes itself thus:
| FeedGenerator is a standalone version of Django’s feedgenerator module.
Dependencies
This library only uses the XML generator from the xml.sax standard library;
it doesn’t use the (undermaintained) Expat parser (or any other XML parser) in any way.
Installation
It is included in the Debian repos as package python3-feedgenerator.
The latest version is 2.2.1, but the version in Debian stable & Ubuntu 24.04 is 2.1.0. The diff mainly has to do with modernising the build, but there have also been some fixes in the upstream (Django) implementation.
To install the latest version, use a virtual environment:
python3 -m venv .venv
source .venv/bin/activate
pip install feedgenerator
Usage
To generate an Atom feed, create an Atom1Feed object:
import feedgenerator
feed = feedgenerator.Atom1Feed(
title="My Blog",
description="Rants & Ramblings by AN Author",
feed_guid="tag:example.com,2025-11:blog",
feed_url="https://www.example.com/feed.atom",
link="https://www.example.com/",
author_name="AN Author",
author_link="https://www.example.com/about.html",
feed_copyright="ⓒ 2025, AN Author",
categories=["Rants", "Ramblings"],
language="en",
)
Add entries with HTML content by calling add_item
after parsing the metadata and reading in the content for each entry:
first_post = """\
<h1>New year, new blog</h1>
<p>Let me tell you all about my blogging setup!</p>
"""
feed.add_item(
title="New year, new blog",
description="My blogging setup",
pubdate=datetime.datetime.fromisoformat("2025-01-01"),
updateddate=datetime.datetime.fromisoformat("2025-11-02"),
link="https://www.example.com/first-post/",
categories=["blogging", "ssg"],
content=first_post,
)
Write the feed to disk as Atom XML:
with open("feed.atom", "w") as f:
feed.write(f, "utf-8")
Output
The parameters for the Atom1Feed constructor and add_item
are mapped to the following XML elements/attributes in the output:
Feed
Implemented in Atom1Feed.add_root_elements:
title→titlesubtitleordescription→subtitlefeed_guidorlink→idfeed_url→hrefattr oflink[rel="self"]link→hrefattr oflink[rel="alternate"]author_name→author/nameauthor_link→author/urifeed_copyright→rightscategories→termattr ofcategoryelements (no scheme/label)language→xml:langattr of parent/feedelement- the timestamp of the newest post →
updated- The timestamp is converted to RFC3339 format, using the “Z” form if the post timestamp does not have a UTC offset.
Entry
Implemented in Atom1Feed.add_item_elements:
title→titledescription→summary[type="html"]- The following chars are escaped:
&,>,<(byxml.sax.saxutils). I.e.summaryhas character data and no children.
- The following chars are escaped:
pubdate→publishedupdateddateorpubdate→updatedlink→hrefoflink[rel="alternate"]categories→termattr ofcategoryelementscontent→content[type="html"]- The following chars are escaped:
&,>,<. So the provided string becomes the character data of thecontentelement, not a tree of child elements. - The alternative allowed by the Atom spec is to include the content as XML
inside an
entry/content[type="xhtml"]/divelement. But that is not supported by this library.
- The following chars are escaped:
unique_id→idif it exists. Otherwise,idis set to a tag URI constructed fromlinkandpubdate.- The tag URI looks like:
tag:{link.hostname},{pubdate.date}:{link.path} - Because permalinks may change, the tag URI should be generated once and stored.
- The tag URI looks like:
References
- How to make a good ID in Atom by Mark Pilgrim
- RFC 4151: Tag URIs
- taguri.org