Build a Simple ETL Pipeline in Python
A simple ETL pipeline that reads JSON Lines, transforms records with filtering and normalization, and writes the result to JSON.
Python code
55 linesimport json
from pathlib import Path
def read_input(file_path: Path) -> list[dict]:
"""Read JSON lines file into list of dicts."""
with file_path.open("r", encoding="utf-8") as f:
return [json.loads(line) for line in f if line.strip()]
def transform(records: list[dict]) -> list[dict]:
"""Transform raw records: filter invalid, normalize fields."""
cleaned = []
for rec in records:
if rec.get("value") is None:
continue
cleaned.append({
"id": rec["id"],
"value": float(rec["value"]),
"status": rec.get("status", "unknown"),
})
return cleaned
def write_output(records: list[dict], output_path: Path) -> None:
"""Write transformed records to JSON file."""
with output_path.open("w", encoding="utf-8") as f:
json.dump(records, f, indent=2)
if __name__ == "__main__":
input_file = Path("input.jsonl")
output_file = Path("output.json")
# Sample input data for demonstration
sample_records = [
{"id": 1, "value": "10.5", "status": "ok"},
{"id": 2, "value": "20.0"},
{"id": 3, "value": None},
{"id": 4, "value": "30.25", "status": "flagged"},
]
with input_file.open("w", encoding="utf-8") as f:
for rec in sample_records:
f.write(json.dumps(rec) + "\n")
# ETL pipeline
raw_data = read_input(input_file)
transformed = transform(raw_data)
write_output(transformed, output_file)
# Verify and print result
with output_file.open("r", encoding="utf-8") as f:
result = json.load(f)
print(f"Transformed {len(transformed)} records:")
print(json.dumps(result, indent=2))
Output
Transformed 3 records:
[
{
"id": 1,
"value": 10.5,
"status": "ok"
},
{
"id": 2,
"value": 20.0,
"status": "unknown"
},
{
"id": 4,
"value": 30.25,
"status": "flagged"
}
]
How it works
The pipeline uses pathlib.Path for cross-platform file handling and the standard json module for parsing and writing. read_input reads JSON Lines, skipping blank lines, while transform filters records without a value field and casts values to floats, defaulting status to 'unknown'. write_output writes the cleaned list as indented JSON. The if __name__ == '__main__' guard makes the functions importable and testable, while the demo creates sample data so you can run the script as-is. This design keeps each stage pure and easy to unit-test independently.
Common mistakes
- Forgetting to skip blank lines in JSONL, causing parse errors
- Assuming all records have a `status` key without using `.get()`
- Not handling missing `value` fields before casting to float
- Using `open()` without a context manager, leaking file handles
Variations
- Use `json.load()` directly on an open file handle for a single JSON document instead of JSONL
- Add `pandas.read_csv()` to ingest CSV input and convert to a DataFrame before transforming
Real-world use cases
- Batch-processing raw API logs stored as JSONL into clean records for a data warehouse.
- Normalizing and validating config dumps from multiple services before loading them into a feature store.
- Moving CSV or JSON event files from an S3 bucket into a relational database with cleaning steps in between.
Sponsored
More from Files & data
- Append a Line to a Log File in Python easy
- Audit File Permissions Across a Project in Python easy
- Automatically Detect Corrupted Files Using SHA-256 Checksums in Python easy
- Automatically Highlight Data Validation Errors Inside Excel Files in Python easy
- Build a Command-Line To-Do List Application with Data Persistence in Python easy
- Build a File Index by Relative Path Hash Map in Python easy
Keep learning
Related tutorials and quizzes for this topic.