20.09.2026

scraper ayağına backend yazmak

siteye ufak bi şey eklemek istedim.

logonun yanında küçük bir konuşma balonu olacak, içinde de instagram biyografim olacak. kullanıcı açısından bakınca hiçbir numarası olmayan bi özellik. iki satır text işte.

ilk birkaç dakika gayet normal. sonra instagram’ın html’ine biraz daha dikkatli bakınca insan biraz hayatı sorguluyor. bi yerde component, başka yerde script, onun altında json, arada meta tag. aradığın bilgi de bunların arasında bi yerlerde takılıyor.

bi süre instagram’ın react componentiyle küfürleşiyorum.

component sakin.

ben değilim.

neyse. og:title, description, og:image gibi meta veriler zaten profil hakkında yeterince şey söylüyor. isim, biyografi, takipçi takip edilen cart curt hepsi geliyor.

güzel.

şimdi bunu sitemden çağırayım dedim.

cors çıktı.

web geliştirirken bazı problemler vardır, görünce şaşırmazsın. sadece tekrar karşılaşmış olmanın verdiği o hafif rahatsızlık gelir. cors da öyle.

browser’a “git şunu şuradan al” diyorsun.

browser sana bakıyor.

hayır.

neden?

bilmiyorum.

tamam kardeşim.

işi gücü bırakıp küçük bi flask api yazdım. username geliyor, servis instagram profilini çekiyor, html parse ediyor, ayıklayıp json döndürüyor.

ilk halini render üzerinde çalıştırdım.

buraya kadar her şey okey.

sonra render’ın ücretsiz servisleri fazla barzo çıktı.

site açılıyor, request gidiyor, cevap gelmesini bekliyorsun. gelmiyor.

ilginç.

insan kendi sitesinde timeout yiyince hoş olmuyo. önce koda sonra loga bakıyo. ee sorun ne amk?

ani bi kararla flask’ı kendi bilgisayarımda 127.0.0.1:5000 üzerinde ayağa kaldırdım. localde zaten güzel çalışıyor. problem şu ki 127.0.0.1‘in dış dünyayla fazla bi tanışıklığı yok.

tailscale ile dışarı açtım, funnel üzerinden https endpoint aldım.

artık benim bilgisayarda çalışan flask servisine internetten ulaşılabiliyor.

buraya kadar hâlâ işin bi mantığı var.

sonra endpointimin kamuya açık olduğunu hatırladım.

insan bazı şeyleri yaptıktan sonra yaptığı şeye ikinci kere bakıyor.

“bunu kim çağırabiliyor?”

güzel soru.

flask tarafına internal secret ekledim. gelen isteğin doğru origin’den gelmesini bekliyor, bi de X-Internal-Secret header’ı doğru olacak. secret kodun içinde değil, environment variable’da.

doğrudan tailscale adresine gidip secret olmadan deniyorum.

403.

olması gereken bu.

sonra origin tarafını kurcaladım. çünkü Origin header’ını curl ile elle yazabiliyorsun.

yani isteyen:

benim adımla selam verebiliyor.

internet biraz tuhaf bi yer.

dolayısıyla origin kontrolü tek başına güvenlik değil. asıl önemli olan internal secret’ın tarayıcıya hiç düşmemesi.

secret’ı frontend’e koyarsan artık secret değildir. bunu ispatlamak için ayrıca uğraşmaya gerek yok.

siteyi doğrudan tailscale’e bağlamadım. araya cloudflare worker koydum.

böylece tarayıcı yalnızca worker’ı görüyor. worker arka tarafta tailscale’e gidiyor, gerekli secret’ı ekliyor, flask isteği alıyor, instagram’a çıkıyor.

şimdilik güzel.

sonra worker’ın public olduğunu hatırlıyorsun.

tabii public olacak.

yoksa site nasıl çağıracak.

dolayısıyla worker’a gelen isteğin ayrıca doğrulanması gerekiyor. turnstile’ı buraya koydum. site token alıyor, worker token’ı cloudflare’a doğrulatıyor. geçerliyse request devam ediyor.

