---
title: "Python'da Oturum ve Çerez Yönetimi: Session ile Giriş"
description: "requests.Session çerezleri istekler arasında taşır ve oturumu sürdürür. Formdan CSRF belirtecini okumayı, girişi doğrulamayı ve oturumu saklamayı anlatıyoruz."
url: https://proxynet.io/tr/blog/python-login-session-cookies
date: 2026-09-19
author: "Acar Diveroli"
category: "Nasıl Yapılır, Web Scraping"
lang: tr
---

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

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.

> **Not: Kısa cevap**
>
> Bir `requests.Session` nesnesi kendi çerez kavanozunu taşır: giriş formuna `GET` yaptığınızda gelen oturum çerezini saklar, `POST` ile gönderdiğiniz form yanıtındaki yeni çerezi günceller ve sonraki her isteğe kendiliğinden ekler. Akış dört adımdır: formu iste, içindeki CSRF belirtecini oku, kullanıcı adı ve parolayla birlikte gönder, girişin gerçekten olup olmadığını sayfa içeriğinden doğrula. Bunların hepsi yalnız **kendi hesabınız** ya da hesap sahibinin yazılı izni için geçerlidir.

## 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ı?](/tr/blog/web-scraping-legal) 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](/tr/blog/proxy-authentication-methods) 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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie) 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ü](https://www.rfc-editor.org/rfc/rfc6265#section-4.1.2.2) ş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:

| Alan | Ne yapar | Betikte anlamı |
|---|---|---|
| `Domain` | Çerezin hangi alan adına gideceği | `panel.ornek.com` çerezi `ornek.com` isteğine eklenmez |
| `Path` | Hangi yol altında geçerli olduğu | `/rapor` yolundaki çerez `/` isteğine gönderilmez |
| `Secure` | Yalnız HTTPS üzerinden gönderilir | HTTP'ye düşen bir istek çerezi taşımaz |
| `HttpOnly` | Sayfa içi JavaScript okuyamaz | Tarayıcı konsolundan `document.cookie` ile göremezsiniz, HTTP istemcisi yine görür |
| `SameSite` | Başka siteden gelen isteklere eklenip eklenmeyeceği | `Strict` 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](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) "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.

| Yol | Ne zaman | Maliyeti | Zayıf yanı |
|---|---|---|---|
| `requests.Session` | Form düz HTML, alanlar sayfada görünüyor | En düşük, tarayıcı yok | JavaScript ile üretilen alanları göremez |
| Playwright `storage_state` | Giriş formu JavaScript ile gönderiliyor ya da belirteç betikle üretiliyor | Yüksek, gerçek tarayıcı açılır | Bellek ve süre, kurulum yükü |
| Hibrit: tarayıcıyla gir, HTTP ile devam et | Giriş karmaşık ama veri sayfaları düz HTML | Bir 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](https://requests.readthedocs.io/en/latest/user/advanced/) 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](/tr/blog/httpx-vs-requests-vs-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ı](/tr/blog/wget-proxy) 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?](/tr/blog/ip-rotation-explained) yazımızda anlattık.

Doğru kurgu iki seçenekten biridir:

- [Sticky Proxy](https://proxynet.io/tr/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](https://proxynet.io/tr/static-isp-residential-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?](/tr/blog/static-ip-for-api-access) yazımızda, sabit IP'nin genel gerekçesi ise [Web Scraping'de Engellenmeden Veri Toplama Yöntemleri](/tr/blog/web-scraping-without-getting-blocked) 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.

- **Tek iş parçacığıyla başlayın.** Günde bir rapor için paralellik kurmayın.
- **İstekler arasına bekleme koyun.** Yukarıdaki betikteki `DELAY_SECONDS` bunun en basit hâlidir.
- **`429` gördüğünüzde durun.** Yanıttaki `Retry-After` başlığı ne kadar bekleyeceğinizi söyler. Kodun tamamı [429 Too Many Requests ve Rate Limit Hatası Nedir?](/tr/blog/http-429-too-many-requests) ve [Scraping'de HTTP Hata Kodları](/tr/blog/http-status-codes-web-scraping) yazılarımızda.
- **Kendinizi tanıtın.** `User-Agent` alanına betiğin adını ve bir iletişim adresi yazmak, site yöneticisinin sizinle konuşabilmesini sağlar; alanın işlevi [User-Agent Nedir, Nasıl Öğrenilir ve Değiştirilir?](/tr/blog/what-is-user-agent) yazımızda.

## 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](https://playwright.dev/python/docs/auth) 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?](/tr/blog/playwright-proxy) yazımızda ayrıntılı anlattık; Selenium tarafındaki karşılığı için [Selenium Proxy Entegrasyonu](/tr/blog/selenium) 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?](/tr/blog/playwright-vs-selenium) 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ü](/tr/data-scraping) 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](/tr/app-testing) 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?](/tr/blog/pagination-web-scraping) 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

| Durum | Yapılacak |
|---|---|
| Sitenin resmî API'si var | Giriş otomasyonuna hiç girmeyin, API anahtarı kullanın |
| Form düz HTML, alanlar sayfada | `requests.Session` ile giriş, çerezleri dosyaya kaydedin |
| Formda `csrf_token` benzeri gizli alan | Her çalıştırmada sayfadan okuyun, koda gömmeyin |
| Form JavaScript ile gönderiliyor | Playwright ile girin, `storage_state` dosyasını saklayın |
| Giriş karmaşık, veri sayfaları düz | Hibrit: tarayıcıyla gir, çerezleri `requests`'e taşı |
| Oturum ortasında kapanıyor | Çıkış IP'sini sabitleyin: [Sticky Proxy](https://proxynet.io/tr/sticky-proxy) |
| Panel yalnız belirli IP'lere açık | [ISP Proxy](https://proxynet.io/tr/static-isp-residential-proxy) ile sabit adres |
| Hesapta iki aşamalı doğrulama var | API anahtarı ya da uygulama parolası isteyin, doğrulamayı aşmaya çalışmayın |
| Hesap sizin değil | Durun; sahibinden yazılı izin alın |

## Sıkça sorulan sorular

### Session ile cookie arasındaki fark nedir?

Ç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ı?](/tr/blog/web-scraping-legal) 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](https://proxynet.io/tr/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](/tr/proxy) bulabilirsiniz.
