Skip to main content

10 Python Scripts That Automate My Everyday Tasks

Practical Python automation scripts for file organization, image resizing, web scraping, PDF merging, and more. Complete code included for each script.

Priya Patel
18 min read
10 Python Scripts That Automate My Everyday Tasks

About two years ago, I realized something embarrassing. I was spending hours every week on tasks a computer should've been doing for me. Renaming files. Resizing images for blog posts. Copying data between CSVs and spreadsheets. Sending the same kind of emails over and over. Each task took maybe five or ten minutes on its own, but add them all up across a week? Hours. Gone. Completely wasted.

So I did what any developer with a mild automation obsession would do — I wrote Python scripts to handle the boring parts. Some save me 30 seconds. Others save me an hour every single week. Point is, once you write them, they just work. Run them, grab your chai, come back to a finished task.

Here are my 10 most-used scripts, with full code you can copy, modify, and start running today. Every script is self-contained — drop it into a .py file and go. Some need third-party libraries, and I've noted the pip install commands wherever that's the case.


1. File Organizer by Extension

My Downloads folder used to look like a warzone. PDFs mixed with screenshots mixed with random .zip files from three months ago. This script scans a folder and moves each file into a subfolder based on its extension.

The Code

import os
import shutil
from pathlib import Path

def organize_folder(target_dir):
    """Organize files into subfolders based on their extension."""
    target_path = Path(target_dir)

    if not target_path.exists():
        print(f"Directory {target_dir} does not exist.")
        return

    extension_map = {
        'Images': ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg', '.webp'],
        'Documents': ['.pdf', '.doc', '.docx', '.txt', '.xlsx', '.xls', '.pptx', '.csv'],
        'Videos': ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv'],
        'Audio': ['.mp3', '.wav', '.flac', '.aac', '.ogg'],
        'Archives': ['.zip', '.rar', '.7z', '.tar', '.gz'],
        'Code': ['.py', '.js', '.ts', '.html', '.css', '.java', '.cpp', '.c'],
        'Installers': ['.exe', '.msi', '.dmg', '.deb', '.rpm'],
    }

    # Reverse map: extension -> folder name
    ext_to_folder = {}
    for folder, extensions in extension_map.items():
        for ext in extensions:
            ext_to_folder[ext] = folder

    moved_count = 0
    for item in target_path.iterdir():
        if item.is_file():
            ext = item.suffix.lower()
            folder_name = ext_to_folder.get(ext, 'Others')
            dest_folder = target_path / folder_name
            dest_folder.mkdir(exist_ok=True)

            dest_file = dest_folder / item.name
            if dest_file.exists():
                # Add a number suffix to avoid overwriting
                base = item.stem
                counter = 1
                while dest_file.exists():
                    dest_file = dest_folder / f"{base}_{counter}{ext}"
                    counter += 1

            shutil.move(str(item), str(dest_file))
            moved_count += 1
            print(f"Moved: {item.name} -> {folder_name}/")

    print(f"\nDone! Moved {moved_count} files.")

if __name__ == "__main__":
    folder = input("Enter the folder path to organize: ").strip()
    organize_folder(folder)

How I Use It

I've got this running as a scheduled task on my Windows machine every Sunday evening. By Monday morning, Downloads folder is pristine. You could hook it up with cron on Linux or launchd on macOS just as easily. The extension map is straightforward to customize — add new categories or shuffle file types around to match how you think about your files.


2. Bulk Image Resizer

Writing blog posts or preparing presentations, I constantly need to resize batches of images to a specific width. Doing it one by one in an image editor is painful. This script handles an entire folder in seconds.

The Code

from PIL import Image
from pathlib import Path

def resize_images(input_dir, output_dir, max_width=800):
    """Resize all images in a folder to a maximum width, preserving aspect ratio."""
    input_path = Path(input_dir)
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)

    supported = {'.jpg', '.jpeg', '.png', '.bmp', '.webp'}

    for img_file in input_path.iterdir():
        if img_file.suffix.lower() in supported:
            with Image.open(img_file) as img:
                if img.width > max_width:
                    ratio = max_width / img.width
                    new_height = int(img.height * ratio)
                    img_resized = img.resize((max_width, new_height), Image.LANCZOS)
                else:
                    img_resized = img.copy()

                output_file = output_path / img_file.name
                img_resized.save(output_file, quality=85, optimize=True)
                print(f"Resized: {img_file.name} ({img.width}x{img.height} -> {img_resized.width}x{img_resized.height})")

