The best way to learn Python isn't to read a textbook — it's to build something that solves a real problem you have. Automation scripts are perfect for this: they're small enough to finish in an afternoon, they use real libraries, and when they work, they actually save you time. Here are five scripts worth writing this weekend, with complete working code for each.

Before you start: Create a virtual environment: python -m venv venv and activate it (source venv/bin/activate on Mac/Linux, venv\Scripts\activate on Windows). Install dependencies as you go with pip install.

Script 1: File Organiser — Sort Your Downloads Folder by Type

If your Downloads folder looks like a digital junk drawer, this script is for you. It scans the folder and moves every file into a subfolder based on its type — all images go to Images/, all PDFs go to Documents/, and so on. Uses only Python's standard library: os, shutil, and pathlib.

import os
import shutil
from pathlib import Path

# Map file extensions to folder names
EXTENSION_MAP = {
    # Images
    ".jpg": "Images", ".jpeg": "Images", ".png": "Images",
    ".gif": "Images", ".webp": "Images", ".svg": "Images",
    # Documents
    ".pdf": "Documents", ".docx": "Documents", ".doc": "Documents",
    ".xlsx": "Documents", ".csv": "Documents", ".txt": "Documents",
    # Videos
    ".mp4": "Videos", ".mov": "Videos", ".avi": "Videos",
    # Audio
    ".mp3": "Audio", ".wav": "Audio", ".flac": "Audio",
    # Code
    ".py": "Code", ".js": "Code", ".html": "Code", ".css": "Code",
    # Archives
    ".zip": "Archives", ".tar": "Archives", ".gz": "Archives",
}

def organise_folder(folder_path: str) -> None:
    folder = Path(folder_path)
    moved = 0
    skipped = 0

    for file in folder.iterdir():
        if file.is_dir():
            continue  # skip folders

        ext = file.suffix.lower()
        subfolder_name = EXTENSION_MAP.get(ext, "Other")
        destination = folder / subfolder_name
        destination.mkdir(exist_ok=True)

        dest_file = destination / file.name
        if dest_file.exists():
            print(f"Skipping (already exists): {file.name}")
            skipped += 1
            continue

        shutil.move(str(file), str(dest_file))
        print(f"Moved: {file.name} → {subfolder_name}/")
        moved += 1

    print(f"\nDone! Moved {moved} files, skipped {skipped}.")

if __name__ == "__main__":
    downloads = str(Path.home() / "Downloads")
    organise_folder(downloads)

Script 2: Email Sender — Personalised Bulk Email From a CSV

Need to email a list of contacts with personalised messages? This script reads a CSV of names and emails, and sends a customised email to each using Python's built-in smtplib. Works with Gmail (enable App Passwords in your Google account settings).

import smtplib
import csv
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# Your email config — use environment variables in production!
SENDER_EMAIL = "your@gmail.com"
APP_PASSWORD = "your_app_password"  # NOT your regular password
SMTP_HOST = "smtp.gmail.com"
SMTP_PORT = 587

def send_email(recipient_name: str, recipient_email: str) -> None:
    msg = MIMEMultipart("alternative")
    msg["Subject"] = f"Hello {recipient_name}!"
    msg["From"] = SENDER_EMAIL
    msg["To"] = recipient_email

    body = f"""Hi {recipient_name},

Thank you for joining our community! We're excited to have you.

Here are a few things to get you started:
- Check out our latest blog posts at vizionikra.ai/blog
- Join our community Discord for support
- Reply to this email with any questions

Best,
The VizioNikraAI Team"""

    msg.attach(MIMEText(body, "plain"))

    with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
        server.starttls()
        server.login(SENDER_EMAIL, APP_PASSWORD)
        server.sendmail(SENDER_EMAIL, recipient_email, msg.as_string())

