Python'da Oturum ve Çerez Yönetimi: Session ile Giriş

Yayın tarihi:

15 dk okuma

Acar Diveroli
Yazar: Acar Diveroli
Terminaldeki set-cookie satırından çerez kavanozundaki sessionid fişine uzanan bağ ve diske yazılan cerezler.json dosyası

Kendi şirketinizin paneline her sabah elle girip bir rapor indiriyorsunuz. Panelin dışa aktarma düğmesi var, resmî bir API'si yok ve aynı beş tıklama her gün tekrarlanıyor. Bunu bir Python betiğine devretmek istediğinizde ilk duvar giriş sayfasıdır: requests.get() ile indirdiğiniz sayfa rapor değil, giriş formudur. Çünkü HTTP durumsuz bir protokoldür ve sunucu sizi ancak her isteğe eklediğiniz bir çerezden tanır.

Bu yazıda çerez ile oturumun ne olduğunu, CSRF belirtecinin neden her formda yeniden okunduğunu ve requests.Session ile bir giriş akışının nasıl kurulduğunu anlatıyoruz. Ardından oturumu diske kaydetmeyi, süresi dolunca yenilemeyi, kimlik bilgisini koddan çıkarmayı ve oturum boyunca aynı çıkış IP'sinde kalmayı ele alıyoruz. Sonunda formunu JavaScript ile gönderen siteler için Playwright'ın storage_state yöntemi var. Kod örnekleri quotes.toscrape.com üzerinde denendi.

Bu yazı hangi girişler için geçerli?

Buradaki her şey tek bir varsayıma dayanıyor: giriş yaptığınız hesap sizin, ya da hesabın sahibi size yazılı izin verdi. Kendi şirketinizin yönetim paneli, kendi mağaza hesabınız, müşterinizin "şu raporu her gün bize çekin" diye yazıyla istediği sistem. Bunun dışındaki senaryolar bu yazının konusu değil.

Koda geçmeden önce üç soru:

  1. Resmî bir API ya da dışa aktarma var mı? Varsa arayüzü otomatikleştirmeyin. Bir API anahtarı kararlıdır ve arayüz değiştiğinde kırılmaz; giriş otomasyonu veriye başka türlü ulaşamadığınızda başvurulan yoldur.
  2. Sitenin kullanım şartları ne diyor? Otomatik erişimi yasaklayan bir madde varsa hesabınız askıya alınabilir. Teknik olarak mümkün olması izinli olduğu anlamına gelmez. Konunun hukuki çerçevesini Web Scraping (Web Kazıma) Yasal mı? yazımızda ele aldık.
  3. İzniniz yazılı mı? Müşteri işlerinde sözlü onay yeterli değildir. Hangi hesapla, hangi sayfalardan, hangi sıklıkta veri çekeceğiniz bir sözleşmede ya da e-postada yazılı olsun.

Bu yazıda bilerek anlatılmayan dört şey var:

  • Parola denemek yok. Bir listeyi form alanına sırayla yazdıran kod (credential stuffing) ya da kombinasyon üreten kod (brute force) burada geçmiyor. Bunları yapmayın: başkasının hesabına yetkisiz erişim Türk Ceza Kanunu'nun 243. maddesinin alanına girer.
  • İki aşamalı doğrulama ve CAPTCHA atlatma yok. Bu kontroller hesabı korumak için konuldu; bir betikle aşmaya çalışmak, korumaya çalıştığınız hesabın güvenliğini düşürür. Kendi hesabınızda 2FA açıksa doğru yol sağlayıcının API anahtarı ya da uygulama parolası mekanizmasıdır; yoksa o iş otomatikleşmeyecek demektir.
  • Başkasının hesabı yok. "Arkadaşımın hesabı", "şirketten ayrılan kişinin hesabı" ya da internette paylaşılmış kimlik bilgileri de buna dahildir.
  • Oturum çerezi çalmak ya da taşımak yok. Başka birinin tarayıcısından kopyalanan çerezle açılan oturum, o kişinin kimliğine bürünmektir. Bu yazıdaki çerez dosyaları yalnız sizin kendi girişinizle üretilir ve yalnız sizin makinenizde durur.

