## GIRDI — yalniz bu blogu doldur ```text ScrapeUnblocker API anahtarim: BURAYA_YAPISTIR Kalkis: BURAYA Tarih araligi: BURAYA Gezi suresi: BURAYA_GUN Kisi basi butce: BURAYA Dahil/haric yerler: (istege bagli, bos kalabilir) Tarama: Hizli ``` Girdin bu bloktur. Sohbette bana soru sorma, eksik alani makul varsayimla doldurup devam et. Anahtar satiri bos ya da hala `BURAYA_YAPISTIR` ise canli veri toplamaya HIC girme: urunu kur, testleri kos, agsiz ornek ciktiyi uret ve sonunda tek satirla anahtarin eksik oldugunu soyle. Kalkis sehir adi ya da havaalani kodu olabilir. Butcede para birimi yazmadiysam TL say. Baslamadan once hedefi tek satir geri oku. Asagidaki urunu bu bilgisayarda sifirdan kur. Benden terminal komutu isteme; komutlari sen calistir, dosyalari sen olustur, testleri sen kos. Depo klonlama. Bos bir `seyahat-butce-radari` klasorunde insa et. ## Sonuc Kullanici hedef sehir soylemeden "Belirledigim tarihlerde, kalkis noktamdan, kisi basi butceme uyan hangi sehirlere gidebilirim?" sorusunu cevaplayan lokal bir urun yap. Urun bir ucus listesi degil karar ekranidir. Ilk ekranda butceye uyan rotalari, kisi basi fiyati, tarihleri, aktarmayi, sureyi, havayolunu, bagaji ve kaynak baglantisini gostersin. Bagaj cevapta yoksa `bilinmiyor` yazsin. Tum ag verisi yalniz ScrapeUnblocker uzerinden gelsin. Baska ucus, otel, doviz veya arama API'si kullanma. Veri gelmezse tahmin etme ve ornek fiyat uydurma. ## Kurulum Python 3.11 veya ustunu dogrula. Proje icinde `.venv` olustur. `requirements.txt` dosyasina tam olarak `scrapeunblocker==0.1.8` yaz ve bu resmi SDK surumunu sanal ortama kur; calisirken kurulu surum 0.1.8 degilse anlasilir hata ver. `.gitignore` icine `.env`, `.venv/`, `cache/`, ham cevaplar, kosu durumu ve yetenek kaniti ekle. Anahtari yukaridaki girdi blogundan al. Ilk isin onu proje icindeki `.env` dosyasina tek seferde yazip iznini `0600` yapmak olsun, sonra hep oradan oku. Sohbette ayrica anahtar isteme. Ortamda `SCRAPEUNBLOCKER_KEY` ya da mevcut bir `.env` varsa blok yerine onu kullan; ucu de yoksa Python `getpass` ile gizli terminal girdisi al. `getpass` YALNIZ argumansiz interaktif kosuda calisir; `--help`, `--fixture-demo` ve `--plan` etkilesimsizdir, anahtari yalniz ortamdan ya da `.env`den okur ve anahtar yoksa `getpass`e girmeden anlasilir mesajla sifirdan farkli kodla ciker. Anahtar HTML, JSON, log, komut satiri argumani, test fixture'i veya ekrana hicbir zaman girmesin. ## Kullanici girdisi Girdi bastaki bloktur, sohbette tekrar sorma. Blokta olmayan alanlari makul varsayimla doldur ve varsayimlari calisma planinda tek satir halinde geri oku: en fazla aktarma 1, tek yon icin en uzun kabul edilen ucus suresi 8 saat, dahil/haric listesi bos, mod Hizli. Gidis-donus tarih penceresini "Tarih araligi" satirindan, gezi suresini "Gezi suresi" satirindan cikar; suresi yazilmadiysa pencereyi tek seferlik tam aralik say. Teknik alan adi sorma. Bu cevaplardan `plan.json` dosyasini kurulum sirasinda SEN yaz ve build kokune GERCEK bir dosya olarak birak; kullanicidan beklenen bir girdi degildir, sema ornegi de degildir. Olcut sudur: `python app.py --plan plan.json` hicbir duzenleme yapilmadan calisir. Kalkis kaydinda ve her hedefte gorunen ad, aramada kullanilacak sade sehir adi ve bir ya da daha cok IATA kodu bulunsun. Hedef listesini kendi genel bilginle olustur; API'yi hedef uretmek icin harcama. Izin listesi verdiyse yalniz o yerleri sec. Haric listesindekileri her durumda ele. ## Kanonik CLI sozlesmesi Kokte tek giris `app.py` olsun; baska giris olusturma. Arayuz tam olarak: - `python app.py --help` -> kullanim ve `exit 0`. - `python app.py --fixture-demo` -> anahtarsiz yerel demo. - `python app.py --plan PLAN_JSON` -> etkilesimsiz canli ucus kosusu. - `python app.py --plan PLAN_JSON --hotel-canary` -> acikca istenen otel kanaryasi. - Argumansiz `python app.py` -> yalniz bu durumda tek seferlik interaktif girdi ve kosu. `--fixture-demo` ile `--plan` birlikte kullanilmasin; `--hotel-canary` `--plan` istesin. Gecersiz birlesim girdi almadan `exit 2` donsun. `--help`; stdin/`input`, `getpass`, SDK/ag ve herhangi bir dosya yazimi olmadan `exit 0` donsun. SDK'yi modul seviyesinde import etme; `scrapeunblocker` importu canli cagri fonksiyonunun icinde tembel olsun. Bu kural tek bir dosyanin degil TUM modullerin kuralidir: surum okuyan bir `manifest` yardimcisinda bile `from scrapeunblocker...` satiri modul govdesinde duramaz, cunku `--fixture-demo` SDK hic kurulu degilken de calismak zorunda (olculdu: tam bu satir yuzunden dustu). ## Iki kesin mod | Mod | Destinasyon | Tarih penceresi | Basarili ucus cevabi | Ag denemesi (tekrar dahil) | |---|---:|---:|---:|---:| | Hizli (varsayilan) | 8 | 1 | 8 | 24 | | Derin | 20 | 2 | 40 | 120 | Bu tavanlar kodda zorunlu olsun; plandaki buyuk deger bunlari asamasin. Istekler sirali kossun, eszamanlilik 1. ## ScrapeUnblocker kullanimi Resmi SDK ile `Client(..., max_retries=0)` ac. Tekrar kararini uygulama versin. Ucus icin `client.skyscanner.flights(...)` kullan. SDK'da bu urun icin baska cagri YOK: metot adi, parametre ya da `rules_hint` benzeri alan uydurma, yedek yollar da ayni iki cagriyla kurulsun. Gerekli alanlar: - `origin`, `dest`, `depart_date`, `return_date`, - `adults`, `cabin`, `market`, `locale`, `currency`, `max_polls`. Cevabin seklini UYDURMA. 14 Agustos 2026 canli olcumunde donen alan yollari sunlardi; birebir bu adlari oku: kok `itineraries`, sonuc listesi `itineraries.results`, durum `itineraries.context.status`. Her sonucta `legs` (gidis `legs[0]`, donus varsa `legs[1]`), `legs[].origin.id` ve `legs[].destination.id` (IATA), `legs[].departure` / `legs[].arrival`, `legs[].durationInMinutes` (tek yon sure), `legs[].stopCount`, `legs[].carriers.marketing[].name`, `price.raw` (sayi), `price.formatted` (metin) ve rezervasyon baglantisi `pricingOptions[0].items[0].url` (bagil gelirse basina `https://www.skyscanner.net` ekle). Baska alan uydurma; fiyati bos olan sonucu fiyat sayma. Her rota ve penceredeki ilk istekte `origin` ve `dest` icin plandaki IATA kodunu kullan; her istekte tek bir IATA kodu gonder, virgulle birlestirilmis liste gonderme. Donen ilk bacagin kalkis ve varis havaalanlarini beklenen IATA listeleriyle dogrula. Yanlis sehre cozulmus bir cevap fiyat sonucu sayilmasin. Sehir yedegi kartezyen degil, kosu-geneli durum makinesi olsun. Iki sayac `0` baslasin ve asla `1`i asmasin: `origin_city_fallback_attempts`, `dest_city_fallback_attempts`. Yedegi her kullandiginda ilgili sayaci BIR ARTIR; karar da tekrar da bu sayacin uzerinden yurusun. Manifeste dusen deger kosuda gercekten olani gostersin, sifir kalan bir sayac raporu yalan yapar. - Normal istek: `origin_IATA/dest_IATA`. - Yalniz dest yanlissa ve dest sayaci `0`sa bir kez `origin_IATA/dest_sehir_adi`. - Yalniz origin yanlissa ve origin sayaci `0`sa bir kez `origin_sehir_adi/dest_IATA`. - `404`, bos veya belirsiz cevapta kullanilmamis dest yedegini, sonra origin yedegini dene. Sira SABITTIR: once hedef tarafi gevser, sonra kalkis. Ters sira hatadir. - `origin_sehir_adi/dest_sehir_adi`, ic ice aday dongusu, ayni parametre tekrari ve sayaci tukenen tarafin sonraki rota/pencerede yeniden sehir yedegi yasaktir. `2 destinasyon x 2 pencere` fixture testi SDK argumanlarinin tam sirasini kanitlasin: belirsiz hatada ilk rota `IATA/IATA`, `IATA/dest_sehir_adi`, `origin_sehir_adi/dest_IATA`; kalanlar yalniz `IATA/IATA`. Iki sayac da `<=1` kalmali. Kodda tek karar kapisi olan isimli `FALLBACK_TRIGGER_MATRIX` sabiti bulunmali. `ALLOW`: yalniz `not_found_404`, kullanilabilir `2xx_empty`, `origin_wrong`, `dest_wrong`, `route_ambiguous`. `DENY`: `400/401/402/403/422`, `429/500/502/503/504/timeout_exhausted`, `200_block_page`, `unknown_error`. Scanner fallback'e yalniz bu matrisin `ALLOW` sonucuyla girilir; genel `except`/basarisizlik yolu fallback baslatamaz. `test_fallback_trigger_matrix_at_scanner_boundary` adli tablo-testinde fake SDK'yi gercek route-scanner sinirindan cagir: her `DENY` satirinda cagri logu tam bir `IATA/IATA`, iki sayac `0`, sehir adi sorgusu yok; `not_found_404` satiri yukaridaki izinli sirayi kanitlar. Yalniz gateway/HTTP siniflandiricisini test etmek yeterli degildir. Uzun taramadan once baslangic kanaryasi yap: ilk pencereyle en fazla ilk uc sehri sirayla dene. Bir sehir sonuc verirse kanarya gecmis sayilir; o sonuc taramanin ilk satiridir. Anahtar ya da parametre hatasi gelirse hemen dur, bunlar rotanin degil istegin kusurudur. Rota kusurunda sonraki sehre gec: `404`, `403`, gecici hata, bos cevap, sinirlara uymayan secenek. Denenen uc sehrin ucu de sonuc vermezse taramayi baslatma: koyu bir `rapor_son.html` uret, `Tarama baslatilmadi` yaz, denenen her sehrin gercek sebebini goster ve hic fiyat uydurma. Kanaryada olumsuz cevap alan sehir-pencere hucresini ana taramada tekrar sorma. Cevabi zaten elinde, ikinci istek yalnizca butceden yer. ## Hata ve tekrar sozlesmesi SDK bu durumlari kod DONDURMEZ, ISTISNA FIRLATIR. Hepsi `ScrapeUnblockerError` altindadir; `APIError` soyundakiler `status_code` tasir (olculdu, surum 0.1.8: `BlockedError`, `NotFoundError`, `RateLimitError`, `AuthenticationError`, `ServerError` ve kardesleri). Asagidaki matrisi bu istisnalari yakalayip `status_code`a bakarak uygula. Hicbir SDK istisnasi kullaniciya cikmasin: kosu tek satirlik anlasilir bir uyariyla ve raporla bitsin, Traceback ile degil. Olculdu, uc ayri kurulum tam burada coktu: biri `BlockedError`i hic yakalamadi, biri SDK'da olmayan bir metot cagirip `AttributeError` aldi, biri hata yolunda `set` serilestirip `TypeError` verdi. - `400`, `401`, `402`, `422`: anahtar, plan veya parametre hatasidir. Tekrar etme, hemen dur. - `403`: kaynak engelidir. Ayni istegi tekrar etme. - `404`: uc veya sonuc bulunamadi. Ayni istegi tekrar etme; olcum `404`un o rotanin yoklugu oldugunu gosterdi, ucun arizasi degil. Bu yuzden `404` tek sehri duserir, taramayi durdurmaz. Yalniz yukaridaki kosu-geneli, taraf-basina tek sehir adi durum makinesi devreye girebilir. - `429`, ag zaman asimi, `500`, `502`, `503`, `504`: en fazla uc deneme yap. Birinci hatadan sonra 1 saniye, ikinciden sonra 2 saniye bekle. - HTTP `200` govdesinde captcha, access denied veya insan dogrulamasi varsa basari sayma. - Bilinmeyen hata icin veri uydurma. Satiri sebebiyle birlikte eksik goster. Yalniz kullanilabilir `2xx` cevaplari basarili ve faturalandirilabilir say. Toplam deneme, basarili 2xx ve cache isabetini ayri tut. Kalan krediyi tahmin etme. ## Fiyat guvenligi `adults` grup toplami dogrulanmamistir: fiyat daima `kisi basi`; iki+ kiside carpma yapma ve `Grup toplami dogrulanmadi` yaz. Eksik bagaji dahil sayma. Butceyi yalniz istenen para biriminde kiyasla; farkli parayi cevirme veya butce-ici sayma, uyari ver. Fiyat degisebilir uyarisini rapora ekle. ## Cache, gecmis ve ilerleme Ayni uc, rota ve parametrelerin basarili cevabini anahtar icermeyen SHA-256 anahtariyla gzip cache'e yaz. Cache omru 6 saat olsun. Cache cevabi ag istegi veya basarili 2xx sayacini artirmasin. JSON'a yazilan ya da cache anahtari uretilen her sozluk JSON'a cevrilebilir kalsin. Istek parametrelerine `set` koyan kurulum, hata yolunda `json.dumps` cagrilinca `TypeError: Object of type set is not JSON serializable` ile traceback dokuyor (olculdu): 403 alan kosu sade uyari yerine cokme gosterdi. Kumeyi sozluge koymadan once sirali listeye cevir; ayni kural manifest, gecmis ve rapor verisi icin de gecerli. Her arama tamamlandiginda `rapor_son.html` ve `run_manifest.json` dosyalarini hemen yenile; kullanici uzun tarama surerken ilk firsatlari gorsun. Manifest en az sunlari tasisin: prompt (`prompt_version=2.0.0`) ve SDK surumu; kosu kimligi, UTC baslangic ve bitis zamani; mod ve sert butceler; durum (starting, canary, calisiyor, tamamlandi, kismi, blocked); tamamlanan ve planlanan arama; toplam deneme, basarili 2xx, cache isabeti; `origin_city_fallback_attempts` ve `dest_city_fallback_attempts`; veri kaynaklari, uyari listesi ve rapor yolu. Fiyat gecmisini ayri JSON dosyasinda tut. Ayni kalkis, tarihler, gezi suresi, para, aktarma ve sure sinirlariyla gelen yeni kosuyu eski fiyatla kiyasla; yeni, dustu, artti ve ayni durumlarini goster. Yuzde 1'in altindaki degisimi buyuk alarm gibi one cikarma. Son yedi kosunun zaman damgasini sakla. ## Otel kapisi Otel varsayilan kapali olsun. Yalniz acik `--hotel-canary` secenegi `client.skyscanner.hotels(...)` cagirsin. Gercek ad + sayisal toplam + para birimi varsa kosu kimligini kaydet; iki farkli canli kosu kaniti olmadan otel vaat etme veya arama. Kapi acilsa da acik kullanici istegi ve en fazla uc butce-ici rota gerekir. Farkli para birimlerini toplama; ucus grup toplami dogrulanmadigi icin birlesik seyahat toplami gosterme. ## HTML raporu Tek dosyalik, mobil uyumlu ve tamamen offline bir HTML uret. Tema koyu olsun: zemin `#0b1120`, panel `#1e293b`, kenar `#334155`, baslik `#f1f5f9`, govde `#e2e8f0`, soluk metin `#94a3b8`. Harici font, stil, script, gorsel veya analitik istegi olmasin. CSP ile `default-src 'none'` uygula. Rezervasyon baglantilari yalniz tiklamayla acilan normal linkler olabilir. Ilk ekranda mod, tarama durumu, butceye uyan rota sayisi, en ucuz rota ve fiyat degisimi yer alsin. Kartlarda kaynak tarihi ve kisi basi etiketi gorunsun. Basarisiz yerler sebebiyle ayri tabloda dursun. Raporu ureten TEK bir renderer yolu vardir. Olcum sayisini iyilestirmek icin ikinci bir cizim yolu, etiket varyanti ya da noktalama degisikligi EKLENMEZ (denendi: `
` -> `