if __name__ == "__main__":
    src = input("Source folder: ").strip()
    dest = input("Output folder: ").strip()
    width = int(input("Max width (default 800): ").strip() or "800")
    resize_images(src, dest, width)

You'll need Pillow for this:

pip install Pillow

quality=85 with optimize=True gives a good balance between file size and visual quality. Going below 80 starts showing noticeable compression artifacts, especially on photographs. I've tried lower and it's not worth the file size savings.


3. Automated Email Sender

I send a weekly status update email to my team every Friday. Format's almost identical each time — just the bullet points change. This script uses Python's built-in smtplib to send HTML emails through Gmail.

The Code

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

def send_email(sender, password, recipient, subject, body_html):
    """Send an HTML email via Gmail SMTP."""
    msg = MIMEMultipart('alternative')
    msg['From'] = sender
    msg['To'] = recipient
    msg['Subject'] = subject

    html_part = MIMEText(body_html, 'html')
    msg.attach(html_part)

    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
        server.login(sender, password)
        server.send_message(msg)
        print(f"Email sent to {recipient}")

if __name__ == "__main__":
    SENDER = "your.email@gmail.com"
    APP_PASSWORD = "your-16-char-app-password"  # Use Gmail App Password, NOT your regular password

    today = datetime.now().strftime("%B %d, %Y")

    updates = [
        "Completed the API refactoring for the payment module",
        "Fixed 3 critical bugs reported by QA team",
        "Started working on the dashboard redesign",
    ]

    bullet_points = "".join(f"<li>{item}</li>" for item in updates)

    html_body = f"""
    <html>
    <body style="font-family: Arial, sans-serif; line-height: 1.6;">
        <h2>Weekly Status Update - {today}</h2>
        <p>Hi Team,</p>
        <p>Here is my update for this week:</p>
        <ul>{bullet_points}</ul>
        <p>Best regards,<br>Priya</p>
    </body>
    </html>
    """

    send_email(SENDER, APP_PASSWORD, "team@company.com", f"Weekly Update - {today}", html_body)

Important: Never use your actual Gmail password. Go to Google Account settings, enable 2-Step Verification, generate an App Password. That 16-character code goes into the script. In production, store it in an environment variable — hardcoding credentials is terrible practice even for personal scripts.


4. Web Scraper for Price Tracking

I've been tracking gadget prices on Amazon and Flipkart for months with this script. Fetches the product page, extracts the current price, logs it to a CSV. When the price drops below my target, it alerts me.

The Code

import requests
from bs4 import BeautifulSoup
import csv
from datetime import datetime
from pathlib import Path

def get_price(url, headers):
    """Scrape product price from a webpage."""
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.content, 'html.parser')

    # Amazon India price selector
    price_element = soup.select_one('span.a-price-whole')
    if price_element:
        price_text = price_element.get_text().replace(',', '').replace('.', '').strip()
        return int(price_text)

    # Flipkart price selector (fallback)
    price_element = soup.select_one('div._30jeq3')
    if price_element:
        price_text = price_element.get_text().replace('₹', '').replace(',', '').strip()
        return int(price_text)

    return None

def track_price(url, product_name, target_price):
    """Track price and log to CSV."""
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                       '(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Accept-Language': 'en-IN,en;q=0.9',
    }

    price = get_price(url, headers)
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")

    if price:
        log_file = Path("price_log.csv")
        file_exists = log_file.exists()

        with open(log_file, 'a', newline='') as f:
            writer = csv.writer(f)
            if not file_exists:
                writer.writerow(['Timestamp', 'Product', 'Price (INR)', 'Target', 'Below Target'])
            writer.writerow([timestamp, product_name, price, target_price, price <= target_price])

        print(f"[{timestamp}] {product_name}: ₹{price:,}")

        if price <= target_price:
            print(f"  ALERT: Price is at or below your target of ₹{target_price:,}!")
            return True
    else:
        print(f"Could not fetch price for {product_name}")

    return False

if __name__ == "__main__":
    products = [
        {
            'url': 'https://www.amazon.in/dp/PRODUCT_ID',
            'name': 'Sony WH-1000XM5',
            'target': 22000
        },
    ]

    for product in products:
        track_price(product['url'], product['name'], product['target'])

You'll need:

pip install requests beautifulsoup4

Word of caution: web scraping breaks when sites change their HTML structure. I check this script every couple of weeks and update CSS selectors if needed. Also, be respectful — don't hit the same page hundreds of times an hour. I run it once daily via a cron job and that's been perfectly fine.


