# Automating repetitive tasks with Python

> How to move manual workflows into reliable Python scripts: which tasks are worth scripting and how to do it right.

- Published: 2022-08-07
- Category: Languages
- Tags: Python
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/automating-repetitive-tasks-with-python/
- Language: en-US
- Author: Muhammet Şafak

---
When you find yourself doing the same workflow for the third time by hand, something should happen: write the script. This rule applies in any language, but Python is a particularly good fit for the job — minimal syntax, a broad standard library, and an ecosystem built exactly for this kind of work.

In this post I want to share my thinking on scripting repetitive tasks, along with a few patterns I use in practice. Not large automation frameworks or orchestration tools — just concrete, hands-on scripts.

## Recognizing what's worth scripting

Not every repetitive task is worth automating. I ask these questions:

- How often does this happen? It should be multiple times a day or every day — not once a week.
- Are the steps fixed, or does each run require different judgment calls? Tasks with fixed steps can be scripted; decision-heavy tasks generally can't.
- Is there error risk? Is it easy to skip a step, write to the wrong directory, or delete a file when doing this by hand?

The more yes answers accumulate, the higher the value of the script.

## File operations: simple but common

Renaming files in a directory according to a rule, moving files with certain extensions to another folder, [merging a large number of CSV files](/en/blog/writing-data-processing-scripts-with-python/) — these are tasks you can write with Python's standard library in minutes.

```python
import os
import shutil
from pathlib import Path

def move_by_extension(source_dir: str, target_dir: str, extension: str) -> int:
    source = Path(source_dir)
    target = Path(target_dir)
    target.mkdir(parents=True, exist_ok=True)

    moved = 0
    for file in source.glob(f"*.{extension}"):
        dest = target / file.name
        if dest.exists():
            print(f"Atlandı (zaten var): {file.name}")
            continue
        shutil.move(str(file), str(dest))
        moved += 1

    return moved

if __name__ == "__main__":
    count = move_by_extension("./indirilenler", "./arsiv/pdf", "pdf")
    print(f"{count} dosya taşındı.")
```

I prefer `pathlib` over `os.path` — it's more readable and object-oriented. `shutil.move` handles the actual move in a platform-independent way.

## Fetching data from external APIs

Having to regularly pull and process data from a third-party service is extremely common. Python's `requests` library makes this straightforward:

```python
import requests
import json
from datetime import datetime

def fetch_and_save(api_url: str, output_file: str) -> None:
    response = requests.get(api_url, timeout=10)
    response.raise_for_status()  # raises on 4xx/5xx responses

    data = response.json()

    with open(output_file, "w", encoding="utf-8") as f:
        json.dump({
            "fetched_at": datetime.utcnow().isoformat(),
            "count": len(data),
            "data": data,
        }, f, ensure_ascii=False, indent=2)

    print(f"{len(data)} kayıt kaydedildi: {output_file}")
```

`raise_for_status()` is a small but important habit. It prevents silent failures — if the response isn't 200 you get an early, explicit error right there, not buried somewhere in the data-processing logic.

## A few habits for reliable scripts

Writing a script is fast; writing a *reliable* script takes a bit more care.

**Idempotency:** Running the script twice should not undo what the first run did. Skip the file if it already exists, skip the record if it has already been processed. The `if dest.exists(): continue` in the example above is a small but solid illustration of this.

**Error handling:** Not sprinkling `try/except` everywhere, but making sure that when something fails you know *what* failed and *why*. The exception message should carry enough information to diagnose the problem.

**Execution logging:** A simple logging mechanism, especially for scheduled scripts. `print` is enough in most cases; for more critical scripts, Python's `logging` module.

**Argument support:** Don't hard-code values inside the script. Use `argparse` or `sys.argv` so the script can accept parameters from the outside. This improves testability and makes the script usable across different environments.

```python
import argparse

parser = argparse.ArgumentParser(description="Dosya taşıma betiği")
parser.add_argument("source", help="Kaynak klasör")
parser.add_argument("target", help="Hedef klasör")
parser.add_argument("--ext", default="pdf", help="Dosya uzantısı (varsayılan: pdf)")
args = parser.parse_args()
```

## When to reach for Python instead of PHP or Go

I work with all three languages, and for automation tasks I usually reach for Python. The reason is simple: Python's standard library is ready-made for this kind of work. `csv`, `json`, `os`, `shutil`, `pathlib`, `argparse`, `logging` — you can get a lot done without pulling in extra dependencies.

PHP works too, of course — but PHP's execution model is oriented toward web requests. You can use it for scripts, but the ecosystem isn't focused in that direction. Go's binary distribution is a strong advantage, but for small internal tools the compile cycle creates extra friction.

With Python you write fast, you iterate fast, and it works. A handful of simple habits is all it takes for your scripts to be reliable and readable. It's the right tool for this job.

If you see a repetitive task, write the script. Then make that script reusable and reliable. Over time, that cycle compounds into a significant productivity gain.
