← The Commonplace

Automation

Glenn Junsay Pansensoy · Programming · October 30, 2026

This is an automation program utilizing Python.

Written by Glenn Junsay Pansensoy  ·  Programming  ·  52 min read

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.

Back to Home 28 lessons Pyodide ready

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.

@PoeticGlenn says: "The best code is the code you never have to write twice. Automation is the art of making yourself obsolete — so you can work on harder problems."

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.

📂 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 Path from pathlib.
  • Step 2: Create a Path object pointing to the current directory using Path.cwd().
  • Step 3: Use path.exists(), path.is_dir(), and path.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.
python
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(), and os.path.isfile() to check properties.
  • Step 6: Use os.environ to access environment variables — useful for configuration.
python
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()
✨ Prefer 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 with readlines().
  • Step 5: Append to a file with "a" mode.
  • Step 6: Delete the file using Path.unlink().
python
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().
python
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 shutil and pathlib.
  • 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.
python
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.
python
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.stdout and result.stderr as bytes or strings with text=True.
  • Step 5: Check the return code with result.returncode.
  • Step 6: Use check=True to raise an exception on non‑zero exit codes.
python
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()
💡 For shell‑like behavior (pipes, wildcards), use 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 env parameter.
  • Step 4: Set a timeout to prevent hanging.
  • Step 5: Handle the timeout exception.
python
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 schedule and time.
  • 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.
python
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()
✨ The 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 BackgroundScheduler from apscheduler.schedulers.background.
  • Step 2: Import CronTrigger for cron‑style scheduling.
  • Step 3: Define a function to run on schedule.
  • Step 4: Create a BackgroundScheduler and add a job.
  • Step 5: Start the scheduler.
  • Step 6: Keep the program running for a few seconds.
  • Step 7: Shut down the scheduler.
python
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()
⚠️ The 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 requests module.
  • 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) or response.json() (API).
  • Step 5: Handle errors with response.raise_for_status().
python
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 BeautifulSoup from bs4.
  • Step 2: Create a sample HTML string.
  • Step 3: Parse the HTML with BeautifulSoup(html, "html.parser").
  • Step 4: Use soup.find() and soup.find_all() to extract elements.
  • Step 5: Access attributes with tag.get("href").
  • Step 6: Extract text with tag.text or tag.get_text().
python
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()
✨ Use 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 smtplib and email.message.
  • Step 2: Create an EmailMessage object.
  • 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.
python
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()
⚠️ In a production environment, use environment variables for credentials — never hard‑code them.

📊 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 openpyxl and Path.
  • 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.
python
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.
python
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.
python
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.
python
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()
⚠️ GUI automation runs on your actual screen. Always test with 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, and datetime.
  • 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.
python
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().
python
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 psutil for 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.
python
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.

python
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), and Keys (represents keyboard keys like Enter).
  • Line 4: Import time so 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 HTML name attribute 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.welcome means "an <h1> tag with class welcome") 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.
⚠️ Selenium needs a real Chrome/Firefox installation plus a matching driver (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.

python
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 the screencap command on the phone itself, saving a PNG screenshot to the phone's storage.
  • Line 9: Runs adb pull to copy that screenshot file from the phone's storage down to the computer running the script.
  • Line 12: Sends an input tap x y command, simulating a finger tap at pixel coordinates (500, 1000) on the phone's screen.
  • Line 15: Sends an input swipe x1 y1 x2 y2 duration command, dragging from (300,1500) to (300,500) over 300 milliseconds — a scroll-up gesture.
  • Lines 18–19: Uses the monkey tool with a package name (com.android.settings) and the LAUNCHER intent category to open that app, exactly like tapping its icon.
⚠️ This requires the Android SDK Platform Tools installed on your computer, a phone with USB debugging enabled, and a real device connection — it cannot run inside the browser-based Pyodide runner, so no Run button is shown for this program.

🎥 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.

python
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), time for pausing, and datetime to 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 ret is False the 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+C gracefully, and the finally block always calls cap.release() to free the camera device.
⚠️ This needs 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.

python
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 time for the demo timer, Path for filesystem paths, and two pieces from watchdog: Observer (runs the background watching thread) and FileSystemEventHandler (the base class you customize to react to events).
  • Line 6: Defines NewFileHandler, a subclass of FileSystemEventHandler, so we can override just the events we care about.
  • Line 7: on_created() is a method watchdog calls 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 Path object 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 with pathlib.
  • 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=False means 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.
⚠️ Requires 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.

28 lessons 34+ code examples 600+ learners

"Automation is not about replacing humans — it's about freeing them to do what only humans can do." — Glenn Junsay Pansensoy (@PoeticGlenn)


Tags: pythonprogrammingaiProgrammingprintautomationcolorbackgroundfontsizeimport
Comments
Share this essay
FacebookXLinkedInWhatsAppTelegramRedditEmail
About | Privacy Policy | Terms of Service | Cookie Policy
© 2026 Glenn Junsay Pansensoy. All rights reserved.
Published on poetic-bytes.blogspot.com  ·  #python #programming #ai #Programming #print