5. PDF Merger

Combining multiple PDF files into one. I do this surprisingly often — invoices, scanned documents, project reports. This script merges any number of PDFs in whatever order you specify.

The Code

from PyPDF2 import PdfMerger
from pathlib import Path

def merge_pdfs(pdf_list, output_filename="merged_output.pdf"):
    """Merge multiple PDF files into one."""
    merger = PdfMerger()

    for pdf_path in pdf_list:
        path = Path(pdf_path)
        if path.exists() and path.suffix.lower() == '.pdf':
            merger.append(str(path))
            print(f"Added: {path.name}")
        else:
            print(f"Skipped (not found or not PDF): {pdf_path}")

    merger.write(output_filename)
    merger.close()
    print(f"\nMerged PDF saved as: {output_filename}")

def merge_folder(folder_path, output_filename="merged_output.pdf"):
    """Merge all PDFs in a folder, sorted by filename."""
    folder = Path(folder_path)
    pdf_files = sorted(folder.glob("*.pdf"))

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

    print(f"Found {len(pdf_files)} PDF files:")
    for f in pdf_files:
        print(f"  - {f.name}")

    merge_pdfs([str(f) for f in pdf_files], output_filename)

if __name__ == "__main__":
    choice = input("Merge (1) specific files or (2) entire folder? ").strip()

    if choice == '1':
        files = input("Enter PDF file paths (comma-separated): ").strip().split(',')
        files = [f.strip() for f in files]
        output = input("Output filename (default: merged_output.pdf): ").strip() or "merged_output.pdf"
        merge_pdfs(files, output)
    else:
        folder = input("Enter folder path: ").strip()
        output = input("Output filename (default: merged_output.pdf): ").strip() or "merged_output.pdf"
        merge_folder(folder, output)

Install the dependency:

pip install PyPDF2

I use merge_folder most often. It picks up all PDFs in alphabetical order, which works great if you name files with numeric prefixes like 01_intro.pdf, 02_chapter1.pdf.


6. CSV to Excel Converter with Formatting

Raw CSVs are ugly. When I need to share data with non-technical colleagues, I convert them to formatted Excel files — styled headers, auto-adjusted column widths, alternating row colours. Makes the output look surprisingly professional.

The Code

import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from pathlib import Path

def csv_to_excel(csv_path, output_path=None):
    """Convert CSV to a formatted Excel file."""
    csv_file = Path(csv_path)
    if output_path is None:
        output_path = csv_file.with_suffix('.xlsx')

    # Read CSV
    df = pd.read_csv(csv_file)

    # Write to Excel
    df.to_excel(output_path, index=False, sheet_name='Data')

    # Now apply formatting
    wb = load_workbook(output_path)
    ws = wb['Data']

    # Header styling
    header_font = Font(bold=True, color="FFFFFF", size=11)
    header_fill = PatternFill(start_color="2563EB", end_color="2563EB", fill_type="solid")
    header_alignment = Alignment(horizontal="center", vertical="center")
    thin_border = Border(
        left=Side(style='thin'),
        right=Side(style='thin'),
        top=Side(style='thin'),
        bottom=Side(style='thin')
    )

    for cell in ws[1]:
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = header_alignment
        cell.border = thin_border

    # Auto-adjust column widths
    for column in ws.columns:
        max_length = 0
        column_letter = column[0].column_letter
        for cell in column:
            if cell.value:
                max_length = max(max_length, len(str(cell.value)))
            cell.border = thin_border
        adjusted_width = min(max_length + 4, 50)
        ws.column_dimensions[column_letter].width = adjusted_width

    # Alternate row coloring
    light_fill = PatternFill(start_color="F0F7FF", end_color="F0F7FF", fill_type="solid")
    for row_idx, row in enumerate(ws.iter_rows(min_row=2, max_row=ws.max_row), start=2):
        if row_idx % 2 == 0:
            for cell in row:
                cell.fill = light_fill

    wb.save(output_path)
    print(f"Formatted Excel file saved: {output_path}")

if __name__ == "__main__":
    csv_file = input("Enter CSV file path: ").strip()
    csv_to_excel(csv_file)

Dependencies:

pip install pandas openpyxl

The alternating row colours and auto-adjusted widths make the output look way more polished than it has any right to. My manager once asked what tool I use to generate "those nice Excel reports" and I just smiled.


7. System Backup Script

Creates compressed backups of important folders. Keeps only the last N backups so it doesn't eat up all your disk space. Simple, reliable, and something everyone should be running.

