Skip to main content

A library for generating F1 blog posts from RSS feeds using AI

Project description

F1 Blog Pipeline

A Python library for generating F1 blog posts from RSS feeds using AI. Automatically extracts articles from F1 news sources and generates comprehensive blog posts using Google's Gemini API.

Features

  • ๐Ÿ“ฐ RSS Feed Parsing with date filtering support
  • ๐Ÿค– AI-Powered Blog Generation using Gemini API
  • ๐ŸŒ Web Scraping with Playwright for full article extraction
  • ๐Ÿ“… Flexible Date Filtering (today, yesterday, specific dates, date ranges)
  • ๐Ÿ”ง Library-First Design - use programmatically in your applications
  • โš™๏ธ Configurable feeds, extraction settings, and generation parameters
  • ๐Ÿ›ก๏ธ Robust Error Handling with partial results on failures

Installation

Basic Installation

# Clone the repository
git clone https://github.com/yourusername/f1-blog-pipeline.git
cd f1-blog-pipeline

# Install dependencies
pip install -r requirements.txt

# Install Playwright browsers
playwright install chromium

Development Installation

# Install in editable mode
pip install -e .

Quick Start

from f1_blog_pipeline import F1BlogPipeline

# Initialize pipeline with API key
pipeline = F1BlogPipeline(api_key='your-gemini-api-key')

# Run full pipeline
result = pipeline.run_full_pipeline(filter_date='today')

# Check results
if result.success:
    print(f"Extracted {result.articles_extracted} articles")
    print(f"Blog: {len(result.blog_content)} characters")

Configuration

Environment Variables

Create a .env file (optional):

# Required
GEMINI_API_KEY=your_gemini_api_key_here

# Optional
GEMINI_MODEL=gemini-2.0-flash

Note: The library does NOT load .env files automatically. Your application is responsible for credential management (use python-dotenv or your own config system).

Security: Never commit .env files to version control. The .env file is in .gitignore by default. Always use .env for local development and CI/CD secrets management for production deployments.

Custom Feeds

custom_feeds = {
    "Autosport": "https://www.autosport.com/rss/feed/f1",
    "The Race": "https://www.the-race.com/feed/",
    "RaceFans": "https://www.racefans.net/feed/"
}

pipeline = F1BlogPipeline(
    api_key='your-key',
    feeds=custom_feeds,
    max_per_feed=10
)

Configuration File

Copy config.yaml.example to config.yaml and customize:

feeds:
  Autosport: "https://www.autosport.com/rss/feed/f1"
  Motorsport: "https://www.motorsport.com/rss/f1/news"

extraction:
  max_per_feed: 5
  timeout: 30
  delay: 2

generation:
  model: "gemini-2.0-flash"
  temperature: 0.7

Usage

Basic Pipeline

from f1_blog_pipeline import F1BlogPipeline

# Initialize
pipeline = F1BlogPipeline(api_key='your-key')

# Run full pipeline
result = pipeline.run_full_pipeline(filter_date='today')

# Access results
print(result.success)              # True/False
print(result.articles_extracted)   # Number of articles
print(result.blog_content)         # Generated blog markdown
print(result.errors)               # List of errors if any

Individual Stages

# Just extract articles
articles = pipeline.extract_articles(filter_date='today')

# Generate blog from existing articles
blog = pipeline.generate_blog(articles, author='Your Name')

Custom Configuration

import logging

pipeline = F1BlogPipeline(
    api_key='your-key',
    feeds={'Source': 'https://...'},
    max_per_feed=10,
    timeout=45000,
    delay=3,
    model='gemini-2.0-pro',
    temperature=0.8,
    max_tokens=8000,
    output_dir='./output',
    logger_level=logging.DEBUG
)

Credential Management

The library is designed to work with any credential management strategy:

# Option 1: Pass API key directly
pipeline = F1BlogPipeline(api_key='sk-...')

# Option 2: Use environment variable (your app sets it)
import os
os.environ['GEMINI_API_KEY'] = get_key_from_your_config()
pipeline = F1BlogPipeline()  # Falls back to env var

