"The best time to plant a tree was twenty years ago. The second best time is now." — Often attributed as a proverb. For you: the best time to stop manually renaming fifty homework files was before they piled up. The second best time is this afternoon, with a script that runs in seconds.
Relatable scenario: It is Sunday night. Your
Downloadsfolder is a graveyard offinal_v2_REALLY_final.pdffiles, your club shared drive has three copies of the same permission slip, and you swore you would "sort that later" for six weeks. Later is now a myth. Automation means teaching your computer to do the boring, repeatable parts so your brain stays free for the parts that actually need you—judgment, creativity, and remembering to text your friend back.
Time estimate: 90–120 minutes to read and run the examples, plus 3–5 hours over a week to adapt scripts to your folders and rules.
What you need: Python 3.10+ (or your course environment), pip permission, and a trusted adult if installing packages on a family computer. For scraping examples: only sites whose terms of service allow it—never hammer servers or scrape personal data without consent.
Learning Objectives
By the end of this module, you will be able to:
- Explain what automation means for everyday digital life: repeatable tasks, triggers, and scripts as "instructions that run without you retyping them."
- Write Python that organizes files by extension, date, or keyword using
pathliband safe moves (not destructive deletes). - Build a simple, ethical web-scraping script that fetches public pages, parses HTML, and respects rate limits and robots expectations.
- Schedule scripts to run on a timer using Python's
schedulelibrary (and know how that differs from OS-level Task Scheduler / cron). - Combine small scripts into a workflow mindset: input folder → process → log → optional notification.
- Reflect on limits: what must stay human (privacy, consent, academic integrity) versus what machines handle well.
1. Why Python for "Boring" Tasks?
Your phone shortcuts are great for one-tap vibes. Python shines when you need logic: "If the file name contains Lab and the extension is .pdf, move it to School/Science—but never overwrite without asking."
Relatable analogy — playlist rules: Music apps sort songs by rules you set. Scripts do the same for files, data, and fetched text—except you write the rules in code.
| You (human) | Python script |
|---|---|
| Decides what "organized" means | Applies that definition the same way every time |
| Handles exceptions ("this file is weird") | Can log weird cases for you to review |
| Stops when ethics say stop | Only does what you coded—so your ethics are in the loop |
Did You Know? Many "no-code" automation tools still run code under the hood. Learning Python means you are not stuck when a menu option does not exist.
Activity (5 min): List five folders on your machine that make you sigh. Pick one to be the target of your first organizer—not the whole drive on day one.
2. File Organization Scripts: From Chaos to Rules
Goal: Walk a folder, look at each file, and move or rename based on rules you define. We use pathlib (modern, readable paths) and copy-then-verify patterns before you trust moves on important school work.
Example: sort Downloads by file extension
[object Object],
,[object Object],
,[object Object],
,[object Object], __future__ ,[object Object], annotations
,[object Object], shutil
,[object Object], pathlib ,[object Object], Path
,[object Object], collections ,[object Object], defaultdict
,[object Object],
SOURCE_DIR = Path.home() / ,[object Object], ,[object Object],
DRY_RUN = ,[object Object], ,[object Object],
,[object Object],
EXTENSION_BUCKETS: ,[object Object],[,[object Object],, ,[object Object],] = {
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],,
}
,[object Object], ,[object Object],(,[object Object],) -> ,[object Object], | ,[object Object],:
,[object Object],
,[object Object], path.name.startswith(,[object Object],):
,[object Object], ,[object Object], ,[object Object],
ext = path.suffix.lower().lstrip(,[object Object],)
,[object Object], ,[object Object], ext:
,[object Object], ,[object Object],
,[object Object], EXTENSION_BUCKETS.get(ext, ,[object Object],)
,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
,[object Object], ,[object Object], source.is_dir():
,[object Object],(,[object Object],)
,[object Object],
,[object Object],
planned: defaultdict[,[object Object],, ,[object Object],[Path]] = defaultdict(,[object Object],)
,[object Object], item ,[object Object], source.iterdir():
,[object Object], ,[object Object], item.is_file():
,[object Object],
dest_name = bucket_for(item)
,[object Object], dest_name ,[object Object], ,[object Object],:
,[object Object],
planned[dest_name].append(item)
,[object Object], folder, files ,[object Object], ,[object Object],(planned.items()):
dest_dir = source / folder
,[object Object],(,[object Object],)
,[object Object], f ,[object Object], files:
target = dest_dir / f.name
,[object Object],
,[object Object], target.exists():
,[object Object],(,[object Object],)
,[object Object],
,[object Object], dry_run:
,[object Object],(,[object Object],)
,[object Object],:
dest_dir.mkdir(parents=,[object Object],, exist_ok=,[object Object],)
shutil.move(,[object Object],(f), ,[object Object],(target))
,[object Object],(,[object Object],)
,[object Object], __name__ == ,[object Object],:
,[object Object],(,[object Object],)
,[object Object],(,[object Object],)
organize(SOURCE_DIR, dry_run=DRY_RUN)What to notice:
DRY_RUNlets you rehearse without moving anything—professional habit.- Hidden files and existing names are handled with explicit skips, not silent overwrites.
EXTENSION_BUCKETSis data you can extend—treat it like a settings block.
Pro Tip: Run on a copy of a folder first. Grandparents were right: "measure twice, cut once" applies to shutil.move.
Try-it: Add a rule: if the filename contains transcript (case-insensitive), force bucket important_docs regardless of extension.
Extension — read rules from JSON (no code redeploy for every new club season):
[object Object],
,[object Object], json
,[object Object], pathlib ,[object Object], Path
CONFIG_PATH = Path(__file__).with_name(,[object Object],)
,[object Object],
,[object Object],
,[object Object], ,[object Object],() -> ,[object Object],[,[object Object],, ,[object Object],]:
,[object Object], ,[object Object], CONFIG_PATH.exists():
,[object Object], {}
data = json.loads(CONFIG_PATH.read_text(encoding=,[object Object],))
,[object Object], {k.lower(): v ,[object Object], k, v ,[object Object], data.get(,[object Object],, {}).items()}
,[object Object],
,[object Object],Pro Tip: JSON configs are how you let future-you (or a non-coder teammate) tweak folders without touching Python logic—just validate keys so typos do not silently break moves.
3. Simple Web Scraping: Power, Politeness, and Boundaries
Web scraping means: your program downloads a page, reads the HTML, and extracts information (headlines, tables, links). It is not magic permission to ignore laws, Terms of Service, or people's privacy.
Green lights (usually): Public pages that allow automated access, your own content, APIs when available (prefer API over scraping when both exist).
Red lights: Log-in walls you bypass, personal data, copyrighted text at scale, anything your school or local law forbids.
Example: fetch and print article titles from a static practice page
Use a small delay between requests if you loop over many URLs—do not spam servers.
[object Object],
,[object Object],
,[object Object],
,[object Object], __future__ ,[object Object], annotations
,[object Object], time
,[object Object], sys
,[object Object], requests
,[object Object], bs4 ,[object Object], BeautifulSoup
,[object Object],
HEADERS = {
,[object Object],: ,[object Object],
}
,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
,[object Object],
resp = requests.get(url, headers=HEADERS, timeout=timeout)
resp.raise_for_status()
,[object Object], resp.text
,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],[,[object Object],]:
,[object Object],
soup = BeautifulSoup(html, ,[object Object],)
titles: ,[object Object],[,[object Object],] = []
,[object Object],
,[object Object], tag ,[object Object], soup.find_all(,[object Object],):
text = tag.get_text(strip=,[object Object],)
,[object Object], text:
titles.append(text)
,[object Object], titles
,[object Object], ,[object Object],() -> ,[object Object],:
,[object Object], ,[object Object],(sys.argv) < ,[object Object],:
,[object Object],(,[object Object],)
,[object Object],(,[object Object],)
sys.exit(,[object Object],)
url = sys.argv[,[object Object],]
,[object Object],(,[object Object],)
html = fetch_html(url)
titles = parse_example_titles(html)
,[object Object], i, t ,[object Object], ,[object Object],(titles[:,[object Object],], start=,[object Object],): ,[object Object],
,[object Object],(,[object Object],)
,[object Object],
time.sleep(,[object Object],)
,[object Object], __name__ == ,[object Object],:
main()Optional — friendly command-line flags with argparse:
[object Object],
,[object Object], argparse
,[object Object], ,[object Object],() -> argparse.Namespace:
p = argparse.ArgumentParser(description=,[object Object],)
p.add_argument(,[object Object],, nargs=,[object Object],, ,[object Object],=,[object Object],)
p.add_argument(,[object Object],, action=,[object Object],, ,[object Object],=,[object Object],)
,[object Object], p.parse_args()
,[object Object],
,[object Object],
,[object Object],
,[object Object],Did You Know? Scripts that accept flags age better than scripts you must edit every time—you forget less, break less.
Install (once): pip install requests beautifulsoup4
Did You Know? robots.txt on a site hints what crawlers should avoid. It is not always legally binding, but it is a respect signal—and some school projects require you to document that you checked it.
Activity: With a teacher-approved URL, open DevTools → Elements. Find one headline node. Write one sentence describing its tag and class. That sentence becomes your selector plan.
4. Scheduling: "Run This Without Me Opening the Laptop"
Two layers:
- In-Python scheduling: A script stays running and fires jobs every N minutes (good for learning, demos on a machine that is on).
- OS scheduling: Windows Task Scheduler, macOS
launchd, Linuxcron—fires a command even when you are not in VS Code.
In-Python: the schedule library
[object Object],
,[object Object],
,[object Object],
,[object Object], __future__ ,[object Object], annotations
,[object Object], time
,[object Object], datetime ,[object Object], datetime
,[object Object], schedule
,[object Object],
,[object Object],
,[object Object],
,[object Object], ,[object Object],() -> ,[object Object],:
now = datetime.now().strftime(,[object Object],)
,[object Object],(,[object Object],)
,[object Object],
,[object Object], ,[object Object],() -> ,[object Object],:
,[object Object],
schedule.every().day.at(,[object Object],).do(job)
,[object Object],
,[object Object],
,[object Object],(,[object Object],)
,[object Object], ,[object Object],:
schedule.run_pending()
time.sleep(,[object Object],)
,[object Object], __name__ == ,[object Object],:
main()Install: pip install schedule
Fun Fact: A loop with sleep is simple but means the script must keep running. For "run at 7 a.m. even if I was not coding yesterday," learn one OS scheduler—your future self will thank you for internships and backups.
Try-it (research): Find Microsoft's or Apple's official doc page for Task Scheduler or Shortcuts automation. Write three bullets: what program you would run, how often, and what could go wrong (wrong path, asleep laptop, permissions).
5. Workflows That Combine Pieces: Logs, Errors, and "Good Enough"
Pattern: Input → transform → output → log file (append-only).
[object Object],
,[object Object],
,[object Object], __future__ ,[object Object], annotations
,[object Object], datetime ,[object Object], datetime
,[object Object], pathlib ,[object Object], Path
LOG_FILE = Path(,[object Object],)
,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
stamp = datetime.now().isoformat(timespec=,[object Object],)
,[object Object],:
,[object Object],
,[object Object],
msg = ,[object Object],
,[object Object], Exception ,[object Object], e: ,[object Object],
msg = ,[object Object],
LOG_FILE.,[object Object],(,[object Object],, encoding=,[object Object],).write(msg)
,[object Object],(msg.strip())
,[object Object], __name__ == ,[object Object],:
run_with_logging(,[object Object],)Pro Tip: Logging beats mystery when something breaks three Tuesdays from now. Pros read logs before they re-run chaos.
Discussion: What should never be automated for school? (Examples: submitting assignments, taking quizzes, impersonating you in email.) List three and why.
Practice Challenges
-
Extension detective: Write a script that only prints a frequency table of extensions in a folder (no moves). Use it on a copy of
Downloads. -
Rename pattern: Files named
IMG_0001.jpg…IMG_0099.jpg—write a script that zero-pads or prefixes with2026-04-01_on copies first. -
Scraper v2: Start from the demo; target a teacher-approved page; extract links (
<a href>) and write the first 15 to a.txtfile. -
Scheduler combo: Schedule two different functions at different times in one
scheduleloop (e.g., "reminder" + "dry-run organize"). -
Error handling: Feed your organizer a non-folder path on purpose. Improve the user-facing message so a friend could understand the fix.
-
CSV append log: After each organize run, append one row to
runs.csv(timestamp,files_moved,dry_run). Plot files_moved over time in Module 05 style. -
Idempotency test: Run the same organizer twice on a copy folder. Second run should move nothing (or only new files). If it churns forever, fix rules.
-
API swap: Replace scrape demo with
requests.geton a public JSON API (NASA APOD, open weather demo with key in.env, etc.). Print one field—same politeness: rate limits.
Your Challenge
Portfolio mini-project: "Personal Ops Kit"
Build a small repo (see Module 08) with:
organize.py— organizes one real folder you use for school or clubs (withDRY_RUNand comments).fetch_info.py— one ethical scrape OR a script that calls a public API instead (even better if the site offers JSON).run_daily.py— usesscheduleOR documents how to hookorganize.pyinto Task Scheduler / cron (paste your OS steps in the README).README.md— what it does, how to install, what you will NOT automate (integrity/privacy), and a screenshot of a dry-run.
Rubric: Could a classmate run your README on their machine without DMing you six times?
Step-by-step README pass (before you call it "done"):
- Wipe assumptions: clone into a new empty folder on a different device if possible.
- Follow your own instructions literally—no side knowledge.
- Record every stumble; each stumble becomes a README bullet or a script fix.
- Add
python -m venv .venv+ activate lines if you use packages—Windows and Mac differ; link to official docs. - Paste one successful terminal transcript at the bottom as proof.
Fun Fact: Hiring managers sometimes run student repos once. Your dry-run GIF + transcript is evidence you respect other people's time.
Stretch: Add Makefile or one cross-platform tasks.py only if your teacher agrees—otherwise plain README commands are perfect.
Key Takeaways
- Automation is repeatable rules executed by code—start with dry runs and logs.
pathlib+shutilare your file-organizing backbone; never overwrite blindly.- Scraping is powerful and easy to abuse—prefer APIs, respect ToS, add delays, and ask adults when unsure.
scheduleteaches the idea of timers; OS schedulers run when your Python window is closed (if the machine is on).- Workflow thinking (input → process → output → log) scales from homework to internships.
- Ethics are not optional—your script is your intent frozen into steps.
Going Further
- Read the official Python tutorial section on file I/O and
pathlib(docs.python.org). - Explore
richortyperlibraries for prettier CLI output—great for portfolio polish. - Pair with Module 08: pin
requirements.txtand add a GIF of your organizer dry-run. - Learn one cloud "serverless" tutorial later—only after local scripts feel boring, not before.
Quick Reference
| Task | Module building block |
|---|---|
| Sort files by type | Extension map + shutil.move |
| Parse a web page | requests + BeautifulSoup |
| Run every night | schedule loop or OS Task Scheduler |
| Stay safe | DRY_RUN, logs, no secrets in code, read ToS |
You are not "lazy" for automating drudgery—you are strategic. Build small scripts, respect boundaries, and let your attention go where only humans can go.