Python Data Pipeline Automation: Build ETL Workflows That Scale
Last updated: July 2026
Data pipelines are the backbone of modern data engineering. Learn how to build robust, scalable ETL workflows in Python.
What is a Data Pipeline?
A data pipeline automates the flow of data from source to destination:
- Extract — Pull data from databases, APIs, files, or streams
- Transform — Clean, filter, aggregate, and enrich the data
- Load — Push the data to a data warehouse, database, or file
Why Python for Data Pipelines?
- Rich ecosystem — Pandas, NumPy, SQLAlchemy, Airflow
- Easy to learn — Simple syntax, great documentation
- Scalable — Works for small scripts to enterprise pipelines
- Community support — Thousands of packages and tutorials
Building Your First Pipeline
Simple ETL Pipeline
import pandas as pd
from sqlalchemy import create_engine
from datetime import datetime
class DataPipeline:
def __init__(self, source_db, dest_db):
self.source = create_engine(source_db)
self.dest = create_engine(dest_db)
def extract(self, query):
"""Extract data from source."""
print(f"[{datetime.now()}] Extracting data...")
return pd.read_sql(query, self.source)
def transform(self, df):
"""Transform the data."""
print(f"[{datetime.now()}] Transforming {len(df)} rows...")
# Clean data
df = df.dropna()
df = df.drop_duplicates()
# Add computed columns
df['processed_at'] = datetime.now()
df['total'] = df['quantity'] * df['price']
# Filter
df = df[df['total'] > 0]
return df
def load(self, df, table_name):
"""Load data to destination."""
print(f"[{datetime.now()}] Loading {len(df)} rows to {table_name}...")
df.to_sql(table_name, self.dest, if_exists='append', index=False)
print(f"[{datetime.now()}] Done!")
def run(self, query, table_name):
"""Run the full pipeline."""
df = self.extract(query)
df = self.transform(df)
self.load(df, table_name)
# Usage
pipeline = DataPipeline(
"postgresql://user:pass@source-db/data",
"postgresql://user:pass@dest-db/warehouse"
)
pipeline.run("SELECT * FROM raw_sales", "clean_sales")
Scheduling with Cron
# pipeline_scheduler.py
import schedule
import time
from pipeline import DataPipeline
def run_daily_pipeline():
pipeline = DataPipeline(SOURCE_DB, DEST_DB)
pipeline.run("SELECT * FROM raw_data WHERE date = CURRENT_DATE", "daily_data")
# Schedule daily at 2 AM
schedule.every().day.at("02:00").do(run_daily_pipeline)
while True:
schedule.run_pending()
time.sleep(60)
Error Handling & Retry
import time
from functools import wraps
def retry(max_attempts=3, delay=5):
"""Decorator for retrying failed operations."""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"Attempt {attempt + 1} failed: {e}")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=10)
def extract_data(query):
return pd.read_sql(query, engine)
Advanced Patterns
1. Parallel Processing
from concurrent.futures import ThreadPoolExecutor
import pandas as pd
def process_chunk(chunk):
"""Process a data chunk."""
return chunk.apply(transform_row, axis=1)
def parallel_process(df, n_workers=4):
"""Process DataFrame in parallel."""
chunks = np.array_split(df, n_workers)
with ThreadPoolExecutor(max_workers=n_workers) as executor:
results = list(executor.map(process_chunk, chunks))
return pd.concat(results)
2. Incremental Loading
def incremental_load(table_name, last_checkpoint):
"""Load only new data since last checkpoint."""
query = f"""
SELECT * FROM source_table
WHERE updated_at > '{last_checkpoint}'
ORDER BY updated_at
"""
df = extract(query)
if not df.empty:
load(df, table_name)
new_checkpoint = df['updated_at'].max()
save_checkpoint(table_name, new_checkpoint)
3. Data Validation
from pydantic import BaseModel, validator
from datetime import datetime
class SalesRecord(BaseModel):
product_id: str
quantity: int
price: float
sale_date: datetime
@validator('quantity')
def quantity_must_be_positive(cls, v):
if v <= 0:
raise ValueError('quantity must be positive')
return v
@validator('price')
def price_must_be_positive(cls, v):
if v <= 0:
raise ValueError('price must be positive')
return v
def validate_dataframe(df):
"""Validate each row in DataFrame."""
errors = []
for idx, row in df.iterrows():
try:
SalesRecord(**row.to_dict())
except Exception as e:
errors.append(f"Row {idx}: {e}")
return errors
Production Checklist
- [ ] Error handling and logging
- [ ] Retry logic for transient failures
- [ ] Data validation
- [ ] Incremental loading
- [ ] Monitoring and alerting
- [ ] Documentation
- [ ] Tests
Get the Production-Ready Version
We have a complete data pipeline toolkit at our store.
What's included:
- Pre-built ETL templates
- Scheduling utilities
- Error handling decorators
- Data validation framework
- Monitoring dashboard
Top comments (0)