The Code

import shutil
from pathlib import Path
from datetime import datetime

def create_backup(source_dirs, backup_root, max_backups=5):
    """Create a timestamped compressed backup of specified directories."""
    backup_root = Path(backup_root)
    backup_root.mkdir(parents=True, exist_ok=True)

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

    for source in source_dirs:
        source_path = Path(source)
        if not source_path.exists():
            print(f"Source not found, skipping: {source}")
            continue

        folder_name = source_path.name
        archive_name = f"{folder_name}_{timestamp}"
        archive_path = backup_root / archive_name

        print(f"Backing up: {source_path} ...")
        shutil.make_archive(
            str(archive_path),
            'zip',
            root_dir=str(source_path.parent),
            base_dir=source_path.name
        )

        archive_size = Path(f"{archive_path}.zip").stat().st_size / (1024 * 1024)
        print(f"  Created: {archive_name}.zip ({archive_size:.1f} MB)")

    # Cleanup old backups
    cleanup_old_backups(backup_root, max_backups)

def cleanup_old_backups(backup_root, max_backups):
    """Remove old backups, keeping only the most recent ones per source folder."""
    all_zips = sorted(backup_root.glob("*.zip"), key=lambda f: f.stat().st_mtime, reverse=True)

    # Group by source folder name (everything before the timestamp)
    from collections import defaultdict
    groups = defaultdict(list)
    for zip_file in all_zips:
        # Extract the folder name part (before _YYYYMMDD_HHMMSS)
        parts = zip_file.stem.rsplit('_', 2)
        if len(parts) >= 3:
            group_name = parts[0]
        else:
            group_name = zip_file.stem
        groups[group_name].append(zip_file)

    for group_name, files in groups.items():
        if len(files) > max_backups:
            for old_file in files[max_backups:]:
                old_file.unlink()
                print(f"  Removed old backup: {old_file.name}")

if __name__ == "__main__":
    folders_to_backup = [
        r"C:\Users\priya\Documents\Projects",
        r"C:\Users\priya\Documents\Notes",
        r"C:\Users\priya\Desktop\WorkFiles",
    ]

    backup_destination = r"D:\Backups"

    create_backup(folders_to_backup, backup_destination, max_backups=5)
    print("\nBackup complete!")

No external dependencies — standard library only. I run this every night at 11 PM via Windows Task Scheduler. max_backups=5 keeps the five most recent backups per folder and auto-deletes older ones. Set it up once and forget about it.


8. WhatsApp Message Scheduler (Concept)

Full honesty here — automating WhatsApp is a gray area. WhatsApp doesn't officially support bots for personal accounts, and unofficial APIs can get your number banned. But this concept script using pywhatkit is handy for one-off scheduled messages. Use it responsibly.

The Code

import pywhatkit as kit
from datetime import datetime, timedelta

def schedule_whatsapp_message(phone_number, message, send_time=None):
    """
    Schedule a WhatsApp message using pywhatkit.
    phone_number should include country code (e.g., +91XXXXXXXXXX)
    send_time is a datetime object; if None, sends 2 minutes from now.
    """
    if send_time is None:
        send_time = datetime.now() + timedelta(minutes=2)

    hour = send_time.hour
    minute = send_time.minute

    print(f"Scheduling message to {phone_number} at {hour:02d}:{minute:02d}")
    print(f"Message: {message}")

    kit.sendwhatmsg(
        phone_no=phone_number,
        message=message,
        time_hour=hour,
        time_min=minute,
        wait_time=15,
        tab_close=True
    )
    print("Message sent successfully!")

if __name__ == "__main__":
    # Example: Send birthday wishes at midnight
    schedule_whatsapp_message(
        phone_number="+919876543210",
        message="Happy Birthday! 🎂 Wishing you an amazing year ahead!",
        send_time=datetime(2026, 2, 15, 0, 0)  # Feb 15 at midnight
    )
pip install pywhatkit

Works by opening WhatsApp Web in your browser and typing the message automatically. Requires you to be logged into WhatsApp Web with your computer running at the scheduled time. Not elegant. But for birthday wishes or reminder messages, it gets the job done. For anything more serious, look into the WhatsApp Business API — that's the official and proper approach.


9. Password Generator

I know password managers can generate passwords. But sometimes I need a quick batch of strong passwords — for test accounts, temporary access, or offline storage. This script generates them with customizable rules.

The Code

import secrets
import string
from typing import Optional