Bir ayrım da isim benzerliği yüzünden gerekiyor: proxy sunucusuna kimlik doğrulamak (user:pass ya da IP yetkilendirme) ile hedef siteye giriş yapmak farklı iki iştir. Birincisini Proxy Kimlik Doğrulama: User:Pass ve IP Whitelist yazımızda anlattık; bu yazının konusu ikincisi.

Çerez ve oturum nedir?

HTTP durumsuzdur: sunucu, iki isteği birbirine bağlayan hiçbir şey hatırlamaz. Bu boşluğu çerezler doldurur. Sunucu yanıtına bir Set-Cookie başlığı koyar, istemci bu değeri saklar ve aynı alan adına giden sonraki isteklere Cookie başlığıyla geri gönderir. Başlığın tüm alanları MDN'in Set-Cookie sayfasında tek tek açıklanır.

"Oturum" ise bu çerezin bir anlamı olmasıdır. İki yaygın kurgu vardır:

  • Sunucu tarafı oturum. Çerezde yalnız rastgele bir kimlik (sessionid, PHPSESSID, JSESSIONID) taşınır; kullanıcının kim olduğu sunucudaki bir kayıtta durur. Sunucu o kaydı silerse oturum, çerez elinizde olsa bile biter.
  • İmzalı çerez. Kullanıcı bilgisi çerezin içindedir ve sunucu onu gizli bir anahtarla imzalar. Flask'ın varsayılan session çerezi böyledir.

Çerezin ne kadar yaşayacağını Expires ve Max-Age belirler. RFC 6265'in 4.1.2.2 numaralı bölümü şunu söyler: bir çerezde Max-Age da Expires da yoksa, istemci onu "geçerli oturum bitene kadar" tutar. Tarayıcıda bu "tarayıcıyı kapatana kadar" demektir; bir Python betiğinde ise "süreç yaşadığı sürece". Bu yüzden betiğiniz her çalıştığında yeniden giriş yapıyorsa şaşırmayın: sakladığı çerez zaten kalıcı değildi.

Çerezin hangi isteklere ekleneceğini ise Set-Cookie başlığının şu alanları belirler:

AlanNe yaparBetikte anlamı
DomainÇerezin hangi alan adına gideceğipanel.ornek.com çerezi ornek.com isteğine eklenmez
PathHangi yol altında geçerli olduğu/rapor yolundaki çerez / isteğine gönderilmez
SecureYalnız HTTPS üzerinden gönderilirHTTP'ye düşen bir istek çerezi taşımaz
HttpOnlySayfa içi JavaScript okuyamazTarayıcı konsolundan document.cookie ile göremezsiniz, HTTP istemcisi yine görür
SameSiteBaşka siteden gelen isteklere eklenip eklenmeyeceğiStrict ise dış bağlantıdan gelen ilk istek oturumsuz görünür

Bu tablo teorik değil, teşhis listesidir: "giriş başarılı görünüyor ama sonraki istek yine giriş sayfasına düşüyor" şikâyetlerinin çoğu Domain ya da Path uyuşmazlığından çıkar.

CSRF belirteci nedir ve neden her seferinde okunur?

Bir bankacılık sayfasında oturumunuz açıkken başka bir sitedeki gizli form sizin adınıza istek gönderebilir. Tarayıcı çerezi otomatik eklediği için sunucu bunu sizin isteğiniz sanır. Buna siteler arası istek sahteciliği (CSRF) denir.

En yaygın savunma, OWASP'ın CSRF önleme sayfasında "senkronize belirteç" diye geçen yöntemdir: sunucu her form sayfasına, o oturuma bağlı rastgele bir değeri gizli bir alan olarak gömer. Form gönderildiğinde bu değer, oturum çerezindeki karşılığıyla eşleşmek zorundadır. Başka bir sitedeki form, sizin oturumunuza ait belirteci bilemeyeceği için eşleşme tutmaz.

Bunun betik yazan taraf için üç sonucu var:

  • Belirteç sabit değildir. Bir kez tarayıcıdan kopyalayıp koda gömerseniz ertesi gün çalışmaz. Her çalıştırmada giriş sayfasını indirip alanı oradan okumanız gerekir.
  • Belirteç oturuma bağlıdır. Belirteci bir istekte, formu başka bir bağlantıda göndermek işe yaramaz. İkisi de aynı Session nesnesinden geçmelidir; zaten Session kullanmanın ilk nedeni bu.
  • Alanın adı siteye göre değişir. csrf_token, csrfmiddlewaretoken (Django), authenticity_token (Rails), _token (Laravel) hepsi aynı işi görür. Kod yazmadan önce formun kaynağına bakın.

Belirteci gizli alan yerine bir başlıkta (X-CSRF-Token) bekleyen uygulamalarda değeri yine sayfadan okursunuz, ama gövdeye değil headers sözlüğüne koyarsınız.

Hangi yolu seçmeli?

Giriş gerektiren bir işte üç yol var ve seçimi sitenin giriş formu belirler.

YolNe zamanMaliyetiZayıf yanı
requests.SessionForm düz HTML, alanlar sayfada görünüyorEn düşük, tarayıcı yokJavaScript ile üretilen alanları göremez
Playwright storage_stateGiriş formu JavaScript ile gönderiliyor ya da belirteç betikle üretiliyorYüksek, gerçek tarayıcı açılırBellek ve süre, kurulum yükü
Hibrit: tarayıcıyla gir, HTTP ile devam etGiriş karmaşık ama veri sayfaları düz HTMLBir kerelik tarayıcı, sonrası ucuzÇerezler iki ortam arasında taşınır, IP tutarlılığı şart

Seçimi tahminle değil, sayfanın kaynağına bakarak yapın: <form method="post"> içinde name taşıyan input alanları görüyorsanız requests yeter. Alanlar boşsa ya da form fetch() ile gönderiliyorsa ikinci ya da üçüncü yola geçin.

requests.Session ile giriş nasıl yapılır?

Sırası önemli beş adım var: ilk dördü girişin kendisi, beşincisi sonraki çalıştırmalar için.

  1. Giriş sayfasını GET ile indirin. Bu yanıt iki şey getirir: henüz anonim olan bir oturum çerezi ve formdaki CSRF belirteci. Session çerezi kendi kavanozuna koyar.
  2. Form alanlarını gerçekten okuyun. Tarayıcıda formu doldurup gönderin ve geliştirici araçlarının Ağ sekmesinde isteğin gövdesine bakın. Alan adları tahmin edilmez, oradan kopyalanır; gizli bir next ya da redirect alanı varsa onu da gönderin.
  3. POST isteğini aynı Session ile atın. Çerez kendiliğinden eklenir. Formun action alanı başka bir yol gösteriyorsa isteği oraya gönderin.
  4. Girişi içerikten doğrulayın. Durum kodu yanıltıcıdır: pek çok uygulama hatalı parolada da 200 döner ve formu bir hata mesajıyla yeniden basar. Ölçüt, yalnız oturum açıkken görünen bir öğedir: çıkış bağlantısı, kullanıcı adı, hesap menüsü.
  5. Çerezleri saklayın. Sonraki çalıştırma giriş adımını atlar.

Requests belgelerinin Session Objects bölümüne göre Session, çerezleri örnek boyunca saklamasının yanında aynı sunucuya giden istekler için TCP bağlantısını da yeniden kullanır. Desen HTTPX, Requests ve AIOHTTP yazımızdaki üç kütüphanede de aynıdır, yalnız sınıf adı değişir.

Çalışan örnek: quotes.toscrape.com

Aşağıdaki kod quotes.toscrape.com/login üzerinde denenmiştir. Burası scraping alıştırması için kurulmuş açık bir eğitim sitesidir ve girdiğiniz her kullanıcı adı ile parolayı kabul eder; bir kimlik doğrulaması yapmaz, yalnız akışı taklit eder. Mekanizmayı önce burada görmek, kendi hesabınızda arka arkaya başarısız giriş üretmenizi önler.

Önce çekirdek akış, dört adım:

python
import os

import requests
from bs4 import BeautifulSoup

BASE_URL = "https://quotes.toscrape.com"

session = requests.Session()

# 1) Giriş formunu iste: oturum çerezi ve CSRF belirteci bu yanıtla gelir
page = session.get(f"{BASE_URL}/login", timeout=20)
page.raise_for_status()
token = BeautifulSoup(page.text, "html.parser").select_one('input[name="csrf_token"]')["value"]

# 2) Kimlik bilgisi koda gömülmez, ortam değişkeninden okunur
payload = {
    "csrf_token": token,
    "username": os.environ["SITE_USERNAME"],
    "password": os.environ["SITE_PASSWORD"],
}

# 3) Formu aynı Session ile gönder; çerezi Session kendisi ekler
result = session.post(f"{BASE_URL}/login", data=payload, timeout=20)
result.raise_for_status()

# 4) Durum koduna değil, sayfanın içeriğine bakarak doğrula
if BeautifulSoup(result.text, "html.parser").select_one('a[href="/logout"]') is None:
    raise SystemExit("Giriş doğrulanamadı")

print("Giriş başarılı, çerezler:", list(session.cookies.keys()))

Kimlik bilgisini çalıştırmadan önce ortama koyun; parolayı koda yazmak onu sürüm geçmişine de yazmaktır:

bash
export SITE_USERNAME="kullanici"
export SITE_PASSWORD="parola"
python login.py

Windows PowerShell'de $env:SITE_USERNAME = "kullanici" biçimi kullanılır. Ortam değişkenlerinin komut satırı araçlarıyla nasıl kalıcı hâle getirileceğini Wget ile Proxy Kullanımı yazımızda anlattık.

Oturum diske nasıl kaydedilir ve ne zaman yenilenir?

Her çalıştırmada yeniden giriş yapmak hesabınızın güvenlik günlüğünü gereksiz kayıtlarla doldurur; bazı uygulamalarda kısa sürede tekrarlanan girişler ek doğrulama da tetikler. Çözüm, çerez kavanozunu bir dosyaya yazıp sonraki çalıştırmada geri yüklemektir.

Aşağıdaki tam betik bunu yapar: kayıtlı çerez varsa yükler, yoksa giriş yapar, oturumun hâlâ açık olduğunu her istekte kontrol eder ve düştüyse bir kez yeniden giriş yapar. Tek seferlik olması önemli: koşulsuz bir döngü, sorun kimlik bilgisindeyse sonsuz giriş denemesine dönüşür.

python
import json
import os
import time
from pathlib import Path

import requests
from bs4 import BeautifulSoup

BASE_URL = "https://quotes.toscrape.com"
COOKIE_FILE = Path("session_cookies.json")
DELAY_SECONDS = 2.0


class LoginError(Exception):
    """Giriş tamamlanamadı; yeniden denemek yerine nedenine bakın."""


def build_session():
    session = requests.Session()
    session.headers["User-Agent"] = "rapor-betigi/1.0 (iletisim: siz@ornek.com)"
    proxy_url = os.environ.get("PROXY_URL")  # ör. http://user:pass@pr.proxynet.io:8000
    if proxy_url:
        session.proxies = {"http": proxy_url, "https": proxy_url}
    return session


def save_cookies(session, path=COOKIE_FILE):
    cookies = [
        {"name": c.name, "value": c.value, "domain": c.domain, "path": c.path,
         "expires": c.expires, "secure": c.secure}
        for c in session.cookies
    ]
    path.write_text(json.dumps(cookies), encoding="utf-8")
    try:
        path.chmod(0o600)  # dosya parola kadar hassastır; Windows'ta etkisi sınırlıdır
    except OSError:
        pass


def load_cookies(session, path=COOKIE_FILE):
    if not path.exists():
        return False
    try:
        cookies = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        return False
    now = time.time()
    for c in cookies:
        if c["expires"] and c["expires"] < now:
            continue  # süresi dolmuş çerezi hiç yükleme
        session.cookies.set(c["name"], c["value"], domain=c["domain"], path=c["path"],
                            expires=c["expires"], secure=c["secure"])
    return True


def is_logged_in(html):
    return BeautifulSoup(html, "html.parser").select_one('a[href="/logout"]') is not None


def login(session):
    page = session.get(f"{BASE_URL}/login", timeout=20)
    page.raise_for_status()
    field = BeautifulSoup(page.text, "html.parser").select_one('input[name="csrf_token"]')
    if field is None:
        raise LoginError("Formda csrf_token alanı yok; sayfa yapısı değişmiş olabilir")
    payload = {
        "csrf_token": field["value"],
        "username": os.environ["SITE_USERNAME"],
        "password": os.environ["SITE_PASSWORD"],
    }
    result = session.post(f"{BASE_URL}/login", data=payload, timeout=20)
    result.raise_for_status()
    if not is_logged_in(result.text):
        raise LoginError("Giriş doğrulanamadı; kimlik bilgisini ve form alanlarını kontrol edin")
    save_cookies(session)


def get_page(session, path):
    """Sayfayı getirir; oturum düşmüşse yalnız bir kez yeniden giriş yapar."""
    for attempt in range(2):
        response = session.get(f"{BASE_URL}{path}", timeout=20)
        response.raise_for_status()
        if is_logged_in(response.text):
            return response
        if attempt == 0:
            print("Oturum geçersiz, yeniden giriş yapılıyor")
            session.cookies.clear()
            login(session)
    raise LoginError("Yeniden girişten sonra da oturum açılamadı; durun ve nedenine bakın")


def main():
    session = build_session()
    if load_cookies(session):
        print("Kayıtlı çerezler yüklendi")
    else:
        print("Kayıtlı oturum yok, giriş yapılıyor")
        login(session)

    for number in range(1, 4):
        response = get_page(session, f"/page/{number}/")
        quotes = BeautifulSoup(response.text, "html.parser").select("div.quote")
        print(f"Sayfa {number}: {len(quotes)} kayıt")
        time.sleep(DELAY_SECONDS)


if __name__ == "__main__":
    main()

İlk çalıştırma "Kayıtlı oturum yok" diyerek giriş yapar, ikincisi "Kayıtlı çerezler yüklendi" diyerek doğrudan veriye geçer. Dikkat edilecek üç nokta:

  • Çerez dosyası bir sırdır. İçindeki değer, o oturum için parolanın yerine geçer. Dosyayı .gitignore'a ekleyin, yedeklere ve paylaşılan klasörlere koymayın; Playwright'ın belgeleri de durum dosyası için aynı uyarıyı yapar.
  • Süresi dolan çerezi yüklemeyin. load_cookies bunu expires alanına bakarak eler; yoksa betik geçersiz bir oturumla yola çıkar.
  • Oturumun düştüğünü içerikten anlayın. Bazı uygulamalar giriş sayfasına 302 ile yönlendirir, bazıları 200 ile formu basar. is_logged_in gibi tek bir ölçüt ikisini de yakalar.

Oturum boyunca neden aynı IP gerekir?

Uygulamaların bir kısmı açılan oturumu, girişin yapıldığı IP adresine ya da o adresin ağına bağlar. Sonraki istek başka bir adresten gelirse oturum kapatılır, kullanıcı giriş sayfasına yönlendirilir ya da ek doğrulama istenir. Bu bir güvenlik kararıdır ve amacı çalınmış bir oturum çerezinin başka bir yerde kullanılmasını zorlaştırmaktır.

Proxy kullanan bir betikte bu şu anlama gelir: rotasyonlu bir havuz, giriş gerektiren işlerde oturumu bozar. Rotasyonlu proxy her istekte ya da kısa aralıklarla çıkış IP'sini değiştirir; giriş yaptığınız adresle rapor indirdiğiniz adres farklı olur ve uygulama sizi tanımaz. Rotasyonun nasıl çalıştığını ve hangi işler için doğru olduğunu IP Rotasyonu Nedir ve Nasıl Çalışır? yazımızda anlattık.

Doğru kurgu iki seçenekten biridir:

  • Sticky Proxy ile panelden 1-60 dakika arasında seçtiğiniz süre boyunca aynı çıkış IP'sinde kalırsınız. Oturum süresi kısaysa ve iş bitince bağlantıyı bırakacaksanız bu yeter.
  • ISP Proxy ile adres çalışmadan çalışmaya da sabit kalır. Panele erişimi belirli IP'lerle sınırlayan sistemlerde tek yol budur, çünkü adresi karşı tarafa bir kez bildirip listeye eklettirirsiniz.

Adresi bir API'nin izin listesine ekletme senaryosu API için Statik IP: IP Yetkilendirme Hatası Nasıl Çözülür? yazımızda, sabit IP'nin genel gerekçesi ise Web Scraping'de Engellenmeden Veri Toplama Yöntemleri yazımızın "Oturum gereken yerde sticky IP" bölümünde.

Bir yanlış okumanın önüne geçelim: sabit IP bir güvenlik kontrolünü atlatma aracı değildir. Yaptığı şey, zaten yetkili olduğunuz bir hesapta kendi trafiğinizi tutarlı göstermektir. Hesap sizin değilse sabit IP de yasal bir erişim üretmez.

Hız sınırı: betiği nazik tutmak

Giriş yapmış bir betik, anonim bir betikten daha görünürdür: istekleriniz artık bir IP adresine değil, doğrudan hesabınıza yazılır. Hız sınırına uymak burada teknik bir tercih değil, hesabınızı korumanın parçasıdır.

Giriş JavaScript ile yapılıyorsa: Playwright storage_state

Bazı uygulamalarda giriş formu klasik bir POST göndermez: alanları JavaScript okur, belirteci tarayıcı üretir, yanıt bir API çağrısıyla gelir. Böyle bir sayfada requests ile gönderilen form sessizce başarısız olur. Bu noktada gerçek bir tarayıcı çalıştırmak gerekir.

Playwright, oturum durumunu tek bir JSON dosyasına yazabilir. Playwright'ın kimlik doğrulama belgelerine göre bu dosya çerezleri ve localStorage içeriğini birlikte taşır; yani oturum bilgisini çerez yerine localStorage içinde tutan uygulamalarda da işe yarar. Uygulama kimlik belirtecini IndexedDB içinde saklıyorsa bunu ayrıca istemeniz gerekir: context.storage_state(path=..., indexed_db=True).

python
import os
from pathlib import Path

from playwright.sync_api import sync_playwright

BASE_URL = "https://quotes.toscrape.com"
STATE_FILE = Path("storage_state.json")
PROXY = {"server": "http://pr.proxynet.io:8000", "username": "user", "password": "pass"}

with sync_playwright() as p:
    browser = p.chromium.launch(proxy=PROXY)
    if STATE_FILE.exists():
        context = browser.new_context(storage_state=STATE_FILE)  # kayıtlı oturumla aç
    else:
        context = browser.new_context()
    page = context.new_page()
    page.goto(BASE_URL)

    if page.locator('a[href="/logout"]').count() == 0:
        page.goto(f"{BASE_URL}/login")
        page.fill("#username", os.environ["SITE_USERNAME"])
        page.fill("#password", os.environ["SITE_PASSWORD"])
        page.click('input[type="submit"]')
        page.wait_for_selector('a[href="/logout"]')  # girişin kanıtı
        context.storage_state(path=STATE_FILE)  # çerezler ve localStorage tek dosyada
        print("Giriş yapıldı, durum kaydedildi")
    else:
        print("Kayıtlı durumla oturum açık")

    browser.close()

İlk çalıştırma giriş yapıp dosyayı yazar, ikincisi giriş adımını hiç görmez. Playwright'ın proxy ayarını ve tarayıcı kurulumunu Playwright Nedir ve Proxy ile Nasıl Kullanılır? yazımızda ayrıntılı anlattık; Selenium tarafındaki karşılığı için Selenium Proxy Entegrasyonu yazımıza bakabilirsiniz.

Hibrit desen buradan çıkar: tarayıcıyla bir kez girip storage_state.json üretir, sonra çerezleri bir requests.Session'a aktarıp veriyi ucuz yoldan çekersiniz.

python
import json
from pathlib import Path

import requests

state = json.loads(Path("storage_state.json").read_text(encoding="utf-8"))
session = requests.Session()
for c in state["cookies"]:
    session.cookies.set(c["name"], c["value"], domain=c["domain"], path=c["path"])

Bu desenin tek şartı var: tarayıcı da, ardından gelen HTTP istemcisi de aynı çıkış IP'sinden çıkmalı. İkisine farklı proxy verirseniz oturum ilk istekte düşer. Hangi tarayıcı otomasyonunun hangi işe uyduğunu Playwright ve Selenium Farkı: Hangisini Seçmeli? yazımızda karşılaştırdık.

Kullanım alanları

  • Kendi panelinizden günlük rapor indirme. Dışa aktarma düğmesinin arkasındaki adres çoğu zaman düz bir dosya bağlantısıdır; oturum çerezi durdukça requests ile indirilir. Genel kurgu veri kazıma çözümü sayfamızda.
  • Giriş arkasındaki bir akışı izleme. Kendi uygulamanızın giriş ve sepet adımlarını her dağıtımdan sonra denetlemek için; kurgusu uygulama testi sayfamızda.
  • Müşteri sisteminden veri aktarımı. Yazılı izin ve sabit bir çıkış IP'siyle, karşı taraf o adresi kendi listesine ekleyebilir.
  • Çok sayfalı listeden veri toplama. Oturum açıldıktan sonrası sıradan bir sayfalama işidir; deseni Sayfalama (Pagination) Nedir, Scraping'de Nasıl Taranır? yazımızda anlattık.

Sık yapılan hatalar ve teşhis

  • Giriş sayfasına sonsuz yönlendirme. İstek 302 ile /login'e dönüyorsa çerez ya hiç gönderilmiyor ya da sunucu tarafında geçersiz. Önce session.cookies içeriğine bakın, sonra çerezin Domain alanını istediğiniz adresle karşılaştırın.
  • Session yerine requests.get kullanmak. Modül düzeyindeki requests.get() her çağrıda yeni bir bağlantı ve boş bir çerez kavanozu açar. Giriş akışında çalışmaz.
  • Girişi durum koduna bakarak doğrulamak. Hatalı parolada da 200 gelebilir. İçerikten bir kanıt arayın.
  • CSRF belirtecini koda gömmek. Bir gün çalışır, ertesi gün "geçersiz belirteç" hatası verir.
  • Oturum ortasında IP değiştirmek. Rotasyonlu bir havuzla giriş yapıp veri çekmek, oturumun düşmesinin en sık nedenidir.
  • Başarısız girişte döngüye girmek. Parola yanlışsa tekrar denemek düzeltmez; bazı sistemlerde hesabı kilitler. Betik ilk başarısızlıkta durmalıdır.

Karar rehberi

DurumYapılacak
Sitenin resmî API'si varGiriş otomasyonuna hiç girmeyin, API anahtarı kullanın
Form düz HTML, alanlar sayfadarequests.Session ile giriş, çerezleri dosyaya kaydedin
Formda csrf_token benzeri gizli alanHer çalıştırmada sayfadan okuyun, koda gömmeyin
Form JavaScript ile gönderiliyorPlaywright ile girin, storage_state dosyasını saklayın
Giriş karmaşık, veri sayfaları düzHibrit: tarayıcıyla gir, çerezleri requests'e taşı
Oturum ortasında kapanıyorÇıkış IP'sini sabitleyin: Sticky Proxy
Panel yalnız belirli IP'lere açıkISP Proxy ile sabit adres
Hesapta iki aşamalı doğrulama varAPI anahtarı ya da uygulama parolası isteyin, doğrulamayı aşmaya çalışmayın
Hesap sizin değilDurun; sahibinden yazılı izin alın

