אם חיפשתם קורס אוטומציה פייתון בלי רעש מסביב, בלי מכירה של קורס ובלי הבטחות מוגזמות, המדריך הזה בנוי בדיוק כך: חמישה סקריפטים קטנים, שימושיים, בטוחים להרצה, שמראים איך פייתון עוזרת לסדר קבצים, לחפש בלוגים עם regex, לעבוד עם CSV ו-SQL, ליצור גיבויים ולנקות משימות חוזרות בשורת הפקודה.
כל הדוגמאות כאן רצות עם Python 3 ומשתמשות רק בספרייה הסטנדרטית. לא צריך חשבון חיצוני, לא צריך API, לא צריך Docker, ולא צריך להתקין חבילות. אם אתם בתחילת הדרך, מומלץ לפתוח במקביל את מדריך צעדים ראשונים ואת עמוד פייתון, ואז לחזור לכאן ולהריץ כל דוגמה בעצמכם.
מה צריך להכין לפני שמתחילים
כדי להפיק ערך מהמאמר, כדאי לעבוד בתיקייה ייעודית, למשל python-automation. כך לא מסתכנים בהרצת סקריפט על תיקייה חשובה בטעות.
- Python 3 מותקן וזמין דרך
pythonאוpython3. - עורך טקסט פשוט: VS Code, Vim, Nano או כל עורך אחר.
- שורת פקודה: ב-Linux/macOS משתמשים ב-bash; ב-Windows אפשר להשתמש ב-PowerShell.
- תיקיית ניסוי שבה מותר ליצור, להזיז ולמחוק קבצים.
ב-Linux/macOS אפשר ליצור תיקייה כך: mkdir python-automation && cd python-automation. ב-Windows PowerShell: mkdir python-automation; cd python-automation.
קורס אוטומציה פייתון בחינם: איך לחשוב על סקריפט קטן
סקריפט אוטומציה טוב לא מתחיל מקוד. הוא מתחיל ממשפט פשוט: איזו פעולה חוזרת על עצמה מספיק פעמים כדי לעצבן? משם מפרקים אותה לשלבים:
- מה הקלט? תיקייה, קובץ CSV, לוג, טקסט, רשימת שמות.
- מה הפעולה? העברה, סינון, שינוי שם, ספירה, גיבוי.
- מה הפלט? קבצים מסודרים, דוח קצר, מסד נתונים, קובץ zip.
- מה עלול להשתבש? הרשאות, קידוד, נתיב שגוי, קובץ שכבר קיים.
העיקרון החשוב ביותר: להריץ קודם במצב יבש, כלומר לראות מה היה קורה בלי לשנות כלום. לכן חלק מהסקריפטים כאן כוללים --apply; בלי הדגל הזה הם רק מדווחים.
סקריפט 1: סידור קבצים לפי סיומת
תיקיית הורדות נוטה להפוך למחסן. הסקריפט הבא מעביר קבצים לתת-תיקיות לפי הסיומת שלהם: pdf, png, txt וכן הלאה. כברירת מחדל הוא לא מזיז כלום, אלא מדפיס מה היה עושה.
from pathlib import Path
import argparse
import shutil
def organize(folder: Path, apply: bool) -> None:
if not folder.exists():
raise FileNotFoundError(f"Folder not found: {folder}")
for item in folder.iterdir():
if not item.is_file():
continue
suffix = item.suffix.lower().lstrip(".") or "no-extension"
target_dir = folder / suffix
target_file = target_dir / item.name
print(f"{item.name} -> {suffix}/{item.name}")
if apply:
target_dir.mkdir(exist_ok=True)
shutil.move(str(item), str(target_file))
def create_demo(folder: Path) -> None:
folder.mkdir(exist_ok=True)
for name in ["notes.txt", "invoice.pdf", "photo.png", "README"]:
(folder / name).write_text("demo\n", encoding="utf-8")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("folder", nargs="?", default="demo_downloads")
parser.add_argument("--apply", action="store_true")
args = parser.parse_args()
path = Path(args.folder)
if args.folder == "demo_downloads" and not path.exists():
create_demo(path)
organize(path, args.apply)שמרו כ-organize_files.py והריצו python organize_files.py. הפלט הצפוי דומה לזה:
notes.txt -> txt/notes.txt
invoice.pdf -> pdf/invoice.pdf
photo.png -> png/photo.png
README -> no-extension/READMEכדי לבצע באמת את ההעברה, הריצו python organize_files.py --apply. אם מריצים על תיקייה אמיתית, כתבו נתיב מפורש: python organize_files.py ~/Downloads ב-Linux/macOS, או נתיב מתאים ב-Windows.
סקריפט 2: שינוי שמות קבצים בצורה עקבית
כשמצלמים מסכים, מורידים דוחות או מקבלים קבצים מכמה מקורות, שמות הקבצים לא תמיד אומרים משהו. הסקריפט הבא מוסיף קידומת מספרית מסודרת לכל קובץ בתיקייה. גם כאן, בלי --apply אין שינוי בפועל.
from pathlib import Path
import argparse
def rename_files(folder: Path, prefix: str, apply: bool) -> None:
files = sorted([p for p in folder.iterdir() if p.is_file()])
for index, file_path in enumerate(files, start=1):
new_name = f"{prefix}_{index:03d}{file_path.suffix.lower()}"
new_path = folder / new_name
print(f"{file_path.name} -> {new_name}")
if apply and file_path != new_path:
file_path.rename(new_path)
def create_demo(folder: Path) -> None:
folder.mkdir(exist_ok=True)
for name in ["Screen Shot.PNG", "report.PDF", "todo.TXT"]:
(folder / name).write_text("demo\n", encoding="utf-8")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("folder", nargs="?", default="demo_rename")
parser.add_argument("--prefix", default="file")
parser.add_argument("--apply", action="store_true")
args = parser.parse_args()
path = Path(args.folder)
if args.folder == "demo_rename" and not path.exists():
create_demo(path)
rename_files(path, args.prefix, args.apply)הרצה לדוגמה: python rename_files.py --prefix doc. הפלט הצפוי:
Screen Shot.PNG -> doc_001.png
report.PDF -> doc_002.pdf
todo.TXT -> doc_003.txtשימו לב: הסקריפט לא מטפל בכפל שמות מורכב. אם כבר קיים doc_001.png, עלולה להיות התנגשות. בסביבת עבודה אמיתית כדאי להריץ קודם ללא --apply, לבדוק את הרשימה, ורק אז לבצע.
סקריפט 3: חיפוש שגיאות בלוגים עם regex
הביטוי regex מופיע הרבה כי הוא חוסך קריאה ידנית של קבצי טקסט ארוכים. הסקריפט הבא מחפש שורות לוג ברמות ERROR או WARNING, שולף זמן, רמה והודעה, ומדפיס דוח קצר.
from pathlib import Path
import re
import argparse
LOG_PATTERN = re.compile(r"^\[(?P<time>\d{2}:\d{2}:\d{2})\] (?P<level>ERROR|WARNING): (?P<message>.+)$")
def create_demo(path: Path) -> None:
path.write_text(
"[09:00:01] INFO: Service started\n"
"[09:01:15] WARNING: Disk space is low\n"
"[09:02:30] ERROR: Failed to open report.csv\n",
encoding="utf-8"
)
def scan_log(path: Path) -> None:
counts = {"ERROR": 0, "WARNING": 0}
for line in path.read_text(encoding="utf-8").splitlines():
match = LOG_PATTERN.search(line)
if not match:
continue
level = match.group("level")
counts[level] += 1
print(f"{match.group('time')} {level}: {match.group('message')}")
print(f"Summary: ERROR={counts['ERROR']}, WARNING={counts['WARNING']}")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("logfile", nargs="?", default="demo.log")
args = parser.parse_args()
log_path = Path(args.logfile)
if args.logfile == "demo.log" and not log_path.exists():
create_demo(log_path)
scan_log(log_path)אחרי שמירה כ-scan_log.py, הריצו python scan_log.py. הפלט הצפוי:
09:01:15 WARNING: Disk space is low
09:02:30 ERROR: Failed to open report.csv
Summary: ERROR=1, WARNING=1החלק החשוב הוא (?P<level>ERROR|WARNING), שמגדיר קבוצה בשם level. כך הקוד נשאר קריא יותר מאשר שימוש במספרי קבוצות כמו group(1).
סקריפט 4: מ-CSV ל-SQLite עם COALESCE
מי ששואל “מה זה SQL” בדרך כלל צריך לראות דוגמה קטנה: SQL היא שפה לשליפה ועדכון של נתונים בטבלאות. כאן ניצור קובץ CSV קטן, נטען אותו למסד SQLite מקומי, ונשתמש ב-COALESCE כדי להחליף ערכים חסרים בטקסט ברירת מחדל.
import csv
import sqlite3
from pathlib import Path
CSV_PATH = Path("tasks.csv")
DB_PATH = Path("tasks.db")
def create_csv() -> None:
rows = [
{"title": "Clean downloads", "owner": "Dana"},
{"title": "Check logs", "owner": ""},
{"title": "Create backup", "owner": "Ari"},
]
with CSV_PATH.open("w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=["title", "owner"])
writer.writeheader()
writer.writerows(rows)
def load_to_db() -> None:
connection = sqlite3.connect(DB_PATH)
try:
connection.execute("DROP TABLE IF EXISTS tasks")
connection.execute("CREATE TABLE tasks (title TEXT NOT NULL, owner TEXT)")
with CSV_PATH.open(newline="", encoding="utf-8") as file:
reader = csv.DictReader(file)
for row in reader:
owner = row["owner"] or None
connection.execute(
"INSERT INTO tasks (title, owner) VALUES (?, ?)",
(row["title"], owner)
)
connection.commit()
for title, owner in connection.execute(
"SELECT title, COALESCE(owner, 'Unassigned') FROM tasks ORDER BY title"
):
print(f"{title}: {owner}")
finally:
connection.close()
if __name__ == "__main__":
create_csv()
load_to_db()הריצו python csv_to_sqlite.py. הפלט הצפוי:
Check logs: Unassigned
Clean downloads: Dana
Create backup: Ariשימו לב לשימוש ב-? בשאילתת ההכנסה. זו כתיבה בטוחה יותר מאשר בניית SQL בעזרת חיבור מחרוזות. גם בסקריפטים פנימיים קטנים, הרגלים כאלה מונעים באגים ובעיות אבטחה.
סקריפט 5: גיבוי תיקייה לקובץ ZIP
לא כל גיבוי צריך מערכת גדולה. לפעמים צריך לשמור צילום מצב של תיקייה לפני שינוי שמות, ניקוי קבצים או ניסוי. הסקריפט הבא יוצר קובץ zip עם חותמת זמן.
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
from datetime import datetime
import argparse
def create_demo(folder: Path) -> None:
folder.mkdir(exist_ok=True)
(folder / "notes.txt").write_text("Important notes\n", encoding="utf-8")
(folder / "config.txt").write_text("demo=true\n", encoding="utf-8")
def backup_folder(folder: Path, output_dir: Path) -> Path:
output_dir.mkdir(exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
zip_path = output_dir / f"{folder.name}_{timestamp}.zip"
with ZipFile(zip_path, "w", ZIP_DEFLATED) as archive:
for path in folder.rglob("*"):
if path.is_file():
archive.write(path, path.relative_to(folder))
return zip_path
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("folder", nargs="?", default="demo_backup")
parser.add_argument("--out", default="backups")
args = parser.parse_args()
source = Path(args.folder)
if args.folder == "demo_backup" and not source.exists():
create_demo(source)
result = backup_folder(source, Path(args.out))
print(f"Created backup: {result}")הרצה: python backup_folder.py. הפלט יכלול שם קובץ עם שעה נוכחית, למשל:
Created backup: backups/demo_backup_20261026_143500.zipהשם המדויק ישתנה לפי זמן ההרצה. לפני שמריצים סקריפטים שמשנים קבצים, גיבוי כזה יכול לחסוך הרבה כאב ראש.
טבלת שימוש מהירה
| משימה | סקריפט | מתי להשתמש |
|---|---|---|
| סידור תיקייה | organize_files.py |
כשיש הרבה קבצים מעורבבים לפי סוג |
| שינוי שמות | rename_files.py |
לפני העלאה, ארכוב או שיתוף פנימי |
| חיפוש בלוג | scan_log.py |
כשצריך למצוא שגיאות מהר |
| טעינת CSV | csv_to_sqlite.py |
כשקובץ טבלאי מתחיל לגדול |
| גיבוי | backup_folder.py |
לפני פעולה שמשנה הרבה קבצים |
טעויות נפוצות ומה עושים כשנתקעים
python: can't open file 'script.py': [Errno 2] No such file or directory
הפקודה רצה מתיקייה אחרת מזו שבה נמצא הקובץ. בדקו איפה אתם עם pwd ב-Linux/macOS או Get-Location ב-Windows PowerShell. עברו לתיקייה הנכונה עם cd, או הריצו עם נתיב מלא.
FileNotFoundError: [Errno 2] No such file or directory
הנתיב לקובץ או לתיקייה שגוי. אם יש רווחים בשם התיקייה, עטפו את הנתיב במרכאות. למשל: python organize_files.py "My Downloads".
PermissionError: [Errno 13] Permission denied
אין הרשאה לקרוא או לכתוב במקום שבחרתם. אל תריצו מיד עם הרשאות מנהל. עדיף להעביר את הניסוי לתיקייה פרטית, למשל תחת תיקיית המשתמש, ולוודא שהקובץ לא פתוח בתוכנה אחרת.
UnicodeDecodeError: 'utf-8' codec can't decode byte
הקובץ לא שמור ב-UTF-8. אפשר לנסות לפתוח אותו בעורך טקסט ולשמור מחדש כ-UTF-8. אם זה לוג ישן, ייתכן שתצטרכו להחליף זמנית את encoding="utf-8" בקידוד המתאים לקובץ.
re.error: missing ), unterminated subpattern at position 0
יש טעות בביטוי הרגולרי, בדרך כלל סוגר חסר או תו מיוחד שלא עבר escaping. התחילו מביטוי קטן שעובד, ואז הוסיפו חלקים בהדרגה.
sqlite3.OperationalError: no such table: tasks
השאילתה רצה לפני יצירת הטבלה, או מול קובץ מסד אחר מזה שחשבתם. מחקו את tasks.db והריצו מחדש את הסקריפט המלא, לא רק חלק ממנו.
איך ממשיכים מכאן בלי להסתבך
אחרי שמריצים את חמש הדוגמאות, כדאי לבחור משימה אחת אמיתית מהיום-יום ולכתוב לה גרסה קטנה. לא מערכת. לא כלי ענק. רק סקריפט שמקבל קלט, עושה פעולה אחת ומדפיס תוצאה ברורה.
- התחילו תמיד עם תיקיית ניסוי.
- הוסיפו מצב יבש לפני פעולה שמשנה קבצים.
- הדפיסו מה הסקריפט עושה, במיוחד בזמן פיתוח.
- העדיפו ספרייה סטנדרטית לפני התקנת חבילות.
- שמרו גרסאות ב-Git לפני שינוי משמעותי; אם הסתבכתם, פקודות כמו
git stashיכולות לעזור לשים עבודה זמנית בצד.
אוטומציה טובה לא נמדדת בכמות שורות הקוד, אלא בכמות הפעולות הידניות שהיא מוחקת. פייתון מצטיינת בדיוק באזור הזה: קבצים, טקסטים, regex, SQL קטן, לינוקס ושורת פקודה. מי שרוצה להעמיק עוד יכול להמשיך דרך מדריכי פייתון ולהפוך כל סקריפט קטן לכלי עבודה יומיומי.


