Python for Automation: The Stack, Five Real Scripts, and When Not To
August 18, 2026

Python is the default language for automation for one unglamorous reason: the library for whatever you're trying to do already exists, and it's usually one import away.
You do not need to learn Python to use it for automation. You need about six libraries and five patterns, and those cover the overwhelming majority of real tasks:
- Files and folders — rename, sort, archive, clean up
- Spreadsheets — read, transform, write, without opening Excel
- APIs and web data — pull, parse, store
- Scheduled jobs — run the above on a timer
- Browser tasks — when there's no API at all
Here's the stack, the five patterns with working code, and — the part most guides skip — the three cases where Python is the wrong answer.
The stack, and nothing more
Resist installing a library per task. This list covers most of what people actually automate:
| Library | Use it for | Ships with Python |
|---|---|---|
pathlib | Every file and folder operation | Yes |
shutil | Copy, move, archive | Yes |
csv / json | Simple structured data | Yes |
requests | HTTP and REST APIs | No |
pandas | Anything tabular beyond trivial | No |
openpyxl | Real Excel files with formatting | No |
playwright | Browser automation | No |
Two notes that save time later. First, pathlib replaced string-based path handling years ago and is genuinely better — if a tutorial uses os.path.join, it's dated. Second, pandas and openpyxl are partners, not alternatives: pandas for reading and transforming data, openpyxl when the output needs formatting, formulas, or multiple sheets.
Pattern one: tidy a folder
The classic first script, and the one people actually keep.
from pathlib import Path
import shutil
downloads = Path.home() / "Downloads"
buckets = {
".pdf": "Documents",
".png": "Images",
".jpg": "Images",
".csv": "Data",
}
for item in downloads.iterdir():
if not item.is_file():
continue
target_name = buckets.get(item.suffix.lower())
if target_name is None:
continue
target = downloads / target_name
target.mkdir(exist_ok=True)
shutil.move(str(item), str(target / item.name))
Twenty lines, no dependencies. The valuable habit shown here is the continue guards — a real folder contains directories, hidden files, and extensions you didn't plan for, and a script that assumes otherwise dies on its third run.
Pattern two: merge and clean spreadsheets
The single highest-value Python automation for most office work.
from pathlib import Path
import pandas as pd
folder = Path("reports")
frames = []
for csv_file in sorted(folder.glob("*.csv")):
frame = pd.read_csv(csv_file)
frame["source_file"] = csv_file.name
frames.append(frame)
combined = pd.concat(frames, ignore_index=True)
combined = combined.drop_duplicates()
combined["date"] = pd.to_datetime(combined["date"], errors="coerce")
combined = combined.dropna(subset=["date"])
combined.to_excel("combined.xlsx", index=False)
That's an afternoon of copy-paste, every month, reduced to a command. The errors="coerce" and the dropna are the important lines — real exports contain rows with broken dates, and without those two the whole script fails on one bad cell.
Pattern three: pull from an API on a schedule
from datetime import date
from pathlib import Path
import requests
response = requests.get(
"https://api.example.com/v1/orders",
headers={"Authorization": "Bearer " + TOKEN},
timeout=30,
)
response.raise_for_status()
output = Path("snapshots") / f"orders-{date.today().isoformat()}.json"
output.parent.mkdir(exist_ok=True)
output.write_text(response.text) # raw body first — survives a parse failure
orders = response.json() # parse only once the snapshot exists
Three things here are not optional in anything you'll run unattended: timeout (without it a hung request hangs forever), raise_for_status (otherwise a 500 response is silently treated as success), and writing the raw body to disk before calling response.json(). Order matters here: if you parse first, malformed or non-JSON content raises and no snapshot is saved — which is exactly the run you needed the evidence from.
For scheduling, don't reach for a library. Use what your OS already has — cron on macOS and Linux, Task Scheduler on Windows. An in-process scheduler dies when the process does, which is exactly when you need it. We cover that layer in task automation.
Pattern four: drive a website with no API
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com/login")
page.fill("#username", USERNAME)
page.fill("#password", PASSWORD)
page.click("button[type=submit]")
page.wait_for_selector(".dashboard")
page.screenshot(path="dashboard.png")
browser.close()
Playwright waits for elements automatically, which removes the sleep-and-pray pattern that made older browser automation so brittle. Use it when a service has no API. When it does have one, use the API — it's faster and it doesn't break when someone redesigns a page.
Keep credentials out of the file. Environment variables via os.environ, or a .env file that is in your .gitignore and stays there.
Pattern five: make it survive unattended
The difference between a script and an automation is what happens when it fails at 3am.
import logging
from pathlib import Path
logging.basicConfig(
filename=Path.home() / "automation.log",
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
def main():
logging.info("run started")
# ... the actual work ...
logging.info("run finished")
if __name__ == "__main__":
try:
main()
except Exception:
logging.exception("run failed")
raise
logging.exception records the full traceback. raise at the end matters: it lets the scheduler know the run failed, instead of reporting success on a job that did nothing. A silent automation that stopped working three weeks ago is worse than no automation, because you've been trusting it.
When Python is the wrong tool
Three honest cases:
The task is one machine, one schedule, no logic. Rename files nightly, empty a folder, back something up. A shell script or a scheduled task is fewer moving parts and won't break when you upgrade Python.
Both apps are on a mainstream automation platform. If Zapier or n8n already connects the two services, using them takes fifteen minutes and doesn't leave you owning authentication, retries, and a server. Writing Python for something a connector already handles is a hobby, not a decision.
Somebody else already built it. The most common waste in this space is rewriting a well-maintained open source tool because finding and running it looked harder than starting fresh. GitHub has a solved version of most common automation tasks.
That last one is the honest bottleneck for a lot of people: the script exists, and getting it running — the right Python version, the dependencies, the keys, the paths — is where it dies. That's the gap Taku works on, mirroring a working AI setup into a desktop workspace and running it there instead of asking you to reproduce someone's environment. The free app library is a quick way to see whether your task already has a published shape. Taku is in Beta, and the Mac app is available now.
FAQ
Is Python good for automation?
Yes, and mostly because of its libraries rather than the language. For files, spreadsheets, APIs, and browsers, the mature library already exists and is well documented.
Do I need to know programming to write Python automation scripts?
Not much. The patterns above are readable with a few hours of basics. Understanding error handling and file paths matters more than knowing the language deeply.
What Python libraries should I learn first?
pathlib and shutil for files, requests for APIs, pandas for tabular data, openpyxl for Excel output, and playwright for browsers. That set covers most tasks.
How do I run a Python script automatically?
Use your operating system's scheduler — cron on macOS and Linux, Task Scheduler on Windows. Avoid in-process schedulers for anything that must survive a restart.
When should I not use Python?
When a shell script covers it, when a connector platform already links both apps, or when a maintained open source tool already does the job.
Key points
- Six libraries cover most real automation; resist installing one per task.
- Guard clauses and coercion matter more than clever code — real data is messy.
- Timeouts, status checks, and logging are what separate a script from an automation.
- Use the OS scheduler, not an in-process one.
- Don't write Python for something a connector or an existing tool already does.