ondan sonra yine aynı zincir:

worker.

tailscale.

flask.

instagram.

bunca katmanın sonunda da dönüp baktığında elinde bi json var.

{
 "username": "kadirsakgz",
 "full_name": "Kadir",
 "bio": "average js enjoyer",
 "followers": "109",
 "following": "186",
 "posts": "0",
 "profile_pic": "https://scontent.cdninstagram.com/v/t51.82787-19/791129..."
}

olay bu.

bi ara yapılan işe uzaktan bakınca hafif komik geliyor.

çünkü başlangıçta basit bi scraper istiyordum.

sonra local flask servisi kurmuşum.

üstüne tailscale funnel eklemişim.

önüne cloudflare worker.

worker’ın önünde turnstile.

içeride secret.

cors kontrolü.

kullanıcının gördüğü şey ise logonun yanında duran birkaç satırlık bi konuşma balonu.

ama artık işin nerede olduğunu biliyoruz.

gelen istek nereden geçiyor belli.

hangi durumda reddediliyor belli.

backend’e doğrudan ulaşılabiliyor mu belli.

secret client’ta mı belli.

test ederken de zaten biraz düşman gibi davranmak gerekiyor.

kendi worker’ına doğru düzgün istek at, sonra bilerek yanlış token ver, tailscale’e doğrudan git, secret olmadan dene.

403.

bi daha dene.

403.

güzel.

bazen sistemin beklediğin cevabı vermesi, doğru şeyi yaptığını anlamanın en güzel yolu.

scraper tarafında da fazla gösteriş yapmaya gerek yok. önce curl-cffi ile chrome benzeri bi istek deneniyor. olmazsa httpx ile farklı header’larla tekrar deneniyor. html geliyor, meta tag’ler parse ediliyor, gerekli alanlar ayrılıyor, json dönüyor.

bugün çalışır.

yarın instagram html yapısını değiştirir.

o gün de düzeltiriz.

en azından kime söveceğimiz belli.

zaten yıllar içinde en işe yarayan şeyin her problemi önceden bilmek olmadığını fark ediyorsun.

en boktan response geliyor.

500.

501.

ilk bakışta anlamsız.

ama hatırlıyorsun.

bi yerden bi ihtimal çıkıyor.

şunu dene.

buraya bak.

header’ı değiştir.

isteği ayır.

log’u tekrar oku.

olmadı.

başka bi şey.

sonra bi bakmışsın çalışıyor.

insanın zamanla kazandığı şey bütün cevapları ezberlemek değil zaten. hangi duvara bi daha kafa atmanın anlamı olmadığını anlamak.

ironiktir ki bunu da kafayı kırınca öğreniyorsun.

karşılaştığın hataların bazıları çok zaman yiyor.

bazıları yemiyor.

sonra yıllar geçiyor, başka bi sistemde benzer bi hata çıkıyor ve kafanın bi yerinden eski bi anı sesleniyor.

“bunu daha önce de yaşamıştık.”

gerisi bi şekilde geliyor.

bütün bu sistem için daha basit yollar var mıydı?

elbette vardı.

bunu inkâr edecek değilim.

ama şu an çalışıyor, public tarafta gereksiz bilgi sızdırmıyor, backend tarafı kendi kapısını kontrol ediyor ve site ihtiyacı olan veriyi alıyor.

yeter.

bi senior gelip “kardeşim sen bunun için niye bu kadar şey kurdun?” diyebilir.

haklı olabilir.

ben de kafamı sallarım.

bi şey yapıyoruz.

bi şey bozuluyor.

düzeltiyoruz.

başka bi şey bozuluyor.

onu da düzeltiyoruz.

arada çok saçma şeylerle uğraşıyoruz.

kaybettiklerimizi görüyoruz.

notumuzu alıyoruz.

çok da şey etmiyoruz.

hayat zaten zor; insanın her hatasına yas tutacak vakti yok.

başka bi yazıda görüşmek üzere.

iyi şanslar.