# Option 3: Load from .env in your app (library doesn't do this)
from dotenv import load_dotenv
load_dotenv()  # YOUR responsibility, not library's
pipeline = F1BlogPipeline()

Date Filtering

# Today's articles only
result = pipeline.run_full_pipeline(filter_date='today')

# Yesterday
result = pipeline.run_full_pipeline(filter_date='yesterday')

# Specific date
result = pipeline.run_full_pipeline(filter_date='2026-02-22')

# Last N days
result = pipeline.run_full_pipeline(days_back=7)

# No filter (latest articles per feed)
result = pipeline.run_full_pipeline()

Examples

See the examples/ directory for detailed usage examples:

Output Formats

Extracted Articles

The pipeline saves extracted articles to articles/extracted_articles.txt in this format:

================================================================================
Source: Autosport
Title: Hamilton explains Mercedes 2026 strategy shift
Authors: ['Scott Mitchell-Malm']
URL: https://www.autosport.com/f1/news/...

[Full article text content with proper formatting and paragraphs...]

================================================================================
Source: Motorsport
Title: Red Bull confirms major technical update for Bahrain
...

Generated Blog Post

The pipeline generates a markdown blog post with YAML frontmatter:

---
title: "2026 Testing Unpacked: Regulation Chaos and Team Performance"
date: 2026-02-22
author: "Adrian"
tags: [f1, testing, 2026-season, bahrain, ferrari, mercedes, red-bull]
summary: "Welcome to the 2026 era. If pre-season testing in Bahrain..."
---

# 2026 Testing Unpacked: Regulation Chaos and Team Performance

Welcome to the 2026 era. If pre-season testing in Bahrain has taught us anything...

## Team Performance Analysis

**Mercedes** emerged as the early pace-setters:
- Russell topped FP1 with a 1:31.247
- New power unit showing promising reliability
- Aerodynamic package generating consistent downforce

**Ferrari** showed mixed results:
- Leclerc struggled with rear stability in high-speed corners
- Team working on suspension geometry adjustments
- Power unit performance on par with expectations

## Technical Updates

The new 2026 regulations have forced teams to rethink...

Project Structure

f1_app/
โ”œโ”€โ”€ f1_blog_pipeline/          # Main library package
โ”‚   โ”œโ”€โ”€ __init__.py           # Public API
โ”‚   โ”œโ”€โ”€ pipeline.py           # Pipeline orchestrator
โ”‚   โ”œโ”€โ”€ core/                 # Core modules
โ”‚   โ”‚   โ”œโ”€โ”€ rss_parser.py     # RSS feed parsing with date filtering
โ”‚   โ”‚   โ”œโ”€โ”€ article_extractor.py  # Article extraction
โ”‚   โ”‚   โ”œโ”€โ”€ blog_generator.py     # AI blog generation
โ”‚   โ”‚   โ””โ”€โ”€ text_cleaner.py       # Text cleaning utilities
โ”œโ”€โ”€ examples/                 # Usage examples
โ”œโ”€โ”€ articles/                 # Extracted articles (generated)
โ”œโ”€โ”€ posts/                    # Generated blog posts (generated)
โ”œโ”€โ”€ requirements.txt          # Dependencies
โ”œโ”€โ”€ pyproject.toml           # Package configuration
โ”œโ”€โ”€ config.yaml.example      # Example configuration
โ””โ”€โ”€ README.md                # This file

API Reference

F1BlogPipeline

Main pipeline class for orchestrating the entire workflow.

class F1BlogPipeline:
    def __init__(
        api_key: Optional[str] = None,
        feeds: Optional[Dict[str, str]] = None,
        max_per_feed: int = 5,
        timeout: int = 30000,
        delay: int = 2,
        model: str = "gemini-2.0-flash",
        temperature: float = 0.7,
        max_tokens: int = 4000,
        output_dir: Optional[str] = None,
        logger_level: Optional[int] = None
    )

PipelineResult

Result object returned by pipeline execution.

