{"id":34682849,"url":"https://github.com/bug-ops/feedparser-rs","last_synced_at":"2026-04-02T18:03:13.555Z","repository":{"id":328702209,"uuid":"1116391587","full_name":"bug-ops/feedparser-rs","owner":"bug-ops","description":"High-performance RSS/Atom/JSON Feed parser for Rust with Python and Node.js bindings","archived":false,"fork":false,"pushed_at":"2026-03-24T22:53:45.000Z","size":1344,"stargazers_count":8,"open_issues_count":6,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-25T23:25:19.657Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bug-ops.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-14T19:09:11.000Z","updated_at":"2026-03-25T09:08:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bug-ops/feedparser-rs","commit_stats":null,"previous_names":["bug-ops/feedparser-rs"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/bug-ops/feedparser-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Ffeedparser-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Ffeedparser-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Ffeedparser-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Ffeedparser-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bug-ops","download_url":"https://codeload.github.com/bug-ops/feedparser-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Ffeedparser-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31312744,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-24T21:17:11.585Z","updated_at":"2026-04-02T18:03:13.548Z","avatar_url":"https://github.com/bug-ops.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# feedparser-rs\n\n[![Crates.io](https://img.shields.io/crates/v/feedparser-rs)](https://crates.io/crates/feedparser-rs)\n[![docs.rs](https://img.shields.io/docsrs/feedparser-rs)](https://docs.rs/feedparser-rs)\n[![PyPI](https://img.shields.io/pypi/v/feedparser-rs)](https://pypi.org/project/feedparser-rs/)\n[![npm](https://img.shields.io/npm/v/feedparser-rs)](https://www.npmjs.com/package/feedparser-rs)\n[![CI](https://img.shields.io/github/actions/workflow/status/bug-ops/feedparser-rs/ci.yml?branch=main)](https://github.com/bug-ops/feedparser-rs/actions)\n[![codecov](https://codecov.io/gh/bug-ops/feedparser-rs/graph/badge.svg)](https://codecov.io/gh/bug-ops/feedparser-rs)\n[![MSRV](https://img.shields.io/badge/MSRV-1.88.0-blue)](https://blog.rust-lang.org/)\n[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue)](LICENSE-MIT)\n\nHigh-performance RSS/Atom/JSON Feed parser written in Rust, with Python and Node.js bindings. A drop-in replacement for Python [feedparser](https://github.com/kurtmckee/feedparser) that is 90-100x faster.\n\n## Features\n\n- **Multi-format support** -- RSS 0.9x, 1.0, 2.0 / Atom 0.3, 1.0 / JSON Feed 1.0, 1.1\n- **Tolerant parsing** -- Handles malformed feeds gracefully with the `bozo` flag pattern, propagated at both feed and entry level\n- **HTTP fetching** -- Built-in URL fetching with compression (gzip, deflate, brotli) and conditional GET (ETag/Last-Modified)\n- **Podcast support** -- iTunes and Podcast 2.0 namespace extensions\n- **Security** -- DoS protection via `ParserLimits`, SSRF protection, input size validation\n- **Multi-language bindings** -- Native Python (PyO3) and Node.js (napi-rs) bindings\n- **feedparser drop-in** -- Dict-style access, field aliases, same API patterns as Python feedparser\n\n## Supported Formats\n\n| Format | Versions | Status |\n|--------|----------|--------|\n| RSS | 0.90, 0.91, 0.92, 1.0, 2.0 | Full support |\n| Atom | 0.3, 1.0 | Full support |\n| JSON Feed | 1.0, 1.1 | Full support |\n\n### Namespace Extensions\n\n| Namespace | Description |\n|-----------|-------------|\n| Dublin Core | Creator, date, rights metadata |\n| Content | Encoded HTML content |\n| Media RSS | Media attachments and metadata |\n| iTunes | Podcast metadata (author, duration, explicit) |\n| Podcast 2.0 | Chapters, transcripts, funding |\n| Syndication | Update schedule (period, frequency, base) |\n| GeoRSS | Geographic location data (point, line, polygon, box) |\n| Creative Commons | License information with `rel=\"license\"` links |\n| Slash | Comment count (`slash:comments`) |\n| WFW | Comment feed URL (`wfw:commentRss`) |\n| Atom Threading (thr:) | In-reply-to, reply count, reply datetime (RFC 4685) |\n\n## Installation\n\n### Rust\n\n```bash\ncargo add feedparser-rs\n```\n\nOr add to your `Cargo.toml`:\n\n```toml\n[dependencies]\nfeedparser-rs = \"0.5.0\"\n```\n\n\u003e [!IMPORTANT]\n\u003e Requires Rust 1.88.0 or later (edition 2024).\n\n### Python\n\n```bash\npip install feedparser-rs\n```\n\n\u003e [!NOTE]\n\u003e Requires Python 3.10 or later.\n\n### Node.js\n\n```bash\nnpm install feedparser-rs\n# or\npnpm add feedparser-rs\n```\n\n\u003e [!NOTE]\n\u003e Requires Node.js 18 or later.\n\n## Usage\n\n### Rust\n\n```rust\nuse feedparser_rs::parse;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let xml = r#\"\n        \u003c?xml version=\"1.0\"?\u003e\n        \u003crss version=\"2.0\"\u003e\n            \u003cchannel\u003e\n                \u003ctitle\u003eExample Feed\u003c/title\u003e\n                \u003clink\u003ehttps://example.com\u003c/link\u003e\n                \u003citem\u003e\n                    \u003ctitle\u003eFirst Post\u003c/title\u003e\n                    \u003clink\u003ehttps://example.com/post/1\u003c/link\u003e\n                \u003c/item\u003e\n            \u003c/channel\u003e\n        \u003c/rss\u003e\n    \"#;\n\n    let feed = parse(xml.as_bytes())?;\n\n    println!(\"Version: {}\", feed.version.as_str());  // \"rss20\"\n    println!(\"Title: {:?}\", feed.feed.title);\n    println!(\"Entries: {}\", feed.entries.len());\n\n    for entry in \u0026feed.entries {\n        println!(\"  - {:?}\", entry.title);\n    }\n\n    Ok(())\n}\n```\n\n#### Fetching from URL\n\n```rust\nuse feedparser_rs::fetch_and_parse;\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let feed = fetch_and_parse(\"https://example.com/feed.xml\")?;\n    println!(\"Fetched {} entries\", feed.entries.len());\n    Ok(())\n}\n```\n\n\u003e [!TIP]\n\u003e Use `fetch_and_parse` for URL fetching with automatic compression handling (gzip, deflate, brotli).\n\n### Python\n\n```python\nimport feedparser_rs as feedparser  # Drop-in replacement\n\n# Parse from bytes, string, or URL (auto-detected)\nd = feedparser.parse(b'\u003crss\u003e...\u003c/rss\u003e')\nd = feedparser.parse('https://example.com/feed.xml')  # URL auto-detected\n\n# Attribute-style access\nprint(d.version)       # 'rss20'\nprint(d.feed.title)\nprint(d.bozo)          # True if parsing had issues\n\n# Dict-style access (feedparser-compatible)\nprint(d['feed']['title'])\nprint(d['entries'][0]['link'])\n\n# Deprecated field aliases work\nprint(d.feed.description)  # -\u003e d.feed.subtitle\nprint(d.channel.title)     # -\u003e d.feed.title\n```\n\n\u003e [!NOTE]\n\u003e Python bindings provide full feedparser compatibility: dict-style access, field aliases, and `time.struct_time` for date fields.\n\n### Node.js\n\n```javascript\nimport { parse, fetchAndParse } from 'feedparser-rs';\n\n// Parse from string\nconst feed = parse('\u003crss version=\"2.0\"\u003e...\u003c/rss\u003e');\nconsole.log(feed.version);  // 'rss20'\nconsole.log(feed.feed.title);\nconsole.log(feed.entries.length);\n\n// Fetch from URL\nconst remoteFeed = await fetchAndParse('https://example.com/feed.xml');\n```\n\nSee [Node.js API documentation](crates/feedparser-rs-node/README.md) for complete reference.\n\n## Cargo Features\n\n| Feature | Description | Default |\n|---------|-------------|---------|\n| `http` | Enable URL fetching with reqwest (gzip/deflate/brotli support) | Yes |\n\nTo disable HTTP support and reduce dependencies:\n\n```toml\n[dependencies]\nfeedparser-rs = { version = \"0.5.0\", default-features = false }\n```\n\n## Workspace Structure\n\n| Crate | Description | Package |\n|-------|-------------|---------|\n| [`feedparser-rs`](crates/feedparser-rs-core) | Core Rust parser | [crates.io](https://crates.io/crates/feedparser-rs) |\n| [`feedparser-rs-node`](crates/feedparser-rs-node) | Node.js bindings | [npm](https://www.npmjs.com/package/feedparser-rs) |\n| [`feedparser-rs-py`](crates/feedparser-rs-py) | Python bindings | [PyPI](https://pypi.org/project/feedparser-rs) |\n\n## Development\n\n```bash\n# Install cargo-make\ncargo install cargo-make\n\n# Run all checks (format, lint, test)\ncargo make ci-all\n\n# Run tests with coverage\ncargo make coverage\n\n# Run benchmarks\ncargo make bench\n```\n\nSee all available tasks:\n\n```bash\ncargo make --list-all-steps\n```\n\n## Benchmarks\n\nMeasured on Apple M1 Pro, parsing real-world RSS feeds:\n\n| Feed Size | Time | Throughput |\n|-----------|------|------------|\n| Small (2 KB) | **10.7 us** | 187 MB/s |\n| Medium (20 KB) | **93.6 us** | 214 MB/s |\n| Large (200 KB) | **939 us** | 213 MB/s |\n\nFormat detection: **128 ns** (near-instant)\n\n### vs Python feedparser\n\n| Operation | feedparser-rs | Python feedparser | Speedup |\n|-----------|---------------|-------------------|---------|\n| Parse 20 KB RSS | 0.09 ms | 8.5 ms | **94x** |\n| Parse 200 KB RSS | 0.94 ms | 85 ms | **90x** |\n\n\u003e [!TIP]\n\u003e Run your own benchmarks with `cargo bench` or compare against Python with `cargo make bench-compare`.\n\n## MSRV Policy\n\nMinimum Supported Rust Version: **1.88.0** (edition 2024).\n\nMSRV increases are considered breaking changes and will result in a minor version bump.\n\n## License\n\nLicensed under either of:\n\n- [Apache License, Version 2.0](LICENSE-APACHE)\n- [MIT License](LICENSE-MIT)\n\nat your option.\n\n## Contributing\n\nContributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) before submitting a pull request.\n\nThis project follows the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbug-ops%2Ffeedparser-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbug-ops%2Ffeedparser-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbug-ops%2Ffeedparser-rs/lists"}