Introduction
Every developer has tasks they do manually that they know should be automated: renaming hundreds of files, sending weekly report emails, downloading data from a website, or backing up a folder. Python is the best tool for all of this.
In this guide, we'll build four real automation scripts:
- Bulk file organizer
- Web scraper for data collection
- Automated email sender
- Task scheduler
Each script is standalone and production-ready.
1. Automate File Organization
If your Downloads folder looks like a crime scene, this script will clean it up automatically. It sorts files into subfolders based on their extension:
import shutil
from pathlib import Path
FOLDER_MAP = {
"Images": [".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp"],
"Documents": [".pdf", ".docx", ".doc", ".txt", ".xlsx", ".csv"],
"Videos": [".mp4", ".mov", ".avi", ".mkv"],
"Audio": [".mp3", ".wav", ".flac", ".aac"],
"Code": [".py", ".js", ".ts", ".html", ".css", ".json"],
"Archives": [".zip", ".tar", ".gz", ".rar"],
}
def get_destination(extension: str) -> str:
for folder, extensions in FOLDER_MAP.items():
if extension.lower() in extensions:
return folder
return "Other"
def organize_folder(folder_path: str) -> dict:
folder = Path(folder_path)
moved = {}
for file in folder.iterdir():
if not file.is_file():
continue
destination_name = get_destination(file.suffix)
destination = folder / destination_name
destination.mkdir(exist_ok=True)
new_path = destination / file.name
# Handle duplicates
counter = 1
while new_path.exists():
stem = file.stem
new_path = destination / f"{stem}_{counter}{file.suffix}"
counter += 1
shutil.move(str(file), str(new_path))
moved[str(file)] = str(new_path)
return moved
if __name__ == "__main__":
downloads = Path.home() / "Downloads"
result = organize_folder(str(downloads))
print(f"Moved {len(result)} files.")
for src, dst in result.items():
print(f" {Path(src).name} → {Path(dst).parent.name}/")
Run this once and it will sort everything. Schedule it to run weekly and your Downloads folder stays clean forever.
2. Web Scraping for Data Collection
Let's scrape job listings from a public jobs board. We'll use requests and BeautifulSoup — the two most practical scraping libraries:
import time
import csv
import requests
from bs4 import BeautifulSoup
from dataclasses import dataclass, fields, astuple
@dataclass
class Job:
title: str
company: str
location: str
url: str
posted: str
def scrape_python_jobs(base_url: str, pages: int = 3) -> list[Job]:
jobs = []
headers = {"User-Agent": "Mozilla/5.0 (compatible; JobScraper/1.0)"}
for page in range(1, pages + 1):
url = f"{base_url}?page={page}"
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
listings = soup.select(".job-listing") # Adjust selector to target site
for listing in listings:
job = Job(
title=listing.select_one(".job-title").get_text(strip=True),
company=listing.select_one(".company-name").get_text(strip=True),
location=listing.select_one(".location").get_text(strip=True),
url=listing.select_one("a")["href"],
posted=listing.select_one(".date-posted").get_text(strip=True),
)
jobs.append(job)
print(f"Page {page}: found {len(listings)} jobs")
time.sleep(1) # Be respectful to the server
return jobs
def save_to_csv(jobs: list[Job], filename: str):
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow([f.name for f in fields(Job)])
writer.writerows([astuple(job) for job in jobs])
if __name__ == "__main__":
all_jobs = scrape_python_jobs("https://example-jobs.com/python", pages=5)
save_to_csv(all_jobs, "python_jobs.csv")
print(f"Saved {len(all_jobs)} jobs to python_jobs.csv")
Always respect robots.txt and add delays between requests. Never hammer a server.
3. Automated Email Sending
Sending weekly reports, notifications, or digest emails manually is a time sink. Python's smtplib plus Gmail handles this perfectly:
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from pathlib import Path
class EmailSender:
def __init__(self, gmail_address: str, app_password: str):
self.address = gmail_address
self.password = app_password
def send(
self,
to: list[str],
subject: str,
body_html: str,
attachments: list[str] | None = None,
) -> bool:
msg = MIMEMultipart("alternative")
msg["From"] = self.address
msg["To"] = ", ".join(to)
msg["Subject"] = subject
msg.attach(MIMEText(body_html, "html"))
for filepath in (attachments or []):
path = Path(filepath)
if not path.exists():
continue
part = MIMEBase("application", "octet-stream")
part.set_payload(path.read_bytes())
encoders.encode_base64(part)
part.add_header("Content-Disposition", f'attachment; filename="{path.name}"')
msg.attach(part)
try:
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(self.address, self.password)
server.sendmail(self.address, to, msg.as_string())
return True
except Exception as e:
print(f"Email failed: {e}")
return False
def send_weekly_report(recipients: list[str], stats: dict):
sender = EmailSender(
gmail_address=os.environ["GMAIL_ADDRESS"],
app_password=os.environ["GMAIL_APP_PASSWORD"],
)
html = f"""
<h2>Weekly Report</h2>
<table border="1" cellpadding="8" style="border-collapse: collapse;">
<tr><td><b>Total Users</b></td><td>{stats['users']}</td></tr>
<tr><td><b>New Signups</b></td><td>{stats['signups']}</td></tr>
<tr><td><b>Revenue</b></td><td>${stats['revenue']}</td></tr>
</table>
<p>Generated automatically by Python.</p>
"""
success = sender.send(recipients, "Weekly Report", html)
print("Report sent!" if success else "Failed to send report.")
For Gmail, use an App Password (not your regular password). Enable 2FA first, then generate one at myaccount.google.com/apppasswords.
4. Scheduling Tasks with Python
Instead of relying on cron or Task Scheduler, you can run Python-based schedules within your script:
import schedule
import time
import logging
from datetime import datetime
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s — %(message)s",
datefmt="%Y-%m-%d %H:%M",
)
def organize_downloads():
logging.info("Running: organize downloads")
# Call your organize_folder() here
logging.info("Done: downloads organized")
def send_daily_report():
logging.info("Sending daily report...")
# Call your send_weekly_report() here
logging.info("Done: report sent")
def backup_database():
logging.info("Backing up database...")
# pg_dump or similar
logging.info("Done: backup complete")
# Configure your schedule
schedule.every().monday.at("09:00").do(organize_downloads)
schedule.every().day.at("18:00").do(send_daily_report)
schedule.every().day.at("02:00").do(backup_database)
schedule.every(30).minutes.do(lambda: logging.info("Heartbeat — scheduler running"))
if __name__ == "__main__":
logging.info("Scheduler started")
while True:
schedule.run_pending()
time.sleep(60)
Run this in the background on your server or local machine with:
nohup python scheduler.py > scheduler.log 2>&1 &
Or wrap it in a systemd service on Linux for automatic startup on reboot.
Putting It All Together
You've now got four automation scripts that solve real problems:
| Script | Time saved | Complexity |
|---|---|---|
| File organizer | 10 min/week | Low |
| Web scraper | Hours of manual data entry | Medium |
| Email sender | 30 min/week | Low |
| Task scheduler | Eliminates manual reminders | Medium |
The real power of automation isn't any single script — it's the habit of recognizing repeated work and writing a script for it. Next time you find yourself doing something for the third time, open a Python file.
Install the required packages:
pip install requests beautifulsoup4 schedule
These four scripts are your foundation. Combine them, extend them, and build bigger automations on top. That's how you turn Python into a personal productivity superpower.