@dataclass
class PipelineResult:
    success: bool                          # Overall success
    articles_extracted: int                # Number of articles
    articles: List[Dict[str, Any]]        # Extracted articles
    blog_content: Optional[str]           # Generated blog
    errors: List[Dict[str, str]]          # Errors
    warnings: List[str]                   # Warnings

Advanced Usage

Batch Processing

Generate blog posts for multiple dates:

from datetime import datetime, timedelta
from f1_blog_pipeline import F1BlogPipeline

pipeline = F1BlogPipeline(api_key='your-key')

# Generate posts for last 7 days
for days_ago in range(7):
    date = datetime.now() - timedelta(days=days_ago)
    date_str = date.strftime('%Y-%m-%d')
    
    result = pipeline.run_full_pipeline(filter_date=date_str)
    if result.success:
        with open(f'posts/blog_{date_str}.md', 'w') as f:
            f.write(result.blog_content)
        print(f"โœ“ Generated blog for {date_str}")

Custom Model Configuration

Test different Gemini models:

pipeline = F1BlogPipeline(
    api_key='your-key',
    model='gemini-2.0-pro',
    temperature=0.9,  # More creative
    max_tokens=8000   # Longer output
)
result = pipeline.run_full_pipeline(filter_date='today')

Available models:

  • gemini-2.0-flash (fast, good quality, cost-effective)
  • gemini-2.0-pro (higher quality, slower, more expensive)
  • gemini-2.0-flash-exp-preview (experimental features)

Error Handling

The pipeline continues on partial failures:

result = pipeline.run_full_pipeline(filter_date='today')

if result.success:
    print(f"Success! {result.articles_extracted} articles")
else:
    print("Pipeline failed")
    for error in result.errors:
        print(f"[{error['stage']}] {error['message']}")

# Check warnings even on success
for warning in result.warnings:
    print(f"Warning: {warning}")

Requirements

  • Python 3.10+
  • google-genai >= 0.4.0
  • playwright >= 1.40.0
  • python-dotenv >= 1.0.0

Troubleshooting

Playwright Browsers Not Installed

playwright install chromium

API Key Not Found

# Option 1: Pass directly
pipeline = F1BlogPipeline(api_key='your-key')

# Option 2: Use environment variable
import os
os.environ['GEMINI_API_KEY'] = 'your-key'
pipeline = F1BlogPipeline()

# Option 3: Load from .env file
from dotenv import load_dotenv
load_dotenv()
pipeline = F1BlogPipeline()

Bot Detection / 403 Errors

Increase delay between requests:

pipeline = F1BlogPipeline(delay=5)  # 5 seconds
result = pipeline.run_full_pipeline(filter_date='today')

License

MIT

Contributing

Contributions welcome! Please open an issue or submit a pull request.

Acknowledgments

Project details


Download files

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

Source Distribution

f1_blog_pipeline-0.1.0.tar.gz (20.9 kB view details)

Uploaded Source

Built Distribution

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

f1_blog_pipeline-0.1.0-py3-none-any.whl (20.5 kB view details)

Uploaded Python 3

File details

Details for the file f1_blog_pipeline-0.1.0.tar.gz.

File metadata

  • Download URL: f1_blog_pipeline-0.1.0.tar.gz
  • Upload date:
  • Size: 20.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for f1_blog_pipeline-0.1.0.tar.gz
Algorithm Hash digest
SHA256 33d20bf6d57998cee5f9aef27c90db652437501e8c100ec4e0d7d2320b749bc0
MD5 bdd517f1c824762ac299361056ebfc7d
BLAKE2b-256 8660f186cb7694a9360a35389ae29cc43799a7f259d2886505609ce263b3b69f

See more details on using hashes here.

File details

Details for the file f1_blog_pipeline-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for f1_blog_pipeline-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d05fbc399c9a47ef912cd913a86dd5ec208b45936d48f59e59ad8124a1b0c560
MD5 4cc9e35696ebfa8759c39c3a40ce5d24
BLAKE2b-256 3ae080bfb01807b1c2db3157bb5105ebcd1c8007af198729f4a6ad9574c76c01

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 Pingdom Monitoring Sentry Error logging StatusPage Status page