def generate_password(
    length: int = 20,
    uppercase: bool = True,
    lowercase: bool = True,
    digits: bool = True,
    special: bool = True,
    exclude_ambiguous: bool = True,
    min_uppercase: int = 1,
    min_digits: int = 1,
    min_special: int = 1
) -> str:
    """Generate a cryptographically secure password."""

    chars = ""
    if lowercase:
        chars += string.ascii_lowercase
    if uppercase:
        chars += string.ascii_uppercase
    if digits:
        chars += string.digits
    if special:
        chars += "!@#$%^&*()-_=+[]{}|;:,.<>?"

    if exclude_ambiguous:
        ambiguous = "0OoIl1"
        chars = ''.join(c for c in chars if c not in ambiguous)

    if not chars:
        raise ValueError("At least one character type must be enabled")

    # Keep generating until all minimums are met
    while True:
        password = ''.join(secrets.choice(chars) for _ in range(length))

        has_upper = sum(1 for c in password if c in string.ascii_uppercase) >= min_uppercase
        has_digit = sum(1 for c in password if c in string.digits) >= min_digits
        has_special = sum(1 for c in password if c in "!@#$%^&*()-_=+[]{}|;:,.<>?") >= min_special

        if has_upper and has_digit and has_special:
            return password

def generate_batch(count: int = 10, **kwargs) -> list:
    """Generate multiple passwords."""
    return [generate_password(**kwargs) for _ in range(count)]

if __name__ == "__main__":
    print("=== Password Generator ===\n")

    count = int(input("How many passwords? (default 5): ").strip() or "5")
    length = int(input("Password length? (default 20): ").strip() or "20")

    passwords = generate_batch(count=count, length=length)

    print(f"\nGenerated {count} passwords ({length} chars each):\n")
    for i, pwd in enumerate(passwords, 1):
        print(f"  {i}. {pwd}")

    # Calculate entropy
    import math
    charset_size = 72  # approximate usable charset after ambiguous removal
    entropy = length * math.log2(charset_size)
    print(f"\n  Estimated entropy: {entropy:.0f} bits")
    print(f"  (128+ bits is considered very strong)")

Uses Python's secrets module rather than random — and this matters a lot. random isn't cryptographically secure; it uses a predictable pseudorandom number generator. secrets pulls from your OS's cryptographic random source, making passwords truly unpredictable. The exclude_ambiguous flag removes characters like 0/O and 1/l/I that look similar in many fonts — lifesaver when you need to read a password aloud or type it manually.


10. Auto-Downloader for YouTube Playlists

I often download educational playlists for offline viewing during travel. This script uses yt-dlp (the maintained fork of youtube-dl) to download entire playlists with sensible defaults.

The Code

import subprocess
import sys
from pathlib import Path

def download_playlist(url, output_dir="downloads", quality="best", audio_only=False):
    """Download a YouTube playlist using yt-dlp."""
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)

    # Output template: PlaylistName/01 - VideoTitle.ext
    output_template = str(output_path / "%(playlist_title)s" / "%(playlist_index)02d - %(title)s.%(ext)s")

    cmd = [
        sys.executable, "-m", "yt_dlp",
        "--output", output_template,
        "--no-overwrites",
        "--add-metadata",
        "--embed-thumbnail",
        "--progress",
    ]

    if audio_only:
        cmd.extend([
            "--extract-audio",
            "--audio-format", "mp3",
            "--audio-quality", "0",
        ])
    else:
        if quality == "best":
            cmd.extend(["-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best"])
        elif quality == "720p":
            cmd.extend(["-f", "bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720]"])
        elif quality == "480p":
            cmd.extend(["-f", "bestvideo[height<=480][ext=mp4]+bestaudio[ext=m4a]/best[height<=480]"])

    cmd.append(url)

    print(f"Downloading {'audio' if audio_only else 'video'} from: {url}")
    print(f"Output directory: {output_path.absolute()}")
    print(f"Quality: {'MP3 audio' if audio_only else quality}\n")

    try:
        subprocess.run(cmd, check=True)
        print("\nDownload complete!")
    except subprocess.CalledProcessError as e:
        print(f"\nDownload failed with error code: {e.returncode}")
    except FileNotFoundError:
        print("yt-dlp not found. Install it with: pip install yt-dlp")

