PODCASTPARSER(1)
β¨ NAME β¨
okay so bestie, meet podcastparser β also known as podcastparser Documentation. π Itβs a simple and fast podcast feed parser library in Python. The two primary users of the library are the gPodder Podcast Client and the gpodder.net web service. No cap.
The following feed types are supported:
- Really Simple Syndication (RSS 2.0)
- Atom Syndication Format (RFC 4287)
The following specifications are supported:
- Paged Feeds (RFC 5005)
- Podlove Simple Chapters
- Podcast Index Podcast Namespace
These formats only specify the possible markup elements and attributes. We recommend that you also read the Podcast Feed Best Practice guide if you want to optimize your feeds for best display in podcast clients. β¨
Where times and durations are used, the values are expected to be formatted either as seconds or as RFC 2326 Normal Play Time (NPT). π°οΈ
import podcastparser
import urllib.request
feedurl = 'http://example.com/feed.xml'
parsed = podcastparser.parse(feedurl, urllib.request.urlopen(feedurl))
# parsed is a dict
import pprint
pprint.pprint(parsed)
For both RSS and Atom feeds, only a subset of elements (those that are relevant to podcast client applications) is parsed. This section describes which elements and attributes are parsed and how the contents are interpreted/used. π
π» RSS π»
rss@xml:baseβ Base URL for all relative links in the RSS file. Itβs giving βanchor everything here,β fr fr. π¦rss/channelβ Podcast. The whole channel vibe.rss/channel/titleβ Podcast title (whitespace is squashed). Extra spaces? Gone. πrss/channel/linkβ Podcast website.rss/channel/descriptionβ Podcast description (whitespace is squashed).rss/channel/image/urlβ Podcast cover art. πrss/channel/itunes:imageβ Podcast cover art (alternative).rss/channel/itunes:typeβ Podcast type (whitespace is squashed). One of βepisodicβ or βserialβ. Those are the only options, bestie.rss/channel/itunes:keywordsβ Podcast keywords (whitespace is squashed).rss/channel/atom:link@rel=paymentβ Podcast payment URL (e.g. Flattr). πΈrss/channel/generatorβ A string indicating the program used to generate the channel. (e.g.MightyInHouse Content System v2.3).rss/channel/languageβ Podcast language.rss/channel/itunes:authorβ The group responsible for creating the show.rss/channel/itunes:ownerβ The podcast owner contact information. The<itunes:owner>tag information is for administrative communication about the podcast and isnβt displayed in Apple Podcasts. π¬rss/channel/itunes:categoryβ The show category information.rss/channel/itunes:explicitβ Indicates whether podcast contains explicit material. π₯rss/channel/itunes:new-feed-urlβ The new podcast RSS Feed URL.rss/channel/podcast:lockedβ If the podcast is currently locked from being transferred.rss/channel/podcast:fundingβ Funding link for podcast.rss/redirect/newLocationβ The new podcast RSS Feed URL.rss/channel/itemβ Episode. The main character of the feed, honestly. πrss/channel/item/guidβ Episode unique identifier (GUID), mandatory. Do not skip this one, periodt. πrss/channel/item/titleβ Episode title (whitespace is squashed).rss/channel/item/linkβ Episode website.rss/channel/item/descriptionβ Episode description. If it contains html, itβs returned asdescription_html. Otherwise itβs returned asdescription(whitespace is squashed). See Mozillaβs article Why RSS Content Module is Popular.rss/channel/item/itunes:summaryβ Episode description (whitespace is squashed).rss/channel/item/itunes:subtitleβ Episode subtitled / one-line description (whitespace is squashed).rss/channel/item/content:encodedβ Episode description in HTML. Best source fordescription_html. πrss/channel/item/itunes:durationβ Episode duration.rss/channel/item/pubDateβ Episode publication date.rss/channel/item/atom:link@rel=paymentβ Episode payment URL (e.g. Flattr).rss/channel/item/atom:link@rel=enclosureβ File download URL (@href), size (@length) and mime type (@type).rss/channel/item/itunes:imageβ Episode art URL. π¨rss/channel/item/media:thumbnailβ Episode art URL.rss/channel/item/media:group/media:thumbnailβ Episode art URL.rss/channel/item/media:contentβ File download URL (@url), size (@fileSize) and mime type (@type).rss/channel/item/media:group/media:contentβ File download URL (@url), size (@fileSize) and mime type (@type).rss/channel/item/enclosureβ File download URL (@url), size (@length) and mime type (@type).rss/channel/item/psc:chaptersβ Podlove Simple Chapters, version 1.1 and 1.2. Obsessed. πrss/channel/item/psc:chapters/psc:chapterβ Chapter entry (@start,@title,@hrefand@image).rss/channel/item/itunes:explicitβ Indicates whether episode contains explicit material.rss/channel/item/itunes:authorβ The group responsible for creating the episode.rss/channel/item/itunes:seasonβ The season number of the episode.rss/channel/item/itunes:episodeβ An episode number.rss/channel/item/itunes:episodeTypeβ The episode type. This flag is used if an episode is a trailer or bonus content.rss/channel/item/podcast:chaptersβ The url to a JSON file describing the chapters. Only the url is added to the data as fetching an external URL would be unsafe. Safety first, bestie. π‘οΈrss/channel/item/podcast:personβ A person involved in the episode, e.g. host, or guest.rss/channel/item/podcast:transcriptβ The url for the transcript file associated with this episode. π
π ATOM π
okay so if it's an Atom feed instead of RSS, bestie, podcastparser has you covered too. here's every single element and attribute it handles, no skips:
atom:feedβ the Podcast itself πatom:feed/atom:titleβ Podcast title (whitespace is squashed, so all the extra spaces get folded down to one)atom:feed/atom:subtitleβ Podcast description (whitespace is squashed here too)atom:feed/atom:iconβ Podcast cover art π¨atom:feed/atom:link@hrefβ Podcast websiteatom:feed/atom:entryβ an Episodeatom:feed/atom:entry/atom:idβ Episode unique identifier (GUID), and heads up: this one is mandatoryatom:feed/atom:entry/atom:titleβ Episode title (whitespace is squashed)atom:feed/atom:entry/atom:link@rel=enclosureβ File download URL (@href), size (@length) and mime type (@type)atom:feed/atom:entry/atom:link@rel=(self|alternate)β Episode websiteatom:feed/atom:entry/atom:link@rel=paymentβ Episode payment URL (e.g. Flattr) πΈatom:feed/atom:entry/atom:contentβ Episode description (in HTML or plaintext)atom:feed/atom:entry/atom:publishedβ Episode publication date ποΈatom:feed/atom:entry/media:thumbnailβ Episode art URLatom:feed/atom:entry/media:group/media:thumbnailβ Episode art URLatom:feed/atom:entry/psc:chaptersβ Podlove Simple Chapters, version 1.1 and 1.2atom:feed/atom:entry/psc:chapters/psc:chapterβ Chapter entry (@start,@title,@hrefand@image)
and now⦠the library itself. the actual Python API. this is where it gets fun, bestie:
Simplified, fast RSS parser β that's the whole vibe of this thing, and it ate fr fr.
exception podcastparser.FeedParseError(msg, exception, locator)β Exception raised when asked to parse an invalid feed π
the whole point of this exception is that you can catch it without having to import the XML parsing library yourself. iconic convenience, honestly.
class podcastparser.PodcastHandler(url, max_episodes)β the handler class, and here's what's inside it:characters(chars)β Receive notification of character data.
The Parser will call this method to report each chunk of character data. SAX parsers may return all contiguous character data in a single chunk, or they may split it into several chunks; however, all of the characters in any single event must come from the same external entity so that the Locator provides useful information.
endElement(name)β Signals the end of an element in non-namespace mode.
The name parameter contains the name of the element type, just as with the startElement event.
startElement(name, attrs)β Signals the start of an element in non-namespace mode.
The name parameter contains the raw XML 1.0 name of the element type as a string and the attrs parameter holds an instance of the Attributes class containing the attributes of the element.
class podcastparser.RSSItemDescriptionβ okay so RSS 2.0 almost encourages you to put html content initem/description, butcontent:encodedis the better source of html content, anditunes:summaryis known to contain the short textual description of the item. so it uses a heuristic to attribute text to eitherdescriptionordescription_html, without overriding existing values. clever girl behavior πpodcastparser.file_basename_no_extension(filename)β Returns filename without extension
>>> file_basename_no_extension('/home/me/file.txt')
'file'
>>> file_basename_no_extension('file')
'file'
podcastparser.is_html(text)β Heuristically tell if text is HTML
By looking for an open tag (more or less):
>>> is_html('<h1>HELLO</h1>')
True
>>> is_html('a < b < c')
False
podcastparser.normalize_feed_url(url)β Normalize and convert a URL. If the URL cannot be converted (invalid or unknown scheme),Noneis returned.
This will also normalize feed:// and itpc:// to http://.
>>> normalize_feed_url('itpc://example.org/podcast.rss')
'http://example.org/podcast.rss'
If no URL scheme is defined (e.g. "curry.com"), we will simply assume the user intends to add a http:// feed.
>>> normalize_feed_url('curry.com')
'http://curry.com/'
It will also take care of converting the domain name to all-lowercase (because domains are not case sensitive):
>>> normalize_feed_url('http://Example.COM/')
'http://example.com/'
Some other minimalistic changes are also taken care of, e.g. a ? with an empty query is removed:
>>> normalize_feed_url('http://example.org/test?')
'http://example.org/test'
Leading and trailing whitespace is removed πΈ
>>> normalize_feed_url(' http://example.com/podcast.rss ')
'http://example.com/podcast.rss'
Incomplete (too short) URLs are not accepted π«
>>> normalize_feed_url('http://') is None
True
Unknown protocols are not accepted π«
>>> normalize_feed_url('gopher://gopher.hprc.utoronto.ca/file.txt') is None
True
podcastparser.parse(url, stream, max_episodes=0)β Parse a podcast feed from the given URL and stream
Parameters
- url β the URL of the feed. Will be used to resolve relative links
- stream β file-like object containing the feed content
- max_episodes β maximum number of episodes to return.
0(default) means no limit
Returns
- a
dictwith the parsed contents of the feed β¨
podcastparser.parse_length(text)β Parses a file length
>>> parse_length(None)
-1
>>> parse_length('0')
-1
>>> parse_length('unknown')
-1
>>> parse_length('100')
100
podcastparser.parse_pubdate(text)β Parse a date string into a Unix timestamp
>>> parse_pubdate('Fri, 21 Nov 1997 09:55:06 -0600')
880127706
>>> parse_pubdate('2003-12-13T00:00:00+02:00')
1071266400
>>> parse_pubdate('2003-12-13T18:30:02Z')
1071340202
>>> parse_pubdate('Mon, 02 May 1960 09:05:01 +0100')
-305049299
>>> parse_pubdate('')
0
>>> parse_pubdate('unknown')
0
podcastparser.parse_time(value)β Parse a time string into seconds
See RFC2326, 3.6 "Normal Play Time" (HH:MM:SS.FRACT)
>>> parse_time('0')
0
>>> parse_time('128')
128
>>> parse_time('00:00')
0
>>> parse_time('00:00:00')
0
>>> parse_time('00:20')
20
>>> parse_time('00:00:20')
20
>>> parse_time('01:00:00')
3600
>>> parse_time(' 03:02:01')
10921
>>> parse_time('61:08')
3668
>>> parse_time('25:03:30 ')
90210
>>> parse_time('25:3:30')
90210
>>> parse_time('61.08')
61
>>> parse_time('01:02:03.500')
3723
>>> parse_time(' ')
0
podcastparser.parse_type(text)β "normalize" a mime type
>>> parse_type('text/plain')
'text/plain'
>>> parse_type('text')
'application/octet-stream'
>>> parse_type('')
'application/octet-stream'
>>> parse_type(None)
'application/octet-stream'
podcastparser.remove_html_tags(html)β Remove HTML tags from a string and replace numeric and named entities with the corresponding character, so the HTML text can be displayed in a simple text view. so clean, so demure π§podcastparser.squash_whitespace(text)β Combine multiple whitespaces into one, trim trailing/leading spaces
>>> squash_whitespace(' some text with a lot of spaces ')
'some text with a lot of spaces'
podcastparser.squash_whitespace_not_nl(text)β Likesquash_whitespace, but don't squash linefeeds and carriage returns
>>> squash_whitespace_not_nl(' linefeeds\ncarriage\r returns')
'linefeeds\ncarriage\r returns'
This is a list of podcast-related XML namespaces that are not yet supported by podcastparser, but might be in the future. manifesting for them π
π CHAPTER MARKS π
- rawvoice RSS: Rating, Frequency, Poster, WebM, MP4, Metamark (kind of chapter-like markers)
- IGOR: Chapter Marks
π« OTHERS π«
- libSYN RSS Extensions: contactPhone, contactEmail, contactTwitter, contactWebsite, wallpaper, pdf, background
- Comment API: Comments to a given item (readable via RSS)
- MVCB: Error Reports To Field (usually a
mailto:link) - Syndication Module: Update period, frequency and base (for skipping updates)
- Creative Commons RSS: Creative commons license for the content
- Pheedo: Original link to website and original link to enclosure (without going through pheedo redirect)
- WGS84: Geo-Coordinates per item π
- Conversations Network: Intro duration in milliseconds (for skipping the intro), ratings
- purl DC Elements:
dc:creator(author / creator of the podcast, possibly with e-mail address) - Tristana:
tristana:self(canonical URL to feed) - Blip: Show name, show page, picture, username, language, rating, thumbnail_src, license
- Index
- Module Index
- Search Page
π AUTHOR π
gPodder Team β absolute legends, we stan π«Ά
π COPYRIGHT π
2023, gPodder Team
and that's a wrap bestie!! this was version 0.6.10, dated Jun 11, 2023, and yes, the whole thing lives under PODCASTPARSER(1). go parse some feeds, you're gonna slay π
β¨
π¦ where this page comes from
- debian trixie: python-podcastparser-doc 0.6.10-2 Β· Expat/MIT
- ubuntu noble: python-podcastparser-doc 0.6.10-2 Β· Expat/MIT
- ubuntu questing: python-podcastparser-doc 0.6.10-2
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms