İçeriğe geç
Muhammet Şafak
en
Diller 2 dk okuma

Python ile veri işleme betikleri yazmak

Tekrarlayan dönüştürme işlerini Python'a devretmenin pratik gerekçesini ve somut betik örneklerini paylaşıyorum.

Kapak görseli — koyu zeminde mor ışık altında dik duran delikli kart destesi, önüne saçılmış küçük yuvarlak kağıt parçalarıyla

Bu yazı 5 yıl önce yazıldı — o günün koşullarını yansıtır.


Her projede bir yerde şu görev çıkar: bir formattan alıp başka formata dönüştür. CSV’den JSON’a, Excel’den düz metne, bir API yanıtından veritabanı kaydına. Küçükse elle yapılıyor. Büyükse ve tekrarlanıyorsa bir şeyler yazmak kaçınılmaz.

Bu görevleri Python’a devretmeye karar verdiğimde Laravel projesindeyim ve PHP yazabiliyorum. Aynı betikleri PHP’de de yazabilirim. Neden Python? Bu sorunun pratikte test edilmiş bir cevabı var artık.

Neden PHP değil?

PHP web isteklerine cevap vermek için tasarlanmış. CLI desteği var, php artisan komutları Laravel projelerinde gayet işe yarıyor. Ama bir dosyayı okuyup dönüştürüp yazmak için composer.json açmak, bir sınıf hiyerarşisi kurmak istemiyorum. Bu görevler için ceremony fazla.

Python’da aynı görev:

#!/usr/bin/env python3
import csv
import json