Sıkça sorulan sorular

Çerez, sunucunun tarayıcınıza ya da istemcinize sakladığı küçük bir veri parçasıdır ve her istekte geri gönderilir. Oturum ise o çerezin işaret ettiği durumdur: kim olduğunuz, ne zamandan beri giriş yaptığınız, hangi yetkileriniz olduğu. Python tarafında requests.Session bu ikisini birleştiren nesnedir; çerezleri saklar ve istekler arasında taşır.

Giriş yaptım ama sonraki istek yine giriş sayfasına düşüyor, neden?

En sık üç neden var. Birincisi, giriş isteğini Session dışında atmışsınızdır ve çerez kaybolmuştur. İkincisi, çerezin Domain ya da Path alanı isteğinizin adresiyle uyuşmuyordur. Üçüncüsü, uygulama oturumu IP'ye bağlamıştır ve rotasyonlu bir proxy yüzünden ikinci istek başka adresten gelmiştir.

CSRF belirtecini bir kere alıp saklayabilir miyim?

Hayır. Belirteç oturuma bağlıdır ve oturum yenilendiğinde değişir. Her çalıştırmada giriş sayfasını indirip alanı oradan okumanız gerekir. Saklanacak şey belirteç değil, girişten sonra gelen oturum çerezidir.

Şifreli bir siteden veri çekmek suç mu?

Belirleyici olan sitenin şifreli olması değil, o hesaba erişim yetkinizin olup olmadığıdır. Kendi hesabınızdan kendi verinizi çekmek olağan bir kullanımdır; başkasının kimlik bilgisiyle giriş yapmak yetkisiz erişimdir ve Türk Ceza Kanunu'nun bilişim suçları bölümünün alanına girer. Kullanım şartları otomatik erişimi yasaklıyorsa kendi hesabınızda bile sözleşmeye aykırı davranmış olursunuz. Ayrıntısı Web Scraping (Web Kazıma) Yasal mı? yazımızda.

Oturum çerezi ne kadar süre geçerli olur?

Uygulamaya göre değişir. Max-Age ya da Expires taşımayan bir çerez, istemci kapandığında biter. Süre taşıyanlar birkaç saatten birkaç haftaya kadar yaşayabilir, ama sunucu kendi tarafındaki kaydı sildiğinde çerez elinizde olsa bile oturum biter. Bu yüzden betikte "süresi doldu mu" kontrolü yerine "oturum hâlâ açık mı" kontrolü yapılır.

Giriş yaptıktan sonra proxy'yi değiştirebilir miyim?

Değiştirmemek gerekir. Oturumu açan IP ile sonraki isteklerin çıktığı IP farklıysa uygulama oturumu kapatabilir ya da ek doğrulama isteyebilir. Aynı iş içinde hem tarayıcı hem HTTP istemcisi kullanıyorsanız ikisine de aynı proxy'yi verin.

Özetle

Giriş gerektiren bir işte teknik çekirdek küçüktür: requests.Session çerezleri taşır, giriş sayfasından okunan CSRF belirteci forma eklenir, sonuç durum koduna değil sayfa içeriğine bakılarak doğrulanır ve oturum bir dosyaya kaydedilip sonraki çalıştırmaya devredilir. Formunu JavaScript ile gönderen sitelerde aynı işi Playwright'ın storage_state dosyası yapar. Oturumun düşmemesi için çıkış IP'si oturum boyunca değişmemeli; bunun için Sticky Proxy ya da sabit adres kullanılır. Ön koşul ise değişmez: hesap sizin olacak ya da sahibinin yazılı izni bulunacak, resmî bir API varsa önce o denenecek. Uygun proxy türlerini proxy hizmetlerimizde bulabilirsiniz.

ChatGPT'ye sorClaude'a sor