Skip to main content

RSS Parser

Downloads Downloads Downloads

PyPI version Python versions Wheel status License

Docs CI PyPi publish

About

rss-parser is a type-safe Python RSS/Atom parsing module built using pydantic and xmltodict.

Installation

pip install rss-parser

or

git clone https://github.com/dhvcc/rss-parser.git
cd rss-parser
poetry build
pip install dist/*.whl

V1 -> V2 Migration

  • The Parser class has been renamed to RSSParser
  • Models for RSS-specific schemas have been moved from rss_parser.models to rss_parser.models.rss. Generic types remain unchanged
  • Date parsing has been improved and now uses pydantic's validator instead of email.utils, producing better datetime objects where it previously defaulted to str

V2 -> V3 Migration

rss-parser 3.x upgrades the runtime models to Pydantic v2. Highlights:

  • New default models now inherit from pydantic.BaseModel v2 and use model_validate/model_dump. If you extend our classes, switch from dict()/json() to model_dump()/model_dump_json().
  • Legacy compatibility lives under rss_parser.models.legacy. Point your custom parser at the legacy schema if you must stay on the v1 API surface.
  • Collections: list-like XML fields now use OnlyList[...] directly with an automatic default_factory so that attributes are always lists (no more Optional[OnlyList[T]] = Field(..., default=[])). Update custom schemas accordingly.
  • Custom hooks: if you relied on rss_parser.pydantic_proxy, import it from rss_parser.models.legacy.pydantic_proxy. The top-level module only re-exports it for backwards compatibility.

See the “Legacy Models” section below for sample snippets showing how to stay on the older types. Tests in this repo cover both tracks to guarantee matching output.

Legacy Models

Pydantic v1-based models are still available under rss_parser.models.legacy. They retain the previous behaviour and re-export the import_v1_pydantic helper as rss_parser.models.legacy.pydantic_proxy.import_v1_pydantic. You can continue to use them by pointing your parser at the legacy schema:

from rss_parser import RSSParser
from rss_parser.models.legacy.rss import RSS as LegacyRSS

class LegacyRSSParser(RSSParser):
    schema = LegacyRSS

Tests in this repository run against both the v2 and legacy models to ensure parity.

Usage

Quickstart

NOTE: For parsing Atom, use AtomParser

from rss_parser import RSSParser
from requests import get  # noqa

rss_url = "https://rss.art19.com/apology-line"
response = get(rss_url)

rss = RSSParser.parse(response.text)

# Print out rss meta data
print("Language", rss.channel.language)
print("RSS", rss.version)

# Iteratively print feed items
for item in rss.channel.items:
    print(item.title)
    print(item.description[:50])

# Language en
# RSS 2.0
# Wondery Presents - Flipping The Bird: Elon vs Twitter
# <p>When Elon Musk posted a video of himself arrivi
# Introducing: The Apology Line
# <p>If you could call a number and say you’re sorry

Here we can see that the description still contains <p> tags - this is because it's wrapped in CDATA like so:

<![CDATA[<p>If you could call ...</p>]]>

Overriding Schema

If you want to customize the schema or provide a custom one, use the schema keyword argument of the parser:

from rss_parser import RSSParser
from rss_parser.models import XMLBaseModel
from rss_parser.models.rss import RSS
from rss_parser.models.types import Tag


class CustomSchema(RSS, XMLBaseModel):
    channel: None = None  # Removing previous channel field
    custom: Tag[str]


with open("tests/samples/custom.xml") as f:
    data = f.read()

rss = RSSParser.parse(data, schema=CustomSchema)

print("RSS", rss.version)
print("Custom", rss.custom)

# RSS 2.0
# Custom Custom tag data

xmltodict

This library uses xmltodict to parse XML data. You can find the detailed documentation here.

The key thing to understand is that your data is processed into dictionaries.

For example, this XML:

<tag>content</tag>

will result in the following dictionary:

{
    "tag": "content"
}

However, when handling attributes, the content of the tag will also be a dictionary:

<tag attr="1" data-value="data">data</tag>

This becomes:

{
    "tag": {
        "@attr": "1",
        "@data-value": "data",
        "#text": "content"
    }
}

Multiple children of a tag will be placed into a list:

<div>
    <tag>content</tag>
    <tag>content2</tag>
</div>

This results in a list:

[
    { "tag": "content" },
    { "tag": "content" },
]

If you don't want to deal with these conditions and want to parse something always as a list, please use rss_parser.models.types.only_list.OnlyList like we did in Channel:

from typing import Optional

from pydantic import Field

from rss_parser.models.rss.item import Item
from rss_parser.models.types.only_list import OnlyList
from rss_parser.models.types.tag import Tag
...


class OptionalChannelElementsMixin(...):
    ...
    items: Optional[OnlyList[Tag[Item]]] = Field(alias="item", default_factory=list)

Tag Field

This is a generic field that handles tags as raw data or as a dictionary returned with attributes.

Example:

from rss_parser.models import XMLBaseModel
from rss_parser.models.types.tag import Tag


class Model(XMLBaseModel):
    width: Tag[int]
    category: Tag[str]


m = Model(
    width=48,
    category={"@someAttribute": "https://example.com", "#text": "valid string"},
)

# Content value is an integer, as per the generic type
assert m.width.content == 48

assert type(m.width), type(m.width.content) == (Tag[int], int)

# The attributes are empty by default
assert m.width.attributes == {} # But are populated when provided.

# Note that the @ symbol is trimmed from the beginning and the name is converted to snake_case
assert m.category.attributes == {'some_attribute': 'https://example.com'}

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Install dependencies with poetry install (pip install poetry).

Using pre-commit is highly recommended. To install hooks, run:

poetry run pre-commit install -t=pre-commit -t=pre-push

License

GPLv3

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rss_parser-3.0.0.tar.gz (28.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rss_parser-3.0.0-py3-none-any.whl (43.0 kB view details)

Uploaded Python 3

File details

Details for the file rss_parser-3.0.0.tar.gz.

File metadata

  • Download URL: rss_parser-3.0.0.tar.gz
  • Upload date:
  • Size: 28.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.10.0 Linux/6.17.0-1020-azure

File hashes

Hashes for rss_parser-3.0.0.tar.gz
Algorithm Hash digest
SHA256 87b0b4d3c51442c90b60d35998522ac1a41e75c96c7be717583fe605187d2299
MD5 557ba3cd4c5e5f90e8ca8db44ef5c72e
BLAKE2b-256 f8ed85f7aec80625cfafdbe4a0ff4ed447ab7e723fa6d8e9d8beae8f77d74b68

See more details on using hashes here.

File details

Details for the file rss_parser-3.0.0-py3-none-any.whl.

File metadata

  • Download URL: rss_parser-3.0.0-py3-none-any.whl
  • Upload date:
  • Size: 43.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.10.0 Linux/6.17.0-1020-azure

File hashes

Hashes for rss_parser-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b5ad248c5a114d4556cd4e501fe6d3f9285ad171019acb0c2edb8c57662cb34a
MD5 49d6c6361af470374f91691054802f91
BLAKE2b-256 8cfd6eea276a9022645193be9f8262a3501274676fd3d926659c68e04d18251e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page