if __name__ == "__main__":
    url = input("Enter YouTube playlist or video URL: ").strip()

    print("\nQuality options:")
    print("  1. Best available")
    print("  2. 720p")
    print("  3. 480p")
    print("  4. Audio only (MP3)")

    choice = input("\nChoice (1-4, default 1): ").strip() or "1"

    quality_map = {"1": "best", "2": "720p", "3": "480p"}
    audio_only = choice == "4"
    quality = quality_map.get(choice, "best")

    output = input("Output directory (default: downloads): ").strip() or "downloads"

    download_playlist(url, output, quality, audio_only)
pip install yt-dlp

Output template creates a neat folder structure — each playlist gets its own folder, videos numbered by playlist index. --no-overwrites means you can re-run the script if it gets interrupted and it'll skip already-downloaded files. --embed-thumbnail adds the video thumbnail to file metadata, so files look nice in file managers and media players.

Legal note: Only download content you have the right to download. Many educational creators also offer offline viewing through YouTube Premium — that's the officially supported way.


How I Keep Them Organized

All my automation scripts live in one folder called ~/scripts/ with a simple naming convention:

ScriptFilenameSchedule
File Organizerorganize_downloads.pyWeekly (Sunday 8 PM)
Image Resizerresize_images.pyOn demand
Email Senderweekly_update.pyWeekly (Friday 5 PM)
Price Trackertrack_prices.pyDaily (9 AM)
PDF Mergermerge_pdfs.pyOn demand
CSV to Excelcsv_to_xlsx.pyOn demand
System Backupbackup.pyDaily (11 PM)
WhatsApp Schedulerwa_schedule.pyOn demand
Password Generatorgen_passwords.pyOn demand
Auto-Downloaderdownload_playlist.pyOn demand

Scheduled scripts run through Windows Task Scheduler (my daily driver's Windows) or cron on my Linux server. Having the right editor matters when writing and debugging these — our best VS Code extensions for 2026 list includes several that speed up Python development noticeably. Route all output to log files so you can check when something goes wrong:

# Example cron entry (Linux)
0 9 * * * /usr/bin/python3 /home/priya/scripts/track_prices.py >> /home/priya/scripts/logs/price_tracker.log 2>&1

Tips I've Learned the Hard Way

Keep scripts simple. Best automation scripts are the ones you can read six months later and immediately understand. Resist the urge to over-engineer them.

Add error handling. A script that crashes silently is worse than no script. At minimum, wrap main logic in a try-except and log the error somewhere.

Use pathlib over os.path. The pathlib module is cleaner, more readable, more Pythonic. There's almost no reason to use os.path.join() anymore when Path(folder) / "subfolder" / "file.txt" exists.

Store secrets properly. Use environment variables or a .env file with python-dotenv for API keys, passwords, sensitive data. Never hardcode them, even for "personal use only." Habits matter more than intent.

Version control your scripts. I keep my scripts folder in a private Git repo. It's saved me more than once when I accidentally broke something during an edit. If you're not comfortable with Git yet, our Git and GitHub mastery guide covers everything you'd need.


Just Start With What Annoys You

Automation's one of those skills where the investment pays back exponentially. The file organizer took maybe 30 minutes to write. It's saved me countless hours over two years. The price tracker's helped me catch deals I would've completely missed.

You don't need to be an advanced Python developer to write scripts like these. Most use straightforward logic — loops, conditionals, file operations — combined with a few well-chosen libraries. Python's ecosystem is ridiculously rich, and there's a library for almost anything you'd want to automate. Python's dominance in this space is a big reason it consistently tops the charts among the most popular programming languages. And if you're running scripts on a Linux server, our Linux for developers guide covers the command-line basics that make managing cron jobs much easier.

My advice? Start with whatever task annoys you the most. The one where you think "ugh, I have to do this again?" That's your first automation candidate. Write the script, test it a few times, schedule it or keep it within reach. Once you've watched a computer do your tedious work for you — once you've experienced that specific satisfaction of a task finishing while you were making coffee — you'll start noticing automation opportunities everywhere. It's a little addictive, honestly. I've got scripts for things that probably aren't worth automating. But that's between me and my cron tab.

Share

Priya Patel

Senior Tech Writer

AI and machine learning specialist with 6 years covering emerging technologies. Previously a senior tech correspondent at TechCrunch India, she now writes in-depth analyses of AI tools, LLM developments, and their real-world applications for Indian businesses.

Stay Ahead in Tech

Get the latest tech news, tutorials, and reviews delivered straight to your inbox every week.

No spam ever. Unsubscribe anytime.

Comments (0)

Leave a Comment

All comments are moderated before appearing. Please be respectful and follow our community guidelines.

Related Articles