Automation
This is an automation program utilizing Python.
Run This Page Locally – Python Setup Instructions
This page is designed as a reference and learning resource. The "Run" button uses Pyodide (Python in your browser) and can only execute pure Python code. For the advanced automation examples (Selenium, OpenCV, psutil, watchdog, etc.), you must run them locally on your own computer.
Step 1: Install Python dependencies
pip install schedule apscheduler requests beautifulsoup4 openpyxl psutil pyautogui selenium opencv-python watchdog
Built-in modules (no install needed): os, pathlib, shutil, subprocess, smtplib, time, datetime
For ADB phone automation: install Android Platform Tools
Copy any code snippet using the Copy button,
paste it into a .py file, and run with python filename.py.
Everything you need to automate tasks with Python: file operations, OS tasks, scheduling, web scraping, email, Excel, GUI automation, website automation, phone automation, CCTV automation, and real-world scripts. 28 interactive lessons — crafted by @PoeticGlenn.
What you will learn
How to automate repetitive tasks, manipulate files and directories, run external commands, schedule jobs, scrape websites, send emails, work with Excel, monitor systems, control the GUI, drive real web browsers, control an Android phone from a script, detect motion on a CCTV/webcam feed, and watch folders for new work — all with Python. Every program below is explained line by line so you understand exactly what each line does, not just what it produces.
Why Python Is the King of Automation
Python is the language of automation. From simple file renaming to complex robotic process automation (RPA), Python's ecosystem of libraries makes it the go‑to choice for developers, system administrators, data engineers, and anyone who wants to stop doing repetitive tasks manually. @PoeticGlenn designed this guide to take you from your first automation script to building robust, production‑ready automation systems that run on schedule, monitor your infrastructure, drive real browsers, control phones, watch cameras, and integrate with everything from spreadsheets to the web.
Save Hours
Automate repetitive tasks and focus on creative, high‑value work instead of manual drudgery.
Full Stack
From files and OS to web, email, GUI, phones, and cameras — one language to automate everything.
Production Ready
Build scheduled, monitored, and robust automation pipelines that run reliably.
Automation isn't just about writing code — it's about eliminating toil. Every script you write saves minutes, hours, or even days of manual work over its lifetime. ⚙️
Automation Is the Superpower Every Developer Needs
If you've ever done the same task twice, you've already felt the need for automation. This curriculum, curated by @PoeticGlenn, teaches you the essential skills to turn Python into your personal automation engine. Here's why these skills are transformative.
⏱️ Time Is Money
Every minute spent on repetitive work is a minute not spent on innovation. Automation scripts pay for themselves in hours, days, or weeks of saved time.
🎯 Reliability
Humans make mistakes. Automation does the same thing, the same way, every time. Eliminate human error from routine tasks.
📈 Scale
What works for one file works for a thousand. Automation lets you scale your efforts without scaling your effort.
🔗 Integration
Python connects everything — files, databases, APIs, email, spreadsheets, phones, and cameras. Build bridges between systems that don't talk to each other.
Automation Is the Engine of Modern Infrastructure
In the broader programming ecosystem, automation is not a niche skill — it's a core competency for every developer, DevOps engineer, data scientist, and system administrator. This curriculum, designed by @PoeticGlenn, reflects the patterns and tools used in millions of production environments worldwide.
DevOps & SRE
Automation is the backbone of CI/CD, infrastructure-as-code, and site reliability. Python scripts orchestrate deployments, monitor systems, and respond to incidents.
Data Engineering
ETL pipelines, data cleaning, and report generation are automation at scale. Python moves data between sources, transforms it, and loads it where it's needed.
Business Process
From invoice processing to inventory management, Python automation saves companies millions by eliminating manual data entry and reconciliation.
The curriculum you're about to explore is not academic — it's the practical foundation used by thousands of open‑source projects and Fortune 500 companies to automate their operations.
📑 Table of Contents
Jump to any section — all entries are equal size
📂 Working with Paths — os and pathlib
Every automation script starts with navigating the filesystem. Learn the modern way with pathlib.
📂 1.1 pathlib — Object‑Oriented Paths
pathlib provides an object‑oriented interface to filesystem paths. It's cleaner, more readable, and cross‑platform.
Step-by-step explanation:
- Step 1: Import
Pathfrompathlib. - Step 2: Create a
Pathobject pointing to the current directory usingPath.cwd(). - Step 3: Use
path.exists(),path.is_dir(), andpath.is_file()to check properties. - Step 4: List all files in the current directory using
path.iterdir(). - Step 5: Use
path / "subfolder" / "file.txt"to build paths — the/operator is overloaded. - Step 6: Access the parent directory with
.parent, file name with.name, and extension with.suffix.
from pathlib import Path def pathlib_demo(): cwd = Path.cwd() print(f"Current directory: {cwd}") print(f"Exists: {cwd.exists()}") print(f"Is directory: {cwd.is_dir()}") print("\nContents:") for item in cwd.iterdir(): print(f" {item.name} ({'dir' if item.is_dir() else 'file'})") sub = cwd / "data" / "config.json" print(f"\nBuilt path: {sub}") print(f"Parent: {sub.parent}") print(f"Name: {sub.name}") print(f"Suffix: {sub.suffix}") pathlib_demo()
pathlib handles path separators automatically — your code works on Windows, macOS, and Linux without modification.📂 1.2 os Module — Traditional Path Operations
The os module provides low‑level system functions. Still widely used in legacy code and for operations not yet in pathlib.
Step-by-step explanation:
- Step 1: Import
os— the standard library for operating system interfaces. - Step 2: Use
os.getcwd()to get the current working directory (string). - Step 3: Use
os.listdir()to list files in a directory. - Step 4: Use
os.path.join()to build paths — this is cross‑platform. - Step 5: Use
os.path.exists(),os.path.isdir(), andos.path.isfile()to check properties. - Step 6: Use
os.environto access environment variables — useful for configuration.
import os def os_demo(): cwd = os.getcwd() print(f"Current directory: {cwd}") print("\nFiles (os.listdir):") for name in os.listdir(cwd): path = os.path.join(cwd, name) if os.path.isdir(path): print(f" 📁 {name}") else: print(f" 📄 {name}") config_path = os.path.join(cwd, "config", "settings.ini") print(f"\nBuilt path: {config_path}") print(f"Exists: {os.path.exists(config_path)}") print("\nEnvironment variables (first 3):") env = os.environ for i, (k, v) in enumerate(env.items()): if i >= 3: break print(f" {k}={v[:30]}...") os_demo()
pathlib for new code — it's more intuitive and less error‑prone. Use os when you need specific system calls.📄 File Operations — Create, Read, Write, Delete
Automation is mostly about files. Learn to create, read, write, move, and delete files programmatically.
📄 2.1 Reading and Writing Text Files
Use open() with context managers to read and write text files safely.
Step-by-step explanation:
- Step 1: Define a filename and some sample content.
- Step 2: Use
with open(...)to write to the file ("w"mode). - Step 3: Use
with open(...)to read the file ("r"mode). - Step 4: Read the entire content with
read()or line by line withreadlines(). - Step 5: Append to a file with
"a"mode. - Step 6: Delete the file using
Path.unlink().
from pathlib import Path def file_rw_demo(): filename = "automation_demo.txt" path = Path(filename) content = "Hello from Python automation!\nLine 2 of content.\n" with open(path, "w", encoding="utf-8") as f: f.write(content) print(f"✅ Wrote {len(content)} characters to {filename}") with open(path, "r", encoding="utf-8") as f: data = f.read() print(f"📄 Read content: {repr(data)}") with open(path, "a", encoding="utf-8") as f: f.write("Appended line!\n") print("✅ Appended a line") with open(path, "r", encoding="utf-8") as f: lines = f.readlines() print(f"📄 Lines: {lines}") path.unlink(missing_ok=True) print("🧹 Deleted file") file_rw_demo()
📄 2.2 Working with Directories
Create, rename, list, and delete directories — essential for any automation script.
Step-by-step explanation:
- Step 1: Create a directory with
Path.mkdir(). - Step 2: Check if a directory exists with
Path.exists(). - Step 3: Create nested directories with
parents=True. - Step 4: List directory contents with
iterdir(). - Step 5: Remove an empty directory with
Path.rmdir(). - Step 6: Remove a directory and all its contents with
shutil.rmtree().
from pathlib import Path import shutil def dir_demo(): base = Path.cwd() / "automation_test" nested = base / "sub" / "subsub" nested.mkdir(parents=True, exist_ok=True) print(f"✅ Created: {nested}") (base / "sub" / "hello.txt").write_text("Hello nested dir!") print("\n📂 Directory tree:") for item in base.rglob("*"): indent = " " * (len(item.relative_to(base).parts) - 1) if item.is_file(): print(f"{indent}📄 {item.name}") else: print(f"{indent}📁 {item.name}") shutil.rmtree(base, ignore_errors=True) print(f"\n🧹 Deleted: {base}") dir_demo()
📦 shutil — High‑Level File Operations
shutil provides copy, move, archive, and delete operations that work across filesystems.
📦 3.1 Copy, Move, and Rename
Copy files and directories, move them, or rename them — all with shutil.
Step-by-step explanation:
- Step 1: Import
shutilandpathlib. - Step 2: Create a source file with some content.
- Step 3: Use
shutil.copy2()to copy with metadata preservation. - Step 4: Use
shutil.move()to move or rename a file. - Step 5: Copy an entire directory tree with
shutil.copytree(). - Step 6: Clean up by removing created files and directories.
from pathlib import Path import shutil def shutil_demo(): src = Path.cwd() / "source.txt" src.write_text("Original content") dest = Path.cwd() / "copy.txt" shutil.copy2(src, dest) print(f"✅ Copied {src} → {dest}") renamed = Path.cwd() / "renamed.txt" shutil.move(dest, renamed) print(f"✅ Moved {dest} → {renamed}") print(f"📄 {renamed} contains: {renamed.read_text()}") src.unlink() renamed.unlink() print("🧹 Cleaned up") shutil_demo()
📦 3.2 Archiving — ZIP and TAR
Create and extract archives programmatically — perfect for backups and distribution.
Step-by-step explanation:
- Step 1: Create a directory with some files to archive.
- Step 2: Use
shutil.make_archive()to create a ZIP archive. - Step 3: Use
shutil.unpack_archive()to extract the archive. - Step 4: List the contents of the extracted directory.
- Step 5: Clean up the temporary files.
from pathlib import Path import shutil def archive_demo(): base = Path.cwd() / "archive_test" base.mkdir(exist_ok=True) (base / "file1.txt").write_text("Content 1") (base / "file2.txt").write_text("Content 2") sub = base / "sub" sub.mkdir() (sub / "file3.txt").write_text("Content 3") archive_name = shutil.make_archive("my_archive", "zip", base) print(f"✅ Created archive: {archive_name}") extract_dir = Path.cwd() / "extracted" shutil.unpack_archive(archive_name, extract_dir) print(f"✅ Extracted to: {extract_dir}") print("\n📂 Extracted contents:") for item in extract_dir.rglob("*"): if item.is_file(): print(f" 📄 {item.relative_to(extract_dir)}") shutil.rmtree(base, ignore_errors=True) shutil.rmtree(extract_dir, ignore_errors=True) Path(archive_name).unlink(missing_ok=True) print("\n🧹 Cleaned up") archive_demo()
shutil.make_archive() supports "zip", "tar", "gztar", and "bztar" formats.💻 Running External Commands — subprocess
Automation often means orchestrating other programs. Learn to run, control, and capture output from external commands.
💻 4.1 subprocess.run() — The Modern Way
subprocess.run() is the recommended way to run external commands in Python 3.5+.
Step-by-step explanation:
- Step 1: Import
subprocess. - Step 2: Use
subprocess.run()with a list of arguments. - Step 3: Capture output with
capture_output=True. - Step 4: Access
result.stdoutandresult.stderras bytes or strings withtext=True. - Step 5: Check the return code with
result.returncode. - Step 6: Use
check=Trueto raise an exception on non‑zero exit codes.
import subprocess def subprocess_demo(): result = subprocess.run(["echo", "Hello from subprocess!"], capture_output=True, text=True) print(f"✅ Command output: {result.stdout.strip()}") print(f"Return code: {result.returncode}") try: subprocess.run(["false"], check=True) except subprocess.CalledProcessError as e: print(f"❌ Command failed with exit code: {e.returncode}") result = subprocess.run(["ls", "/nonexistent"], capture_output=True, text=True) print(f"stderr: {result.stderr.strip()}") subprocess_demo()
shell=True — but be careful with user input to avoid injection.💻 4.2 Piping and Advanced Control
Chain commands, set environment variables, and control timeouts.
Step-by-step explanation:
- Step 1: Use
subprocess.Popen()for more control. - Step 2: Pipe output from one command to another.
- Step 3: Set environment variables with the
envparameter. - Step 4: Set a timeout to prevent hanging.
- Step 5: Handle the timeout exception.
import subprocess import os def advanced_subprocess(): p1 = subprocess.Popen(["ls"], stdout=subprocess.PIPE, text=True) p2 = subprocess.Popen(["grep", ".py"], stdin=p1.stdout, stdout=subprocess.PIPE, text=True) p1.stdout.close() output = p2.communicate()[0] print("🔍 Python files:") print(output) env = os.environ.copy() env["MY_VAR"] = "automation" result = subprocess.run(["printenv", "MY_VAR"], env=env, capture_output=True, text=True) print(f"✅ MY_VAR = {result.stdout.strip()}") advanced_subprocess()
⏰ Scheduling Tasks — Run on a Schedule
Automation runs on its own schedule. Learn to schedule tasks with Python's schedule library and time.
⏰ 5.1 Basic Scheduling with schedule
The schedule library provides a human‑friendly API for scheduling tasks.
Step-by-step explanation:
- Step 1: Import
scheduleandtime. - Step 2: Define a function to run on schedule.
- Step 3: Use
schedule.every().syntax to set the schedule. - Step 4: In an infinite loop, call
schedule.run_pending(). - Step 5: Use
time.sleep(1)to avoid busy‑waiting. - Step 6: Run the scheduler for a limited number of iterations for demo purposes.
import schedule import time def scheduled_task(): print(f"⏰ Task ran at {time.strftime('%H:%M:%S')}") def scheduler_demo(): print("⏳ Starting scheduler (runs for 5 seconds)") schedule.every(2).seconds.do(scheduled_task) start = time.time() while time.time() - start < 5: schedule.run_pending() time.sleep(0.5) print("✅ Scheduler stopped") scheduler_demo()
schedule library supports seconds, minutes, hours, days, weeks, and specific times of day.⏰ 5.2 Cron‑Like Scheduling with apscheduler
apscheduler provides cron‑like scheduling with more advanced features.
Step-by-step explanation:
- Step 1: Import
BackgroundSchedulerfromapscheduler.schedulers.background. - Step 2: Import
CronTriggerfor cron‑style scheduling. - Step 3: Define a function to run on schedule.
- Step 4: Create a
BackgroundSchedulerand add a job. - Step 5: Start the scheduler.
- Step 6: Keep the program running for a few seconds.
- Step 7: Shut down the scheduler.
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger import time def cron_task(): print(f"⏰ Cron task ran at {time.strftime('%Y-%m-%d %H:%M:%S')}") def cron_demo(): scheduler = BackgroundScheduler() scheduler.add_job(cron_task, CronTrigger(second="*/2")) scheduler.start() print("⏳ Cron scheduler running for 6 seconds...") time.sleep(6) scheduler.shutdown() print("✅ Cron scheduler stopped") cron_demo()
apscheduler library is not installed in the Pyodide environment by default. This demo shows the pattern — in a real environment, you'd install it with pip.🌐 Web Scraping — Extract Data from Websites
Automate data collection from the web using requests and BeautifulSoup.
🌐 6.1 Fetching Web Pages with requests
Use the requests library to fetch web pages and APIs.
Step-by-step explanation:
- Step 1: Import the
requestsmodule. - Step 2: Use
requests.get()to fetch a URL. - Step 3: Check the status code with
response.status_code. - Step 4: Access the content with
response.text(HTML) orresponse.json()(API). - Step 5: Handle errors with
response.raise_for_status().
import requests def requests_demo(): url = "https://httpbin.org/get" response = requests.get(url) print(f"✅ Status: {response.status_code}") data = response.json() print(f"📡 Origin: {data.get('origin')}") print(f"📡 URL: {data.get('url')}") params = {"name": "automation", "value": 42} response = requests.get(url, params=params) data = response.json() print(f"📡 Params: {data.get('args')}") requests_demo()
🌐 6.2 Parsing HTML with BeautifulSoup
Use BeautifulSoup to parse HTML and extract structured data.
Step-by-step explanation:
- Step 1: Import
BeautifulSoupfrombs4. - Step 2: Create a sample HTML string.
- Step 3: Parse the HTML with
BeautifulSoup(html, "html.parser"). - Step 4: Use
soup.find()andsoup.find_all()to extract elements. - Step 5: Access attributes with
tag.get("href"). - Step 6: Extract text with
tag.textortag.get_text().
from bs4 import BeautifulSoup def soup_demo(): html = """ <html> <body> <h1>Automation Tutorial</h1> <ul class="links"> <li><a href="/lesson1">Lesson 1</a></li> <li><a href="/lesson2">Lesson 2</a></li> <li><a href="/lesson3">Lesson 3</a></li> </ul> <p class="desc">Learn Python automation.</p> </body> </html> """ soup = BeautifulSoup(html, "html.parser") title = soup.find("h1").text print(f"📄 Title: {title}") links = soup.find_all("a") print(f"🔗 Found {len(links)} links:") for link in links: print(f" - {link.text} → {link.get('href')}") desc = soup.find("p", class_="desc") print(f"📝 Description: {desc.text if desc else 'Not found'}") soup_demo()
requests + BeautifulSoup together: fetch with requests, parse with BeautifulSoup.📧 Email Automation — Send and Receive
Automate email with smtplib for sending and imaplib for reading.
📧 7.1 Sending Email with smtplib
Use smtplib to send emails via SMTP servers.
Step-by-step explanation:
- Step 1: Import
smtplibandemail.message. - Step 2: Create an
EmailMessageobject. - Step 3: Set
From,To,Subject, and body. - Step 4: Connect to an SMTP server using
smtplib.SMTP(). - Step 5: Start TLS with
starttls()for security. - Step 6: Log in and send the email.
- Step 7: Quit the connection.
import smtplib from email.message import EmailMessage def email_demo(): msg = EmailMessage() msg["From"] = "automation@example.com" msg["To"] = "recipient@example.com" msg["Subject"] = "Automation Report" msg.set_content("This is an automated email from Python.") print("📧 Email built:") print(f" From: {msg['From']}") print(f" To: {msg['To']}") print(f" Subject: {msg['Subject']}") print(f" Body: {msg.get_content()}") print("\n💡 In production, connect to an SMTP server with:") print(" server = smtplib.SMTP('smtp.example.com', 587)") print(" server.starttls()") print(" server.login('user', 'password')") print(" server.send_message(msg)") print(" server.quit()") email_demo()
📊 Excel Automation — openpyxl
Read, write, and manipulate Excel files with openpyxl — no Excel needed.
📊 8.1 Reading and Writing Excel Files
Use openpyxl to work with .xlsx files programmatically.
Step-by-step explanation:
- Step 1: Import
openpyxlandPath. - Step 2: Create a new workbook with
Workbook(). - Step 3: Select the active sheet or create a new one.
- Step 4: Write data to cells using
sheet["A1"] = "value". - Step 5: Save the workbook with
wb.save(). - Step 6: Load an existing workbook with
load_workbook(). - Step 7: Iterate over rows and cells to read data.
from openpyxl import Workbook, load_workbook from pathlib import Path def excel_demo(): filename = "automation.xlsx" path = Path(filename) wb = Workbook() ws = wb.active ws.title = "Data" ws["A1"] = "Name" ws["B1"] = "Score" ws.append(["Alice", 95]) ws.append(["Bob", 88]) ws.append(["Charlie", 92]) wb.save(filename) print(f"✅ Created {filename}") wb = load_workbook(filename) ws = wb["Data"] print("\n📊 Data:") for row in ws.iter_rows(values_only=True): print(f" {row}") path.unlink(missing_ok=True) print("\n🧹 Deleted file") excel_demo()
📊 8.2 Working with Existing Excel Files
Read data from existing files, filter, and write results.
Step-by-step explanation:
- Step 1: Load an existing workbook.
- Step 2: Access a specific sheet by name.
- Step 3: Iterate over rows with
iter_rows(). - Step 4: Filter data based on conditions.
- Step 5: Write filtered results to a new sheet.
- Step 6: Save the workbook.
from openpyxl import Workbook, load_workbook from pathlib import Path def excel_filter_demo(): filename = "grades.xlsx" path = Path(filename) wb = Workbook() ws = wb.active ws.title = "Grades" ws.append(["Student", "Score", "Passed"]) ws.append(["Alice", 95, "Yes"]) ws.append(["Bob", 45, "No"]) ws.append(["Charlie", 78, "Yes"]) ws.append(["Diana", 62, "Yes"]) ws.append(["Eve", 33, "No"]) wb.save(filename) wb = load_workbook(filename) ws = wb["Grades"] passed = [] for row in ws.iter_rows(min_row=2, values_only=True): if row[2] == "Yes": passed.append(row) print(f"✅ Found {len(passed)} students who passed:") for student in passed: print(f" {student[0]} - {student[1]}") path.unlink(missing_ok=True) print("\n🧹 Cleaned up") excel_filter_demo()
🖥️ System Monitoring — psutil
Monitor CPU, memory, disk, and network usage with psutil — the cross‑platform system monitoring library.
🖥️ 9.1 System Metrics with psutil
Get real‑time system information: CPU, RAM, disk, and processes.
Step-by-step explanation:
- Step 1: Import
psutil. - Step 2: Use
psutil.cpu_percent()to get CPU usage. - Step 3: Use
psutil.virtual_memory()for RAM details. - Step 4: Use
psutil.disk_usage()for disk space. - Step 5: Use
psutil.net_io_counters()for network stats. - Step 6: Use
psutil.process_iter()to list running processes.
import psutil def system_monitor(): print("🖥️ System Monitoring") cpu = psutil.cpu_percent(interval=0.5) print(f"CPU: {cpu}%") mem = psutil.virtual_memory() print(f"RAM: {mem.percent}% used ({mem.used // (1024**2)} MB / {mem.total // (1024**2)} MB)") disk = psutil.disk_usage("/") print(f"Disk: {disk.percent}% used ({disk.used // (1024**3)} GB / {disk.total // (1024**3)} GB)") net = psutil.net_io_counters() print(f"Network: {net.bytes_sent // (1024**2)} MB sent, {net.bytes_recv // (1024**2)} MB received") processes = list(psutil.process_iter(["pid", "name"]))[:5] print(f"\n📋 Top 5 processes:") for p in processes: print(f" PID {p.info['pid']}: {p.info['name']}") system_monitor()
psutil works on Linux, Windows, and macOS — perfect for cross‑platform automation.🖱️ GUI Automation — pyautogui
Control the mouse and keyboard programmatically. Automate repetitive GUI tasks.
🖱️ 10.1 Mouse and Keyboard Control
pyautogui lets you move the mouse, click, type, and take screenshots.
Step-by-step explanation:
- Step 1: Import
pyautogui. - Step 2: Use
pyautogui.size()to get screen resolution. - Step 3: Use
pyautogui.position()to get mouse position. - Step 4: Use
pyautogui.moveTo()to move the mouse. - Step 5: Use
pyautogui.click()to click. - Step 6: Use
pyautogui.write()to type text. - Step 7: Use
pyautogui.screenshot()to capture the screen.
import pyautogui def gui_automation_demo(): print("🖱️ GUI Automation Demo") print(f"Screen size: {pyautogui.size()}") print(f"Mouse position: {pyautogui.position()}") print("\n💡 In a real environment, you would use:") print(" pyautogui.moveTo(100, 100, duration=0.5)") print(" pyautogui.click()") print(" pyautogui.write('Hello from automation!')") print("\n📸 Screenshot would be captured with:") print(" pyautogui.screenshot('screenshot.png')") print(" # Returns a PIL Image object") print(f"\n✅ pyautogui version: {pyautogui.__version__}") gui_automation_demo()
pyautogui.FAILSAFE = True (enabled by default) — move the mouse to a corner to abort.🚀 Real‑World Automation Patterns
Putting it all together — complete automation scripts for real‑world scenarios.
🚀 11.1 Backup Script
A complete backup script that creates timestamped archives of a directory.
Step-by-step explanation:
- Step 1: Import
shutil,pathlib, anddatetime. - Step 2: Define source and backup directories.
- Step 3: Generate a timestamp for the archive name.
- Step 4: Use
shutil.make_archive()to create the backup. - Step 5: Print success message with the backup location.
from pathlib import Path import shutil from datetime import datetime def backup_demo(): source = Path.cwd() / "data_to_backup" source.mkdir(exist_ok=True) (source / "file1.txt").write_text("Backup this!") (source / "file2.txt").write_text("And this too!") backup_dir = Path.cwd() / "backups" backup_dir.mkdir(exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") archive_name = backup_dir / f"backup_{timestamp}" shutil.make_archive(str(archive_name), "zip", source) print(f"✅ Backup created: {archive_name}.zip") shutil.rmtree(source, ignore_errors=True) Path(f"{archive_name}.zip").unlink(missing_ok=True) backup_dir.rmdir() print("🧹 Cleaned up") backup_demo()
🚀 11.2 File Organizer
Sort files in a directory by extension into subfolders.
Step-by-step explanation:
- Step 1: Define a mapping of extensions to folder names.
- Step 2: Iterate over all files in the directory.
- Step 3: Get the file extension with
Path.suffix. - Step 4: Determine the target folder from the mapping.
- Step 5: Create the folder if it doesn't exist.
- Step 6: Move the file to the target folder with
shutil.move().
from pathlib import Path import shutil def organize_files_demo(): base = Path.cwd() / "to_organize" base.mkdir(exist_ok=True) for ext in [".txt", ".pdf", ".jpg", ".py", ".docx"]: (base / f"file{ext}").write_text(f"Sample {ext} file") mapping = { ".txt": "text_files", ".pdf": "pdf_files", ".jpg": "images", ".png": "images", ".py": "python_scripts", ".docx": "documents", } print("📂 Organizing files...") for item in base.iterdir(): if item.is_file(): ext = item.suffix.lower() target = mapping.get(ext, "other") target_dir = base / target target_dir.mkdir(exist_ok=True) shutil.move(str(item), str(target_dir / item.name)) print(f" Moved {item.name} → {target}/") print("\n📂 Organized structure:") for dir_name in ["text_files", "pdf_files", "images", "python_scripts", "documents", "other"]: dir_path = base / dir_name if dir_path.exists(): files = list(dir_path.glob("*")) print(f" {dir_name}/: {len(files)} files") shutil.rmtree(base, ignore_errors=True) print("\n🧹 Cleaned up") organize_files_demo()
🚀 11.3 Health Check Script
Monitor system health and send an alert if metrics exceed thresholds.
Step-by-step explanation:
- Step 1: Import
psutilfor system metrics. - Step 2: Define thresholds for CPU, RAM, and disk usage.
- Step 3: Collect current metrics with
psutil. - Step 4: Compare each metric against its threshold.
- Step 5: Build an alert message if any threshold is exceeded.
- Step 6: Print the health report.
import psutil def health_check(): thresholds = { "cpu": 80, "ram": 80, "disk": 85 } cpu = psutil.cpu_percent(interval=0.5) mem = psutil.virtual_memory() disk = psutil.disk_usage("/") alerts = [] if cpu > thresholds["cpu"]: alerts.append(f"CPU at {cpu}% (threshold: {thresholds['cpu']}%)") if mem.percent > thresholds["ram"]: alerts.append(f"RAM at {mem.percent}% (threshold: {thresholds['ram']}%)") if disk.percent > thresholds["disk"]: alerts.append(f"Disk at {disk.percent}% (threshold: {thresholds['disk']}%)") print("🖥️ System Health Check") print(f"CPU: {cpu}%") print(f"RAM: {mem.percent}%") print(f"Disk: {disk.percent}%") if alerts: print("\n⚠️ ALERTS:") for alert in alerts: print(f" ❌ {alert}") else: print("\n✅ All systems healthy!") health_check()
🤖 Advanced Automation — Websites, Phones & CCTV
Beyond files and the desktop: drive a real browser, control an Android phone, watch a camera feed for motion, and auto-process a folder. Every line of every program below is explained.
🌐 12.1 Website Automation with Selenium
Selenium drives a real browser exactly like a human would: it opens pages, fills in forms, clicks buttons, and reads back whatever appears on screen. This is the foundation of login bots, form-fillers, and scrapers for pages that need JavaScript to render.
What this program does:
Logs into a sample website, types a username and password, submits the form, waits for the page to load, and reads the welcome message that comes back.
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time def website_automation_demo(): driver = webdriver.Chrome() try: driver.get("https://example.com/login") username_field = driver.find_element(By.NAME, "username") username_field.send_keys("automation_user") password_field = driver.find_element(By.NAME, "password") password_field.send_keys("secure_password") password_field.send_keys(Keys.RETURN) time.sleep(2) welcome = driver.find_element(By.CSS_SELECTOR, "h1.welcome").text print(f"✅ Logged in. Welcome message: {welcome}") finally: driver.quit() print("🧹 Browser closed") website_automation_demo()
Line-by-line explanation:
- Lines 1–3: Import
webdriver(controls the browser),By(a set of strategies for locating elements, such as by name or CSS selector), andKeys(represents keyboard keys like Enter). - Line 4: Import
timeso the script can pause while pages load. - Line 7:
webdriver.Chrome()launches a real, visible Chrome window that Selenium controls — this is the browser "session". - Line 9:
try:begins a block that guarantees cleanup even if something goes wrong further down. - Line 10:
driver.get(url)navigates the open browser to the login page, just like typing the URL yourself. - Lines 12–13:
find_element(By.NAME, "username")locates the input box whose HTMLnameattribute is"username";send_keys()types text into it, character by character. - Lines 15–16: Same idea for the password field — locate it by name, then type the password.
- Line 18:
send_keys(Keys.RETURN)simulates pressing the Enter key inside the password field, which submits the form. - Line 19:
time.sleep(2)pauses for two seconds so the next page has time to load before we try to read it. - Line 21: Locates an element by CSS selector (
h1.welcomemeans "an<h1>tag with classwelcome") and reads its visible text with.text. - Line 22: Prints the welcome message that was scraped from the page after login.
- Line 24:
finally:runs no matter what — even if the login failed or an element was not found. - Line 25:
driver.quit()closes the browser window and ends the session, freeing memory.
pip install selenium), so it cannot run inside the in-browser Pyodide runner on this page — copy it into your own terminal to try it. It will not execute if you press Run above.📱 12.2 Phone Automation with ADB (Android Debug Bridge)
ADB is a command-line tool that ships with the Android SDK. Once a phone has USB debugging enabled and is plugged in (or connected over Wi-Fi), Python can drive it through subprocess — taking screenshots, tapping the screen, swiping, and launching apps.
What this program does:
Lists connected devices, pulls a screenshot from the phone to the computer, simulates a tap, simulates a swipe, and launches an app by its package name.
import subprocess def phone_automation_demo(): devices = subprocess.run(["adb", "devices"], capture_output=True, text=True) print("📱 Connected devices:") print(devices.stdout) subprocess.run(["adb", "shell", "screencap", "-p", "/sdcard/screen.png"]) subprocess.run(["adb", "pull", "/sdcard/screen.png", "screen.png"]) print("✅ Screenshot saved as screen.png") subprocess.run(["adb", "shell", "input", "tap", "500", "1000"]) print("✅ Tapped at (500, 1000)") subprocess.run(["adb", "shell", "input", "swipe", "300", "1500", "300", "500", "300"]) print("✅ Swiped up") subprocess.run(["adb", "shell", "monkey", "-p", "com.android.settings", "-c", "android.intent.category.LAUNCHER", "1"]) print("✅ Launched Settings app") phone_automation_demo()
Line-by-line explanation:
- Line 1: Import
subprocess— the same module used earlier for running external commands. ADB itself is not a Python library; it's a program we call from Python. - Line 4: Runs
adb devices, which asks ADB to list every phone or emulator currently connected and authorized. - Line 8: Runs
adb shell screencap -p /sdcard/screen.png— this executes thescreencapcommand on the phone itself, saving a PNG screenshot to the phone's storage. - Line 9: Runs
adb pullto copy that screenshot file from the phone's storage down to the computer running the script. - Line 12: Sends an
input tap x ycommand, simulating a finger tap at pixel coordinates (500, 1000) on the phone's screen. - Line 15: Sends an
input swipe x1 y1 x2 y2 durationcommand, dragging from (300,1500) to (300,500) over 300 milliseconds — a scroll-up gesture. - Lines 18–19: Uses the
monkeytool with a package name (com.android.settings) and theLAUNCHERintent category to open that app, exactly like tapping its icon.
🎥 12.3 CCTV / Webcam Automation — Motion Detection with OpenCV
OpenCV (cv2) is the standard computer-vision library. Pointed at a webcam or an IP camera / CCTV stream, it can compare consecutive frames and automatically save a snapshot the moment something moves — the same core idea behind commercial motion-triggered security cameras.
What this program does:
Opens a camera feed, keeps a reference "background" frame, compares every new frame against it, and saves a timestamped snapshot whenever enough pixels have changed to count as motion.
import cv2 import time from datetime import datetime def cctv_motion_detection(): cap = cv2.VideoCapture(0) ret, first_frame = cap.read() first_gray = cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY) first_gray = cv2.GaussianBlur(first_gray, (21, 21), 0) print("🎥 Monitoring for motion... press Ctrl+C to stop") try: while True: ret, frame = cap.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (21, 21), 0) delta = cv2.absdiff(first_gray, gray) thresh = cv2.threshold(delta, 25, 255, cv2.THRESH_BINARY)[1] motion_pixels = cv2.countNonZero(thresh) if motion_pixels > 5000: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"motion_{timestamp}.jpg" cv2.imwrite(filename, frame) print(f"🚨 Motion detected! Saved snapshot: {filename}") first_gray = gray time.sleep(0.5) except KeyboardInterrupt: print("\n🛑 Stopped monitoring") finally: cap.release() cctv_motion_detection()
Line-by-line explanation:
- Lines 1–3: Import
cv2(OpenCV),timefor pausing, anddatetimeto build timestamped filenames. - Line 6:
cv2.VideoCapture(0)opens camera index 0 — the first connected camera. An IP/CCTV camera would instead use its RTSP URL string here, e.g.cv2.VideoCapture("rtsp://..."). - Line 8: Reads one frame from the camera to use as the starting reference ("what the room normally looks like").
- Line 9: Converts that frame from color (BGR) to grayscale — motion detection only needs brightness, not color, which is faster to process.
- Line 10: Blurs the grayscale frame slightly with a Gaussian blur so tiny sensor noise doesn't get mistaken for motion.
- Line 14: Begins an infinite loop that keeps reading frames until the user stops the program or the camera disconnects.
- Lines 15–17: Reads the next frame; if
retisFalsethe camera failed to return a frame, so the loop breaks. - Lines 19–20: Applies the exact same grayscale-and-blur treatment to the new frame so it can be fairly compared to the reference.
- Line 22:
cv2.absdiff()computes the pixel-by-pixel absolute difference between the reference frame and the current frame — bright areas mean "this pixel changed a lot". - Line 23:
cv2.threshold()converts that difference into pure black-and-white: any pixel that changed by more than 25 becomes white (255), everything else becomes black. - Line 25:
cv2.countNonZero()counts how many white (changed) pixels exist — a simple measure of "how much motion is happening". - Lines 27–31: If the changed-pixel count crosses the 5000 threshold, build a timestamped filename and save the current color frame as a JPEG snapshot with
cv2.imwrite(). - Line 33: Updates the reference frame to the current one, so future comparisons are against the most recent "normal" state rather than a stale one.
- Line 34: Sleeps briefly between checks to limit CPU usage.
- Lines 36–39: Catches
Ctrl+Cgracefully, and thefinallyblock always callscap.release()to free the camera device.
pip install opencv-python and an actual camera or CCTV/RTSP stream — the browser sandbox has no camera access, so this program has no Run button here. Test it locally.📥 12.4 Task Automation — Watching a Folder with watchdog
A lot of real automation is reactive: "whenever a new file shows up, do something with it." The watchdog library listens to filesystem events in the background and fires your function the instant a file is created, changed, or deleted — no polling loop required.
What this program does:
Watches a folder for new files. The moment a file appears, it automatically moves it into a subfolder named after its extension — a live, always-on version of the file organizer from section 11.2.
import time from pathlib import Path from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class NewFileHandler(FileSystemEventHandler): def on_created(self, event): if event.is_directory: return path = Path(event.src_path) print(f"📥 New file detected: {path.name}") target_dir = path.parent / path.suffix.lstrip(".") target_dir.mkdir(exist_ok=True) path.rename(target_dir / path.name) print(f"✅ Moved to {target_dir}/") def folder_watcher_demo(): watch_path = Path.cwd() / "watched_folder" watch_path.mkdir(exist_ok=True) event_handler = NewFileHandler() observer = Observer() observer.schedule(event_handler, str(watch_path), recursive=False) observer.start() print(f"👀 Watching {watch_path} for new files (runs 10s)...") time.sleep(10) observer.stop() observer.join() print("🧹 Stopped watching") folder_watcher_demo()
Line-by-line explanation:
- Lines 1–4: Import
timefor the demo timer,Pathfor filesystem paths, and two pieces fromwatchdog:Observer(runs the background watching thread) andFileSystemEventHandler(the base class you customize to react to events). - Line 6: Defines
NewFileHandler, a subclass ofFileSystemEventHandler, so we can override just the events we care about. - Line 7:
on_created()is a methodwatchdogcalls automatically every time a new file or folder appears in the watched directory. - Lines 8–9: If the new item is a directory (not a file), exit early — we only want to react to files.
- Line 10: Wraps the raw path string from the event into a
Pathobject for easier manipulation. - Line 11: Prints which file was just detected.
- Line 13: Builds a target folder name from the file's extension (
.suffix) with the leading dot stripped, e.g..txt→txt. - Line 14: Creates that target folder if it doesn't already exist.
- Line 15:
path.rename()moves the file into the target folder — renaming to a new location is how you move files withpathlib. - Line 18: Defines the function that sets everything up and runs the watcher.
- Lines 19–20: Creates the folder that will be watched, if it doesn't exist yet.
- Line 22: Creates an instance of our custom handler.
- Line 23: Creates an
Observer— the object that actually watches the filesystem in a background thread. - Line 24:
observer.schedule()tells the observer which handler to call, which folder to watch, and whether to watch subfolders too (recursive=Falsemeans top level only). - Line 25: Starts the background watching thread.
- Line 28: Keeps the main program alive for 10 seconds so the background thread has time to catch events — in production this would run indefinitely.
- Lines 30–31: Stops the observer thread and waits for it to fully shut down.
pip install watchdog, which is not preloaded in the Pyodide sandbox, and needs a real filesystem to watch — copy this into a local Python environment to run it.❓ Frequently Asked Questions — Python Automation
Common questions about automating with Python, answered.
What's the difference between shutil and os for file operations?
+os provides low‑level system functions. shutil builds on os to provide high‑level operations like copying entire directory trees, moving files across filesystems, and creating archives. Use shutil for common tasks, os for fine‑grained control.
How do I run a Python script on a schedule?
+Use the schedule library for in‑process scheduling, or use your operating system's scheduler: cron on Linux/macOS, Task Scheduler on Windows. For production, consider APScheduler or a dedicated orchestration tool like Celery.
Is web scraping legal?
+Check the website's robots.txt and terms of service. Scraping public data for personal use is usually fine, but avoid overwhelming servers (use delays) and respect copyright. Always identify your bot with a custom User-Agent.
What's the best library for Excel automation?
+openpyxl is the most popular for reading/writing .xlsx files. For older .xls files, use xlrd (read) and xlwt (write). For data‑heavy workflows, consider pandas which uses openpyxl or xlrd under the hood.
How do I keep my automation scripts secure?
+Never hard‑code credentials. Use environment variables, .env files, or a secrets manager. Validate all inputs, especially when using subprocess with shell=True. Run automation scripts with the least privilege necessary.
What should I automate first?
+Start with tasks you do repeatedly: file renaming, data entry, report generation, or system checks. The best automation is the one that saves you the most time. Start small and iterate.
Can I use Python to automate Excel on macOS/Linux?
+Yes! openpyxl works on all platforms because it reads/writes the file format directly — no Excel installation needed. For Windows‑specific COM automation (controlling the Excel application itself), you'd need win32com which only works on Windows.
What's the difference between Selenium and requests + BeautifulSoup?
+requests just downloads raw HTML — it never runs JavaScript. Selenium opens a real browser, so it can click buttons, fill forms, wait for JavaScript-rendered content, and log into accounts. Use requests for simple static pages; use Selenium when the page needs interaction or JavaScript to show its data.
Do I need a rooted phone to automate it with ADB?
+No. Tapping, swiping, screenshotting, and launching apps through adb shell input and monkey all work on a stock, non‑rooted phone — you only need USB debugging enabled in Developer Options. Rooting is only needed for deeper system-level automation.
Can Python automation work with real CCTV/IP cameras, not just a webcam?
+Yes. Most IP cameras and CCTV/NVR systems expose an RTSP stream URL (often something like rtsp://user:pass@camera-ip:554/stream). Pass that URL string straight into cv2.VideoCapture() instead of a camera index, and the rest of the motion-detection code works the same way.
Why use watchdog instead of just checking a folder in a loop?
+A polling loop (checking every few seconds) wastes CPU and adds delay. watchdog uses your operating system's native file-event notifications, so your handler fires instantly when a file appears, with near-zero CPU cost while idle.
💛 You've Mastered Python Automation
From file operations and subprocess to scheduling, web scraping, email, Excel, system monitoring, GUI automation, website automation, phone automation, and CCTV motion detection — you now have a comprehensive toolkit for automating almost anything with Python. You've learned to orchestrate, monitor, and integrate systems in ways that save hours every day.
This curriculum, crafted by @PoeticGlenn, is designed to be your go‑to reference whenever you need to automate a task in Python. Bookmark it, share it with your team, and build automation that makes your work — and your team's work — faster and more reliable.
"Automation is not about replacing humans — it's about freeing them to do what only humans can do." — Glenn Junsay Pansensoy (@PoeticGlenn)