def csv_to_json(input_path: str, output_path: str) -> None:
    rows = []
    with open(input_path, newline='', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            rows.append(dict(row))

    with open(output_path, 'w', encoding='utf-8') as f:
        json.dump(rows, f, ensure_ascii=False, indent=2)

    print(f"{len(rows)} kayıt dönüştürüldü.")

if __name__ == '__main__':
    csv_to_json('input.csv', 'output.json')

Dış bağımlılık sıfır. csv ve json standart kütüphanede. Dosya kaydet, python3 convert.py çalıştır, bitti.

Gerçek bir ihtiyaç: müşteri verisi temizleme

Geçen ay şöyle bir görev geldi: bin satırlık bir Excel dosyası, farklı formatlarda telefon numaraları (05xx xxx xx xx, +905xxxxxxxxx, 0 5xx-xxx-xxxx), bunları normalize et ve CSV olarak kaydet.

import re
import csv
import openpyxl
from typing import Optional

def normalize_phone(raw: str) -> Optional[str]:
    """Türkiye telefon numarasını 05XXXXXXXXX formatına çevirir."""
    digits = re.sub(r'\D', '', raw)

    if digits.startswith('90') and len(digits) == 12:
        digits = '0' + digits[2:]

    if len(digits) == 10 and digits.startswith('5'):
        digits = '0' + digits

    if len(digits) == 11 and digits.startswith('05'):
        return digits

    return None  # geçersiz


def process_excel(input_path: str, output_path: str) -> None:
    wb = openpyxl.load_workbook(input_path)
    ws = wb.active

    valid = []
    invalid = []

    for row in ws.iter_rows(min_row=2, values_only=True):
        name, phone_raw = row[0], str(row[1] or '')
        normalized = normalize_phone(phone_raw)
        if normalized:
            valid.append({'name': name, 'phone': normalized})
        else:
            invalid.append({'name': name, 'phone': phone_raw})

    with open(output_path, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=['name', 'phone'])
        writer.writeheader()
        writer.writerows(valid)

    print(f"Geçerli: {len(valid)}, Geçersiz: {len(invalid)}")


process_excel('musteriler.xlsx', 'temiz.csv')

openpyxl tek dış bağımlılık. Bu betiği PHP’de de yazabilirdim — ama re yerine PCRE, openpyxl yerine PhpSpreadsheet, genel sadelik yerine daha fazla boilerplate. Aynı sonuç, daha fazla sürtünme.

Bir detay daha var: bu betik geçersiz kayıtları da ayrı bir listeye alıyor. Başlangıçta bu adımı atlamıştım, yalnızca geçerli olanları yazmıştım. Müşteri şunu sordu: “Toplam 1000 satır göndermiştim, 847’si geldi, 153’üne ne oldu?” Geçersiz kayıtları ayrı bir dosyaya yazmak bu soruyu yanıtlamanın en temiz yolu oldu.

Tekrarlayan görevleri otomatize etmek

Bazı betikler tek seferlik. Bazıları ise haftalık çalıştırılıyor: FTP’den indir, dönüştür, veritabanına yükle. Bu rutinler için Python betiği + cron kombinasyonu gayet yeterli.

# weekly_import.py
import ftplib
import csv
import psycopg2  # ya da mysql-connector-python

def download_from_ftp(remote_path: str, local_path: str) -> None:
    with ftplib.FTP('ftp.supplier.com') as ftp:
        ftp.login(user='user', passwd='pass')
        with open(local_path, 'wb') as f:
            ftp.retrbinary(f'RETR {remote_path}', f.write)

def import_products(csv_path: str) -> int:
    conn = psycopg2.connect(dsn="...")
    cur = conn.cursor()
    count = 0

    with open(csv_path, newline='', encoding='utf-8') as f:
        for row in csv.DictReader(f):
            cur.execute(
                "INSERT INTO products (sku, name, price) VALUES (%s, %s, %s) "
                "ON CONFLICT (sku) DO UPDATE SET name = EXCLUDED.name, price = EXCLUDED.price",
                (row['SKU'], row['Name'], float(row['Price']))
            )
            count += 1

    conn.commit()
    cur.close()
    conn.close()
    return count

Bu kod web uygulamamın dışında yaşıyor, Laravel’i ilgilendirmiyor. Bağımsız bir araç, bağımsız bir süreç.

Betik büyüyünce dikkat edilmesi gerekenler

Tek dosyalık betikler küçük kalırsa sorun yok. Ama zamanla şunlar oluyor: önce bir fonksiyon, sonra birkaç fonksiyon, sonra “bu fonksiyonu şu betik de kullanabilir” düşüncesi ve kopyalama dönemi yeniden başlıyor. Bu noktada bir utils.py ya da küçük bir paket yapısı oluşturmak daha sağlıklı. Python’da pip install -e . ile yerel geliştirmede editable kurulum yapılabiliyor; betikler bu yerel paketten import edebiliyor.

Bir de hata yakalama meselesi var. Tek seferlik betiklerde kabaca çalışan bir şey yeterli olabilir. Ama haftalık otomatik çalışan bir betikte süreci gösteren net bir çıktı ve beklenmedik hata anında anlamlı bir mesaj önemli. try/except bloklarını üretim betiklerine eklemeyi ertelemek sonradan pişman oldunan bir karar.

Dil seçimi kararı

Veri işleme betikleri için şu kararı verdim: iş kütüphanesi gerektiriyorsa (Excel, SFTP, karmaşık regex) Python. Laravel projesiyle entegre çalışıyorsa (modeli okuyup başka modeli oluşturmak) php artisan komutu. Ölçek küçükse ve hız önemliyse Go bile deneyebilirim.

Bu ayrımı açık tutmak, her şeyi aynı dile sıkıştırmaktan daha iyi sonuç veriyor.


Python’un veri işleme betikleri için tercih olmaya başlaması bende bir dil değişikliği değil, doğru aracı seçme alışkanlığının olgunlaşması. PHP hâlâ ana dil; Python bu konudaki boşluğu dolduruyor.

Bu konudaki deneyler

Bir Sentry issue'sunu çalışan koda karşı analiz eden, düzeltmeyi diff ve regresyon testiyle sunan, onay almadan dosyaya dokunmayan Claude Code skill'i.

Şu an ne yapıyor

Bir Sentry issue'sunu çalışan koda karşı okuyup kök neden analizi ve düzeltme planı çıkarıyor; onay alınmadan tek bir dosyaya dokunmuyor. Claude Code kullanan ve prodüksiyon hatasını triyaj etmek isteyen herkes skill'i kurabilir.

Açık kaynak Python Bash Sentry API +3 daha
Temmuz 2026 — Devam ediyor

Dile özgü serileştirme yerine dondurulmuş bir JSON zarfı; polyglot kuyruk standardı BabelQueue'ya dönüştü.

Şu an ne yapıyor

Dondurulmuş JSON zarfı dört dilde — PHP, Python, Go, Node.js — aynı baytları okutuyor; sidecar ya da broker eklentisi gerekmiyor. Polyglot kuyruk kuran ekipler spesifikasyonu ve SDK'ları bugün kullanabilir.

Açık kaynak JSON Redis RabbitMQ +4 daha
Mart 2026 — Haziran 2026
Etiketler: #Python
Uzmanlık: Python Geliştirici
Paylaş:

Yorumlar

Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi