pyfinews
pyfinews is a lightweight Python library for discovering financial-news headlines through Google News RSS. It provides a high-level client API, configurable financial-domain taxonomy, publisher-priority query planning, date filtering, normalized article models, Google News URL resolution, and article-text extraction.
The package uses Python's standard library for HTTP transport, feedparser for RSS/Atom parsing, googlenewsdecoder and selectolax for resolving Google News links, and Trafilatura for downloading publisher pages and extracting main article text. These are runtime dependencies and are installed automatically with pyfinews.
Features
- Discover financial news across configurable domains and topics.
- Search for tickers, companies, sectors, macroeconomic topics, and arbitrary phrases.
- Configure the RSS request budget through
query_limit. - Prioritize publishers by ascending
priority(priority1is highest). - Combine domain terms into OR expressions for publisher/domain query planning.
- Load default or custom publisher and domain JSON configuration.
- Filter by lookback window and sort results newest-first.
- Deduplicate returned headlines by normalized title before applying
max_articles. - Optionally resolve Google News RSS links to publisher URLs.
- Optionally download article pages and extract their main text.
- Preserve item-level failures as result statuses so one problematic article does not stop a batch.
- Keep transport and query-planning layers testable and customizable.
Requirements
- Python 3.12 or newer.
uvis recommended for development and dependency management.pipcan also be used to install the published package.
Installation
Install the published package with uv:
uv add pyfinews
Or install it with pip:
python -m pip install pyfinews
To work on a local checkout, install the development dependencies:
uv sync --extra dev
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,
)
# One custom Google News query
semiconductor_news = news.search(
"semiconductor supply chain",
hours=48,
max_articles=20,
)
# Resolve Google News URLs and extract article text when useful
resolved_articles = news.get(
topic="AAPL",
max_articles=10,
resolve_urls=True,
extract_text=True,
show_progress=False,
)
for article in market_news:
print(article.published_at, article.source, article.title)
print(article.url)
show_progress controls item-level progress messages written to stderr during URL resolution or text extraction. Set show_progress=False to suppress them.
Article-text extraction
Article-text extraction is available through Trafilatura, which is installed as a runtime dependency. Use NewsClient to process a batch:
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)
The client also provides extract_article_text(article) for 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 statuses. A page-level failure is returned as a result and does not stop the batch.
Trafilatura handles publisher-page downloading and main-text extraction. Extraction may fail because of site structure, network issues, or publisher restrictions. It does not bypass paywalls, logins, or other access controls.
Google News URL resolution
Google News RSS entries commonly link to Google News URLs rather than directly to publisher pages. URL resolution is a separate step and does not change the original RSS URL. Resolve URLs during a client query with resolve_urls=True, or resolve articles you already have using resolve_article_urls().
from pyfinews import NewsClient
with NewsClient() as news:
articles = news.search(
'Apple earnings',
max_articles=10,
resolve_urls=True,
show_progress=False,
)
for article in articles:
print(article.title)
print("RSS URL:", article.url)
print("Publisher URL:", article.resolved_url)
print("Resolution status:", article.url_resolution_status)
The package also includes a script for resolving URLs in an exported JSON file. The input can be a JSON array or an object containing an articles array:
uv run python scripts/resolve_urls.py data/articles.json
By default, the script writes data/articles_resolved.json. It preserves the original url field and adds rss_url, resolved_url, url_resolution_status, and url_resolution_error. Individual failures do not stop the batch, and the original RSS URL remains available for provenance.
NewsArticle.resolved_url is optional. Article-text extraction uses resolved_url when present and otherwise falls back to url. URL resolution does not guarantee that a publisher page is accessible or extractable, and it does not bypass paywalls or other access controls.
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, the financial taxonomy is used.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.resolve_urls=False: whether to resolve Google News URLs to publisher URLs.extract_text=False: whether to extract main text from article pages.show_progress=True: whether to display item-level progress during URL resolution or text extraction.
max_articles controls the maximum number of returned articles; max_articles=None disables the client-side result limit. The number returned still depends on what Google News supplies. Headlines are deduplicated by normalized title before max_articles is applied; the URL is not part of the deduplication key.
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, and supports URL resolution and article-text extraction when requested. It makes one RSS request, so query_limit does not apply.
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.
ArticleTextResult
Each extraction result contains:
url: the URL used for extraction.text: extracted article text, orNoneif extraction failed.status:success,fetch_failed, orextraction_failed.error: an explanatory message when the operation failed.
URLResolutionResult
Each URL-resolution result contains the original RSS URL, the resolved publisher URL when successful, a status, and an error message when resolution fails. Resolution failures are returned as results rather than raised for individual URLs.
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
Clone the repository and install development dependencies:
uv sync --extra dev
Run the test suite and static checks:
uv run pytest
uv run ruff check .
uv run ruff format --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.
- Article-text extraction attempts to download publisher pages, but some pages may not yield text because of network failures, page structure, paywalls, logins, or other access controls.
- URL resolution may fail for individual RSS entries; check
url_resolution_statusandurl_resolution_errorbefore relying onresolved_url.
Documentation
License
MIT. See LICENSE.
Metadata
Release files for pyfinews 0.1.5
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.5.tar.gz | 81.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyfinews-0.1.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 108.8 kB
Release files / pyfinews-0.1.5.tar.gz
| Download URL | pyfinews-0.1.5.tar.gz |
|---|---|
| Size | 81.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5161f5c3c11ac50d479014521f298d5ed6af72a0b70bacf2c69b363bdf74994e
|
|
BLAKE2b-256 checksum How to use checksums |
40a4398f9bf563eddcbac0207fb6d8e991be28aae8ae16215267a42922bc06aa
|
| 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.5-py3-none-any.whl
| Download URL | pyfinews-0.1.5-py3-none-any.whl |
|---|---|
| Size | 27.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b0a09d72d68c68efc3d6e39739bcc2d728d6dc94e9595a3f597766ac4c546756
|
|
BLAKE2b-256 checksum How to use checksums |
0a6b80ee6becf7f33e37c6908d55ca9d1d57f09c4f8b1a52c8eccc84e61efeef
|
| 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.5
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.