def send_bulk_from_csv(csv_path: str) -> None:
    with open(csv_path, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            try:
                send_email(row["name"], row["email"])
                print(f"Sent to {row['email']}")
            except Exception as e:
                print(f"Failed for {row['email']}: {e}")

if __name__ == "__main__":
    send_bulk_from_csv("contacts.csv")
    # CSV format: name,email
    # Maria,maria@example.com
    # John,john@example.com

Script 3: Website Monitor — Get Alerted When a Site Goes Down

This script checks a list of URLs every 5 minutes and emails you if any of them return a non-200 status or become unreachable. Uses requests for HTTP and schedule for the timer loop.

import requests
import schedule
import time
import smtplib
from datetime import datetime
from email.mime.text import MIMEText

SITES_TO_MONITOR = [
    "https://yoursite.com",
    "https://api.yoursite.com/health",
]

SENDER_EMAIL = "monitor@gmail.com"
ALERT_EMAIL = "you@gmail.com"
APP_PASSWORD = "your_app_password"

def send_alert(url: str, status: str) -> None:
    msg = MIMEText(f"ALERT: {url} is DOWN\nStatus: {status}\nTime: {datetime.now()}")
    msg["Subject"] = f"Site Down: {url}"
    msg["From"] = SENDER_EMAIL
    msg["To"] = ALERT_EMAIL
    with smtplib.SMTP("smtp.gmail.com", 587) as server:
        server.starttls()
        server.login(SENDER_EMAIL, APP_PASSWORD)
        server.sendmail(SENDER_EMAIL, ALERT_EMAIL, msg.as_string())

def check_sites() -> None:
    print(f"[{datetime.now().strftime('%H:%M:%S')}] Checking {len(SITES_TO_MONITOR)} sites...")
    for url in SITES_TO_MONITOR:
        try:
            response = requests.get(url, timeout=10)
            if response.status_code != 200:
                print(f"DOWN: {url} — HTTP {response.status_code}")
                send_alert(url, f"HTTP {response.status_code}")
            else:
                print(f"OK:   {url}")
        except requests.exceptions.RequestException as e:
            print(f"DOWN: {url} — {e}")
            send_alert(url, str(e))

schedule.every(5).minutes.do(check_sites)
check_sites()  # Run immediately on start
while True:
    schedule.run_pending()
    time.sleep(30)

Script 4: PDF Merger — Combine Multiple PDFs Into One

Stop manually copying content between PDFs. This script takes every PDF in a folder (or a specific list) and merges them into a single file. Uses PyPDF2 (or the newer pypdf package).

import pypdf  # pip install pypdf
from pathlib import Path

def merge_pdfs(input_folder: str, output_file: str) -> None:
    folder = Path(input_folder)
    pdf_files = sorted(folder.glob("*.pdf"))

    if not pdf_files:
        print("No PDF files found in the folder.")
        return

    writer = pypdf.PdfWriter()

    for pdf_path in pdf_files:
        print(f"Adding: {pdf_path.name}")
        reader = pypdf.PdfReader(str(pdf_path))
        for page in reader.pages:
            writer.add_page(page)

    with open(output_file, "wb") as out:
        writer.write(out)

    total_pages = sum(len(pypdf.PdfReader(str(p)).pages) for p in pdf_files)
    print(f"\nMerged {len(pdf_files)} PDFs ({total_pages} pages) → {output_file}")

if __name__ == "__main__":
    merge_pdfs(
        input_folder="./reports",   # folder containing PDFs to merge
        output_file="merged_report.pdf"
    )
Extension idea: Add a command-line interface with argparse so you can run python merge_pdfs.py --input ./reports --output combined.pdf from any terminal.

Script 5: Daily News Digest — Scrape Headlines and Email a Summary

This script scrapes the top headlines from Hacker News (a JSON API, no parsing needed) every morning and emails you a formatted digest. Uses requests for the API call and schedule to run at a set time each day.

import requests
import schedule
import time
import smtplib
from email.mime.text import MIMEText
from datetime import datetime

SENDER_EMAIL = "digest@gmail.com"
RECIPIENT_EMAIL = "you@gmail.com"
APP_PASSWORD = "your_app_password"
TOP_N = 10  # number of stories to include

def fetch_hn_top_stories(n: int) -> list:
    # Hacker News has a public JSON API — no scraping required
    top_ids = requests.get(
        "https://hacker-news.firebaseio.com/v0/topstories.json"
    ).json()[:50]

    stories = []
    for story_id in top_ids[:50]:
        story = requests.get(
            f"https://hacker-news.firebaseio.com/v0/item/{story_id}.json"
        ).json()
        if story and story.get("url"):
            stories.append(story)
        if len(stories) == n:
            break
    return stories

def build_digest(stories: list) -> str:
    date_str = datetime.now().strftime("%A, %B %d %Y")
    lines = [f"Your Hacker News Digest — {date_str}\n", "=" * 50]
    for i, story in enumerate(stories, 1):
        lines.append(f"\n{i}. {story['title']}")
        lines.append(f"   {story['url']}")
        lines.append(f"   {story.get('score', 0)} points | {story.get('descendants', 0)} comments")
    return "\n".join(lines)

def send_digest() -> None:
    print("Fetching top stories...")
    stories = fetch_hn_top_stories(TOP_N)
    body = build_digest(stories)

    msg = MIMEText(body, "plain")
    msg["Subject"] = f"Daily Tech Digest — {datetime.now().strftime('%b %d')}"
    msg["From"] = SENDER_EMAIL
    msg["To"] = RECIPIENT_EMAIL

    with smtplib.SMTP("smtp.gmail.com", 587) as server:
        server.starttls()
        server.login(SENDER_EMAIL, APP_PASSWORD)
        server.sendmail(SENDER_EMAIL, RECIPIENT_EMAIL, msg.as_string())
    print("Digest sent!")

# Schedule to run at 07:30 every morning
schedule.every().day.at("07:30").do(send_digest)
send_digest()  # Run once immediately
while True:
    schedule.run_pending()
    time.sleep(60)
What to build next: Combine scripts 3 and 5 — a monitoring system that sends you a daily summary of site uptime stats alongside your news digest. Adding a SQLite database to log uptime history turns it into a real monitoring tool.

Each of these five scripts teaches you something different: file I/O and path manipulation, SMTP and CSV processing, HTTP requests and scheduled tasks, PDF manipulation, and API consumption. Together, they cover most of the patterns you'll use in real Python automation work.

Go From Scripts to Production Python

Our Python course takes you from beginner to writing clean, tested, production-grade Python — including automation, APIs, data processing, and AI integration.

View the Python Course →
PC

Pal C

AI Engineer & Full-Stack Developer

Software engineer and AI specialist with 8+ years of experience. Has taught 500+ students from 15+ countries.