This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 0.1.7 instead.
pyfinews
pyfinews is a lightweight Python library for discovering financial-news headlines through Google News RSS. It provides a small client API, configurable financial-domain taxonomy, publisher-priority query planning, date filtering, and normalized article models.
The RSS provider uses Python's standard library for HTTP transport and feedparser for RSS/Atom parsing. Optional article downloading and main-text extraction are handled by Trafilatura. feedparser is installed automatically as a runtime dependency.
Features
- General financial-news discovery across configurable domains.
- Topic-specific discovery for tickers, companies, sectors, macro topics, and search phrases.
- Configurable RSS request budget through
query_limit. - Publisher selection ordered by ascending
priority(priority1is highest). - Multiple domain terms combined into a single OR expression per publisher/domain combination.
- Configurable publisher and domain JSON files.
- Lookback filtering and newest-first sorting.
- No article deduplication: repeated items returned by Google News or overlapping queries are preserved.
- Testable transport and query-planning layers.
- Optional Google News RSS URL resolution to publisher URLs.
- Optional article main-text extraction with Trafilatura.
Requirements
- Python 3.12 or newer.
uvis recommended for environment and dependency management.
Installation
Install from a local checkout:
uv sync --extra dev
Install from a package index if published:
uv add pyfinews
Optional article-text extraction
Article text extraction is an optional dependency so headline discovery does not need to install the extraction stack. From a source checkout, install it with:
uv sync --extra article-text
Or, when installing the published package, use the article-text extra. Then:
from pyfinews import NewsClient
with NewsClient() as news:
articles = news.search('Apple earnings', max_articles=10, resolve_urls=True)
results = news.extract_articles_text(articles)
for article, result in zip(articles, results):
if result.status == "success":
print(article.title)
print(result.text)
else:
print(article.title, result.status, result.error)
extract_article_text(article) handles one NewsArticle or an HTTP(S) URL. extract_articles_text(articles) processes a list sequentially and preserves input order and duplicates. Each ArticleTextResult contains url, text, status, and error. ArticleTextStatus is a public string enum with SUCCESS, FETCH_FAILED, and EXTRACTION_FAILED; an individual page failure does not stop the batch. Trafilatura handles both publisher-page downloading and main-text extraction. Article extraction does not bypass paywalls, logins, or publisher access controls.
Optional Google News URL resolution
Google News RSS entries commonly link to Google News URLs rather than directly to publisher pages. URL resolution is an optional, separate step; the normal RSS ingestion flow remains unchanged. You can resolve results directly through NewsClient using resolve_urls=True on get() or search(), or call resolve_article_urls() on existing results. Install the resolver extra from a source checkout:
uv sync --extra url-resolution
After exporting RSS results to a JSON array (or an object with an articles array), resolve them with:
uv run --extra url-resolution python scripts/resolve_urls.py data/articles.json
The script writes data/articles_resolved.json by default. It preserves the original url field, adds rss_url, resolved_url, url_resolution_status, and url_resolution_error, and continues processing when individual URLs fail. The original RSS URL remains available for provenance. The NewsArticle.resolved_url field is optional and appended to the model to preserve existing positional arguments. ArticleTextExtractor.extract(article) uses resolved_url when present and otherwise falls back to url. URL resolution does not bypass publisher paywalls, access controls, or site availability problems.
Quick start
from pyfinews import NewsClient
with NewsClient() as news:
# Broad financial-news discovery
market_news = news.get(hours=24, query_limit=30, max_articles=100)
# Topic-specific discovery
apple_news = news.get(
topic='AAPL OR "Apple Inc"',
hours=72,
query_limit=10,
max_articles=50,
)
# A single custom Google News query
custom_news = news.search('semiconductor supply chain', hours=48, max_articles=20)
for article in market_news:
print(article.published_at, article.source, article.title)
print(article.url)
API overview
NewsClient(...)
timeout=10.0: HTTP timeout in seconds.query_limit=30: default maximum number of RSS requests made by eachget()call.taxonomy=None: optional list of domain configuration dictionaries.publishers=None: optional list of publisher configuration dictionaries.provider=None: optional provider implementation, useful for testing or customization.
news.get(...)
topic=None: optional ticker, company, sector, macro topic, or query phrase. If omitted, uses the financial taxonomy.hours=24: lookback window in hours. Set toNoneto disable local lookback filtering and omit the freshness operator.max_articles=20: maximum number of articles returned; passNonefor no client-side maximum.query_limit=None: per-call maximum number of RSS requests; defaults to the client's configured budget.
max_articles controls the maximum number of returned articles; max_articles=None disables the result limit (the number returned still depends on what Google News supplies). query_limit controls the number of RSS requests, not the number of articles. Each attempted query consumes one request from the budget. A small budget favors higher-priority publishers and may not cover every domain.
news.search(query, ...)
Runs one arbitrary query without the financial taxonomy or publisher query planner. It accepts hours and max_articles, but not query_limit because it makes one RSS request.
NewsArticle
Each result is an immutable NewsArticle with title, url, optional source, published_at, retrieved_at, summary, query, resolved_url, url_resolution_status, and url_resolution_error fields. Datetimes are normalized to UTC. url retains the URL supplied by the feed; resolved_url optionally stores the publisher URL.
Query planning
For market-wide discovery, the planner:
- Excludes domains and publishers marked
enabled: false. - Sorts enabled publishers by ascending numeric
priority, with publisher ID as a deterministic tie-breaker. - Combines all configured terms within each domain into one quoted OR expression.
- Generates publisher/domain queries, iterating publishers by priority and domains in taxonomy order.
- Appends broad domain-only queries after publisher-targeted combinations.
- Removes duplicate query strings and returns no more than
query_limitqueries.
For topic-specific discovery, the planner starts with the base topic query, followed by publisher-targeted variants in priority order. Publisher names are query hints, not strict source filters. The aliases and regions fields are retained as metadata; they do not currently affect query selection.
Configuration
Default JSON files ship with the package:
pyfinews.taxonomy/default_taxonomy.json: financial domains and query terms.pyfinews.publishers/default_publishers.json: publisher metadata.
Example publisher entry:
{
"id": "reuters",
"name": "Reuters",
"aliases": ["Reuters News"],
"regions": ["global", "asia"],
"enabled": true,
"priority": 1
}
Load the defaults or a custom configuration file:
from pyfinews import load_publishers, load_taxonomy
domains = load_taxonomy()
publishers = load_publishers()
custom_domains = load_taxonomy("config/taxonomy.json")
custom_publishers = load_publishers("config/publishers.json")
See Configuration for the schema and validation rules.
Development and tests
uv sync --extra dev
uv run pytest
uv run ruff check .
uv run python -m compileall -q src tests
An optional live run is available at scripts/get_news.py. It makes external network requests, so it is separate from the unit test suite:
uv run python scripts/get_news.py
Important behavior and limitations
- Google News RSS is not a stable, officially documented search API; behavior and availability may change.
- Freshness operators are approximate; local date filtering is also applied when
hoursis specified. - Publisher names in queries do not guarantee that all results originate from that publisher.
- Summary text and timestamps depend on the RSS entries returned by Google News.
- Headline discovery retrieves RSS-provided metadata. Optional article-text extraction attempts to retrieve publisher pages, but does not bypass paywalls, logins, or publisher access controls; some pages may not yield text.
Documentation
License
MIT. See LICENSE.
Progress updates for URL resolution and text extraction
NewsClient.get() and NewsClient.search() print item-level progress updates to stderr by default when URL resolution or article-text extraction is enabled. Set show_progress=False to suppress these updates. Headlines are deduplicated by normalized title before max_articles is applied; URLs are not part of the deduplication key.
articles = client.get(
topic="AAPL",
max_articles=20,
resolve_urls=True,
extract_text=True,
show_progress=True,
)
Metadata
Release files for pyfinews 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pyfinews-0.1.1.tar.gz | 78.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyfinews-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 105.7 kB
Release files / pyfinews-0.1.1.tar.gz
| Download URL | pyfinews-0.1.1.tar.gz |
|---|---|
| Size | 78.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d54e12347e66fd96bbf93e5026ee023e2f1f61f019d6e6db511c6e1236748a03
|
|
BLAKE2b-256 checksum How to use checksums |
7c9430ad180d1dca837c56f5d167cc00a66285f3fe1a9f338ed1da3748112595
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pyfinews-0.1.1-py3-none-any.whl
| Download URL | pyfinews-0.1.1-py3-none-any.whl |
|---|---|
| Size | 27.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c4cef4e45ca80e08025e271954ad413747b0fcc4ae7d1b0a2c11003a7449c7ee
|
|
BLAKE2b-256 checksum How to use checksums |
57969a4b8031f57c6be5cb57268e90820c383f8af689fe003f0c2e27089aa860
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for pyfinews 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.