Feed Generator (ATOM, RSS, Podcasts)
feedgen is a Python library (version 1.0.0) for generating web feeds in ATOM and RSS formats, including support for podcast extensions. It is a standalone evolution of Django's `feedgenerator` module, licensed under both FreeBSD and LGPLv3+. The library maintains an active development status, with its latest stable release on December 25, 2023, and continues to be a robust tool for programmatic feed creation.
Common errors
-
ValueError: unconverted data remains: ' GMT'
cause Attempting to set a date string that `dateutil.parser` (used internally by feedgen) cannot fully parse, often due to non-standard format or trailing text.fixEnsure date strings are in a standard format (e.g., ISO 8601) and include timezone information, or pass a `datetime.datetime` object with `tzinfo` set. -
XML Syntax Error: Entity '...' not defined
cause Special characters like `&`, `<`, `>` are not properly escaped in the feed content, violating XML rules.fixLet `feedgen` handle content by passing plain strings to `fe.content()` or `fe.fe.summary()`. It will automatically escape these characters. Avoid manually constructing XML snippets within content fields. -
Feed reader shows old items or fetches repeatedly.
cause This usually indicates missing or incorrect `pubDate`/`lastBuildDate` with timezone information, leading the reader to believe content is new or its update status is ambiguous.fixVerify that `fg.lastBuildDate()` and `fe.published()`/`fe.updated()` are always set with timezone-aware `datetime` objects, preferably UTC, e.g., `datetime.datetime.now(datetime.timezone.utc)`.
Warnings
- breaking Version 1.0.0 removed official support for Python 2. While older versions might work, Python 2 is no longer tested or maintained.
- gotcha Date and time fields (`pubDate`, `lastBuildDate`, `published`, `updated`) *must* include timezone information for proper parsing by feed readers. Without it, readers may incorrectly interpret dates (e.g., as future dates) or repeatedly fetch content.
- gotcha RSS feeds require a `guid` (Global Unique Identifier) for each entry to uniquely identify it. While `feedgen` provides a `guid()` method, ensure the value is truly unique and consistent for the item across feed updates. For items that are not permalinks, set `isPermaLink=False`.
- gotcha Improperly formatted XML can lead to feed validation errors or prevent feed readers from parsing your content. This often happens with special characters in content or missing required fields.
Install
-
pip install feedgen
Imports
- FeedGenerator
from feedgen.feed import FeedGenerator
- FeedEntry
from feedgen.entry import FeedEntry
Quickstart
from feedgen.feed import FeedGenerator
import datetime
fg = FeedGenerator()
fg.id('http://example.com/pages/1')
fg.title('My Example Feed')
fg.author({'name': 'John Doe', 'email': 'john.doe@example.com'})
fg.link(href='http://example.com', rel='alternate')
fg.link(href='http://example.com/feed.xml', rel='self')
fg.language('en')
fg.description('A fantastic example feed generated by feedgen.')
fg.lastBuildDate(datetime.datetime.now(datetime.timezone.utc))
# Add an entry
fe = fg.add_entry()
fe.id('http://example.com/pages/1/article1')
fe.title('First Article Title')
fe.link(href='http://example.com/articles/1')
fe.published(datetime.datetime(2023, 1, 10, 10, 0, 0, tzinfo=datetime.timezone.utc))
fe.updated(datetime.datetime(2023, 1, 15, 12, 30, 0, tzinfo=datetime.timezone.utc))
fe.summary('This is a summary of the first article.')
fe.content('This is the full content of the first article, potentially with <b>HTML</b>.')
fe.author({'name': 'Jane Doe', 'email': 'jane.doe@example.com'})
# Add another entry
fe2 = fg.add_entry()
fe2.id('http://example.com/pages/1/article2')
fe2.title('Second Article Title')
fe2.link(href='http://example.com/articles/2')
fe2.published(datetime.datetime(2023, 2, 1, 9, 0, 0, tzinfo=datetime.timezone.utc))
fe2.updated(datetime.datetime(2023, 2, 5, 11, 45, 0, tzinfo=datetime.timezone.utc))
fe2.summary('Summary of the second article.')
# Generate and print the RSS feed
print(fg.rss_str(pretty=True).decode('utf-8'))
# Generate and print the ATOM feed
# print(fg.atom_str(pretty=True).decode('utf-8'))