Takip et

Puppeteer, Node.js, Docker ve Kubernetes ile Eş Zamanlı Bir Web Scraper Nasıl İnşa Edilir?

Puppeteer, Node.js, Docker ve Kubernetes ile Eş Zamanlı Bir Web Scraper Nasıl İnşa Edilir? Günümüzün veri odaklı dünyasında, web siteler

Puppeteer, Node.js, Docker ve Kubernetes ile Eş Zamanlı Bir Web Scraper Nasıl İnşa Edilir?

Günümüzün veri odaklı dünyasında, web sitelerinden bilgi toplamak iş zekası, pazar araştırması ve rekabet analizi gibi birçok alanda kritik bir rol oynamaktadır. Web scraping, bu veriye programatik olarak erişmenin temel yöntemidir. Ancak, büyük ölçekli ve dinamik web sitelerinden veri çekerken tekil bir scraper yetersiz kalabilir. İşte bu noktada eş zamanlılık ve ölçeklenebilirlik devreye girer.

Bu makalede, modern web scraping ihtiyaçlarını karşılamak üzere tasarlanmış, güçlü ve ölçeklenebilir bir mimariyi adım adım inceleyeceğiz. Node.js’in asenkron yapısından yararlanarak Puppeteer ile tarayıcı otomasyonu gerçekleştirecek, Docker ile uygulamamızı konteynerize edecek ve son olarak Kubernetes’in orkestrasyon yetenekleriyle scraper’ımızı yüksek oranda eş zamanlı ve hataya dayanıklı hale getireceğiz. Bu teknoloji yığını, sadece veri toplamakla kalmayacak, aynı zamanda operasyonel verimlilik ve esneklik de sağlayacaktır.

Web Scraping ve Eş Zamanlılık Neden Önemli?

Web scraping, web sitelerindeki yapılandırılmamış veriyi yapılandırılmış formata dönüştürme sürecidir. Bu süreç, e-ticaret sitelerinden ürün fiyatları çekmekten, haber sitelerinden makale başlıkları toplamaya kadar geniş bir yelpazeyi kapsar. Ancak, modern web siteleri genellikle JavaScript ile dinamik olarak yüklenen içeriklere sahiptir ve geleneksel HTTP istek tabanlı scraper’lar bu içeriği göremez. Puppeteer gibi tarayıcı otomasyon araçları bu sorunu çözerek gerçek bir kullanıcı deneyimini simüle etmemizi sağlar.

Eş zamanlılık ise, birden fazla scraping görevini aynı anda veya paralel olarak yürütme yeteneğidir. Tekil bir scraper, her seferinde bir URL’yi işlerken, binlerce veya milyonlarca URL’yi taramak günler hatta haftalar sürebilir. Eş zamanlı bir yaklaşım, bu süreyi önemli ölçüde kısaltır, kaynak kullanımını optimize eder ve scraping sürecinin genel verimliliğini artırır. Özellikle hedef sitenin talepleri, ağ gecikmeleri ve işlem süreleri göz önüne alındığında, eş zamanlılık olmadan büyük veri setlerini toplamak pratik değildir. Ayrıca, bir görevin başarısız olması durumunda diğer görevlerin etkilenmemesi ve sistemin genel dayanıklılığı açısından da eş zamanlılık kritik bir faktördür.

Kullanılacak Teknolojilere Genel Bakış

Bu projenin temelini oluşturan dört ana teknoloji, her biri kendi alanında güçlü yetenekler sunar ve bir araya geldiklerinde sinerjik bir çözüm oluştururlar.

Node.js

Node.js, JavaScript’i tarayıcı dışında çalıştırmamızı sağlayan açık kaynaklı, sunucu tarafı bir çalışma zamanı ortamıdır. Olay döngüsü (event loop) tabanlı, engellemeyen (non-blocking) G/Ç modeli sayesinde, eş zamanlı işlemler için mükemmel bir seçimdir. Web scraper’lar genellikle ağ istekleri ve dosya işlemleri gibi G/Ç yoğun görevler içerir. Node.js’in bu yapısı, aynı anda binlerce bağlantıyı yönetebilirken performans düşüşü yaşamadan yüksek verimli bir scraping motoru oluşturmamıza olanak tanır. Geniş npm ekosistemi de geliştirme sürecini hızlandırır ve birçok kullanışlı kütüphaneye erişim sağlar.

Puppeteer

Puppeteer, Google tarafından geliştirilen bir Node.js kütüphanesidir ve Chrome veya Chromium gibi headless (başsız) veya tam sürüm tarayıcıları bir API aracılığıyla kontrol etmemizi sağlar. Bu, JavaScript ile oluşturulan dinamik içeriği işleyebilen, formları doldurabilen, düğmelere tıklayabilen, ekran görüntüleri alabilen ve hatta PDF’ler oluşturabilen bir scraper inşa edebileceğimiz anlamına gelir. Geleneksel HTTP kütüphanelerinin aksine, Puppeteer gerçek bir tarayıcı ortamını taklit ederek, modern web sitelerinin karmaşık yapılarıyla etkileşime geçme yeteneği sunar. Bu, özellikle AJAX çağrıları, tek sayfa uygulamaları (SPA’lar) ve karmaşık DOM manipülasyonları içeren siteler için vazgeçilmezdir.

Docker

Docker, uygulamaları ve bağımlılıklarını izole edilmiş, taşınabilir birimler olan konteynerler içinde paketlemeyi sağlayan bir platformdur. Bir uygulamanın “benim makinemde çalışıyor” sorununu ortadan kaldırır. Scraper’ımız için Docker kullanmak, Node.js ve Puppeteer’ın tüm bağımlılıklarının (örneğin, Chromium’un ihtiyaç duyduğu sistem kütüphaneleri) tek bir imajda toplanmasını sağlar. Bu, geliştirme ortamından üretim ortamına kadar tutarlı bir çalışma ortamı sunar, dağıtımı basitleştirir ve “bağımlılık cehennemi” sorunlarını en aza indirir. Her bir scraper örneği ayrı bir konteynerde çalışabilir, bu da izolasyon ve kaynak yönetimi açısından büyük avantajlar sunar.

Kubernetes

Kubernetes (genellikle K8s olarak anılır), konteynerize edilmiş iş yüklerini ve servisleri otomatik olarak dağıtmak, ölçeklendirmek ve yönetmek için kullanılan açık kaynaklı bir sistemdir. Docker konteynerlerini orkestra etmek için endüstri standardı haline gelmiştir. Web scraper’ımız için Kubernetes kullanmak, binlerce scraping görevini eş zamanlı olarak yürütmek için dinamik olarak Pod’lar oluşturmamızı, yükü dengelememizi, başarısız olan Pod’ları otomatik olarak yeniden başlatmamızı ve genel sistemin hataya dayanıklı olmasını sağlamamızı mümkün kılar. Otomatik ölçeklendirme (Horizontal Pod Autoscaler – HPA) ile talebe göre kaynakları dinamik olarak ayarlayabiliriz, böylece maliyetleri optimize ederken performans sürekliliğini garanti ederiz.

Temel Web Scraper Uygulaması

Bu bölümde, Puppeteer ve Node.js kullanarak basit bir web scraper’ın nasıl oluşturulacağını adım adım inceleyeceğiz. Amacımız, belirli bir web sayfasından veri çekmek ve bu veriyi yapılandırılmış bir formatta kaydetmektir.

Proje Yapısı ve Bağımlılıklar

Öncelikle yeni bir Node.js projesi oluşturalım ve gerekli bağımlılıkları kuralım:

mkdir concurrent-scraper
cd concurrent-scraper
npm init -y
npm install puppeteer p-queue

p-queue kütüphanesi, eş zamanlı görevlerin sayısını kontrol etmemize yardımcı olacak basit bir kuyruk yönetimi sağlar. Proje yapımız başlangıçta şöyle olabilir:

concurrent-scraper/
├── node_modules/
├── package.json
├── package-lock.json
└── scraper.js

Puppeteer ile İlk Adımlar

Şimdi scraper.js dosyamızı oluşturalım ve Puppeteer ile basit bir sayfa ziyaretini ve veri çekimini gerçekleştirelim. Örnek olarak, bir blog sayfasındaki başlıkları çekmeyi hedefleyebiliriz:

const puppeteer = require('puppeteer');

async function scrapePage(url) {
    let browser;
    try {
        browser = await puppeteer.launch({ headless: true }); // headless: true varsayılan olarak gelir
        const page = await browser.newPage();
        await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 }); // Sayfanın yüklenmesini bekle

        // Sayfadaki tüm başlıkları çek
        const titles = await page.evaluate(() => {
            const elements = Array.from(document.querySelectorAll('h1, h2, h3'));
            return elements.map(el => el.innerText.trim());
        });

        console.log(URL: ${url});
        console.log('Başlıklar:', titles);
        return { url, titles };

    } catch (error) {
        console.error(URL ${url} işlenirken hata oluştu:, error);
        return { url, error: error.message };
    } finally {
        if (browser) {
            await browser.close();
        }
    }
}

// Örnek kullanım
// scrapePage('https://example.com').then(data => console.log('Çekilen veri:', data));

Bu kod parçası, Puppeteer’ı başlatır, belirtilen URL’ye gider, sayfadaki H1, H2, H3 etiketlerini bulur ve iç metinlerini çeker. Son olarak, tarayıcıyı kapatır. waitUntil ve timeout seçenekleri, sayfa yükleme davranışını kontrol etmek için önemlidir.

Veri Çekme ve Kaydetme

Önceki örnekte sadece başlıkları çektik. Gerçek bir senaryoda, daha fazla veri noktası (fiyat, açıklama, resim URL’leri vb.) çekmek isteyebiliriz. page.evaluate() fonksiyonu içinde, tarayıcı bağlamında çalışan JavaScript kodunu yazarak DOM üzerinde her türlü manipülasyonu yapabiliriz. Örneğin, bir ürün sayfasından fiyat ve açıklama çekmek için:

        const productData = await page.evaluate(() => {
            const title = document.querySelector('h1.product-title')?.innerText.trim();
            const price = document.querySelector('.product-price')?.innerText.trim();
            const description = document.querySelector('.product-description')?.innerText.trim();
            return { title, price, description };
        });
        console.log('Ürün Verileri:', productData);
        return { url, productData };

Çekilen veriyi daha sonra bir dosyaya (JSON, CSV) veya bir veritabanına kaydedebiliriz. Basit bir JSON dosyasına kaydetme örneği:

const fs = require('fs').promises; // Node.js dosya sistemi modülü

// ... (scrapePage fonksiyonu) ...

async function main() {
    const urlsToScrape = [
        'https://example.com/product/1',
        'https://example.com/product/2',
        // ...
    ];
    const allScrapedData = [];
    for (const url of urlsToScrape) {
        const data = await scrapePage(url);
        allScrapedData.push(data);
    }
    await fs.writeFile('scraped_data.json', JSON.stringify(allScrapedData, null, 2));
    console.log('Tüm veriler scraped_data.json dosyasına kaydedildi.');
}

// main(); // Eş zamanlılık ekledikten sonra çağıracağız.

Hata Yönetimi ve Robustluk

Web scraping, ağ sorunları, sayfa yapısı değişiklikleri, anti-bot mekanizmaları ve sunucu hataları gibi birçok sorunla karşılaşabilir. Scraper’ımızın robust olması için hata yönetimi kritik öneme sahiptir:

  • try-catch blokları: Puppeteer işlemleri sırasında meydana gelebilecek hataları yakalamak için kullanılır.
  • Zaman aşımları (timeout): page.goto() veya page.waitForSelector() gibi işlemlerde belirli bir süre içinde yanıt alınamazsa hata fırlatır.
  • Yeniden deneme mekanizmaları: Geçici ağ hataları veya sunucu yoğunluğu durumunda işlemi birkaç kez yeniden denemek faydalı olabilir (örneğin, axios-retry gibi kütüphanelerle veya özel bir retry fonksiyonu ile).
  • User-Agent rotasyonu: Bazı siteler belirli User-Agent’lara karşı hassas olabilir. Farklı User-Agent’lar kullanmak bot tespitini atlatmaya yardımcı olabilir.
  • Proxy kullanımı: IP adresinizin engellenmesini önlemek için proxy sunucuları üzerinden istek göndermek önemlidir.
  • Headless modda sorun giderme: headless: false ile tarayıcıyı görsel olarak açarak hataları daha kolay tespit edebiliriz.

Yukarıdaki scrapePage fonksiyonu, temel bir try-catch bloğu içerir. Daha gelişmiş senaryolarda, belirli hata kodlarına göre farklı yeniden deneme stratejileri uygulamak veya başarısız olan URL’leri ayrı bir kuyruğa atmak düşünülebilir.

Eş Zamanlılık ve Performans Optimizasyonu

Tek bir URL’yi çekmek basit olsa da, binlerce URL’yi verimli bir şekilde çekmek için eş zamanlılık şarttır. Node.js, asenkron yapısıyla bu konuda doğal bir avantaj sunar.

Node.js’te Eş Zamanlılık

Node.js’in olay döngüsü, G/Ç işlemlerinin engellemeden çalışmasını sağlar. Bu, birden fazla web sayfasını aynı anda ziyaret etmeye çalıştığımızda CPU kaynaklarının boşta kalmamasını ve ağ isteklerinin paralel olarak yürütülmesini sağlar. Ancak, çok fazla eş zamanlı Puppeteer tarayıcı örneği açmak sistem kaynaklarını (RAM, CPU) hızla tüketebilir ve hedef sunucu tarafından engellenmenize neden olabilir. Bu nedenle, eş zamanlılık seviyesini sınırlamak önemlidir.

Daha önce kurduğumuz p-queue kütüphanesi, eş zamanlı çalışan görev sayısını kontrol etmek için mükemmel bir araçtır. İşte p-queue kullanarak eş zamanlı scraping örneği:

const PQueue = require('p-queue');
// ... (scrapePage fonksiyonu yukarıdaki gibi) ...

async function mainConcurrent() {
    const urlsToScrape = [
        'https://www.example.com/page1',
        'https://www.example.com/page2',
        'https://www.example.com/page3',
        'https://www.example.com/page4',
        'https://www.example.com/page5',
        // ... binlerce URL
    ];

    const concurrencyLimit = 5; // Aynı anda çalışacak maksimum tarayıcı sayısı
    const queue = new PQueue({ concurrency: concurrencyLimit });
    const allScrapedData = [];

    const promises = urlsToScrape.map(url =>
        queue.add(() => scrapePage(url).then(data => allScrapedData.push(data)))
    );

    await Promise.all(promises); // Tüm scraping görevlerinin bitmesini bekle

    await fs.writeFile('scraped_data_concurrent.json', JSON.stringify(allScrapedData, null, 2));
    console.log(Tüm veriler ${concurrencyLimit} eş zamanlı görevle scraped_data_concurrent.json dosyasına kaydedildi.);
}

mainConcurrent();

Bu yaklaşım, Puppeteer’ın tarayıcı başlatma ve kapatma maliyetini her URL için tekrar tekrar üstlenir. Daha optimize bir yaklaşım, tek bir tarayıcı örneği başlatıp birden fazla sekme (page) kullanmaktır. Ancak bu, eş zamanlılık yönetimini daha karmaşık hale getirebilir ve bazı sitelerde oturum yönetimi sorunlarına yol açabilir. Genellikle, her görev için yeni bir tarayıcı başlatmak daha izole ve hataya dayanıklı bir yaklaşımdır, ancak kaynak tüketimi daha yüksek olabilir.

İş Kuyrukları ve Mesajlaşma

Uygulamamızın tek bir Node.js sürecinin ötesine geçip birden fazla sunucuda veya Kubernetes Pod’unda çalışmasını istediğimizde, basit p-queue yeterli olmaz. Bu durumda, dağıtık bir iş kuyruğu sistemine ihtiyacımız vardır. Redis tabanlı BullMQ gibi kütüphaneler, scraping görevlerini bir kuyruğa atmak ve birden fazla işçi (worker) tarafından bu görevlerin işlenmesini sağlamak için idealdir.

Mimari şöyle değişir:

  1. Bir “üretici” (producer) uygulaması, çekilecek URL’leri bir Redis kuyruğuna ekler.
  2. Birden fazla “tüketici” (consumer/worker) uygulaması, kuyruktan görevleri alır ve Puppeteer ile scraping işlemini gerçekleştirir.
  3. İşlenen veriler doğrudan bir veritabanına (MongoDB, PostgreSQL) veya bir depolama hizmetine (AWS S3) kaydedilir.

Bu model, iş yükünü yatay olarak ölçeklendirmemize olanak tanır. Yeni bir işçi eklemek veya mevcut işçileri kaldırmak kolaydır. Kubernetes ortamında bu, Pod’ları ölçeklendirmek anlamına gelir.

Docker ile Konteynerleştirme

Scraper’ımızı farklı ortamlarda (geliştirme, test, üretim) tutarlı bir şekilde çalıştırmak için Docker kullanacağız. Puppeteer’ın Chromium bağımlılığı nedeniyle Dockerfile biraz daha karmaşık olabilir.

Dockerfile Oluşturma

Projemizin kök dizininde bir Dockerfile oluşturalım:

# Node.js için temel imaj
FROM node:18-slim

Uygulama dizini oluştur

WORKDIR /app

Gerekli sistem bağımlılıklarını yükle

Puppeteer'ın Chromium'u çalıştırması için gerekli paketler

RUN apt-get update && apt-get install -y \ chromium \ # veya chromium-browser \ # veya google-chrome-stable \ # Eğer chromium'u doğrudan kullanmak isterseniz. # Puppeteer kendi Chromium'unu indirir ancak bazı bağımlılıkları sistemde ister. # Aşağıdaki liste genel olarak yeterlidir: gconf-service \ libasound2 \ libatk1.0-0 \ libcairo2 \ libcups2 \ libfontconfig1 \ libgdk-pixbuf2.0-0 \ libgtk-3-0 \ libnspr4 \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libnss3 \ libxss1 \ libxtst6 \ xauth \ fonts-liberation \ libappindicator1 \ libnss3 \ lsb-release \ xdg-utils \ wget \ --no-install-recommends && rm -rf /var/lib/apt/lists/*

Puppeteer'ın varsayılan Chromium'u yerine sistemde kurulu olanı kullanmasını sağlayalım (isteğe bağlı)

ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium

Node.js bağımlılıklarını kopyala ve yükle

COPY package*.json ./ RUN npm install

Uygulama kodunu kopyala

COPY . .

Scraper'ı çalıştırmak için komut

CMD ["node", "scraper.js"]

Not: chromium yerine google-chrome-stable veya microsoft-edge-stable gibi daha güncel tarayıcılar da kullanılabilir, ancak bu, Docker imajının boyutunu artırabilir. PUPPETEER_EXECUTABLE_PATH değişkeni, Puppeteer’ın kendi indirdiği Chromium yerine sistemde kurulu olanı kullanmasını sağlar, bu da imaj boyutunu azaltabilir ve güncellemeyi kolaylaştırabilir. Ancak, Puppeteer’ın kendi indirdiği Chromium sürümüyle uyumluluk sorunları yaşanabilir. En güvenli yol, Puppeteer’ın kendi Chromium’unu indirmesine izin vermektir, ancak bu durumda node:18-slim yerine node:18 gibi daha büyük bir imaj kullanmak veya gerekli bağımlılıkları manuel olarak eklemek gerekebilir. Yukarıdaki apt-get install komutu, Puppeteer’ın kendi Chromium’unu çalıştırması için gerekli olan genel bağımlılıkları içerir.

Docker Image Oluşturma ve Çalıştırma

Dockerfile’ı oluşturduktan sonra, Docker imajımızı inşa edebiliriz:

docker build -t my-scraper:1.0 .

İmaj oluşturulduktan sonra, yerel makinemizde çalıştırabiliriz:

docker run --rm my-scraper:1.0

--rm bayrağı, konteyner durduğunda otomatik olarak kaldırılmasını sağlar. Eğer Puppeteer headless: false ile çalıştırılıyorsa, X sunucusu gibi ek yapılandırmalar gerekebilir, ancak üretim ortamında genellikle headless mod tercih edilir.

Puppeteer ve Headless Chrome Sorunları

Docker ortamında Puppeteer ile çalışırken bazı yaygın sorunlar ve çözümleri:

  • Eksik Bağımlılıklar: Dockerfile‘da listelenen apt-get install komutları, Chromium’un çalışması için gerekli temel kütüphaneleri sağlar. Bu liste, kullanılan temel imaj ve Chromium sürümüne göre değişiklik gösterebilir.
  • Sandbox Modu: Linux konteynerlerinde güvenlik nedeniyle Chromium’un sandbox modu bazen sorun çıkarabilir. Bu durumda Puppeteer’ı args: ['--no-sandbox', '--disable-setuid-sandbox'] seçenekleriyle başlatmak gerekebilir:
    await puppeteer.launch({
                headless: true,
                args: ['--no-sandbox', '--disable-setuid-sandbox']
            });

    Ancak, --no-sandbox güvenlik riski taşıdığı için dikkatli kullanılmalıdır.

  • Paylaşılan Bellek (/dev/shm): Chromium, bazı durumlarda /dev/shm (paylaşılan bellek) kullanır ve Docker’ın varsayılan boyutu (64MB) yetersiz kalabilir. Bu durumda Docker’ı --shm-size=1gb gibi bir değerle çalıştırmak gerekir:
    docker run --rm --shm-size=1gb my-scraper:1.0

    Kubernetes’te bu ayar Pod tanımında yapılır.

Kubernetes ile Ölçeklendirme ve Orkestrasyon

Scraper’ımızı Docker konteynerlerine dönüştürdükten sonra, bu konteynerleri büyük ölçekte yönetmek ve otomatikleştirmek için Kubernetes’i kullanacağız. Bu, scraping operasyonlarımızı daha dayanıklı, ölçeklenebilir ve yönetilebilir hale getirecektir.

Kubernetes Temelleri

Kubernetes’in temel bileşenleri:

  • Pod: Kubernetes’teki en küçük dağıtılabilir birimdir. Bir veya daha fazla konteyner içerir (bizim durumumuzda bir Node.js/Puppeteer konteyneri). Pod’lar geçicidir ve yeniden başlatıldığında IP adresleri değişebilir.
  • Deployment: Pod’ların durumunu yöneten bir Kubernetes kaynağıdır. Belirli sayıda Pod’un çalıştığından emin olur, güncelleme ve geri alma işlemlerini yönetir.
  • Service: Bir grup Pod’a kararlı bir ağ erişimi sağlar. Pod’lar yeniden başlatılsa bile Service’in IP adresi değişmez, bu da diğer servislerin Pod’lara erişmesini kolaylaştırır.
  • Job/CronJob: Tek seferlik veya zamanlanmış görevler için kullanılır. Bizim eş zamanlı scraper’ımız için genellikle Deployment daha uygundur, çünkü sürekli çalışan bir worker havuzu oluşturmak isteriz. Ancak, belirli bir URL listesini bir kez taramak için Job kullanılabilir.

Deployment ve Pod Tanımları

Scraper uygulamamızı Kubernetes’e dağıtmak için bir Deployment tanımı oluşturacağız. Bu, scraping görevlerini yerine getirecek worker Pod’larını yönetecektir. scraper-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: scraper-worker
  labels:
    app: scraper
spec:
  replicas: 3 # Başlangıçta 3 worker Pod çalıştır
  selector:
    matchLabels:
      app: scraper
  template:
    metadata:
      labels:
        app: scraper
    spec:
      containers:
      - name: scraper-container
        image: my-scraper:1.0 # Docker imajımızın adı
        imagePullPolicy: IfNotPresent # İmaj yerel olarak varsa çekme
        resources:
          requests:
            memory: "512Mi" # Her Pod için minimum 512MB bellek isteği
            cpu: "500m"    # Her Pod için minimum 0.5 CPU çekirdeği isteği
          limits:
            memory: "1Gi"  # Her Pod için maksimum 1GB bellek sınırı
            cpu: "1"       # Her Pod için maksimum 1 CPU çekirdeği sınırı
        env:
          - name: CONCURRENCY_LIMIT # Scraper içindeki eş zamanlılık limiti
            value: "3" # Her Pod içinde aynı anda 3 tarayıcı çalışsın
        # Puppeteer'ın /dev/shm sorununu çözmek için
        volumeMounts:
          - name: dshm
            mountPath: /dev/shm
      volumes:
        - name: dshm
          emptyDir:
            medium: Memory
            sizeLimit: "1Gi" # Paylaşılan bellek boyutu

Bu tanım, my-scraper:1.0 imajından 3 adet Pod çalıştırır. Her Pod’un belirli bellek ve CPU kaynakları vardır ve Puppeteer için /dev/shm ayarı yapılmıştır. CONCURRENCY_LIMIT ortam değişkeni, her Pod’un kendi içinde kaç eş zamanlı scraping görevi yürüteceğini kontrol eder.

Uygulamak için:

kubectl apply -f scraper-deployment.yaml

Servisler ve İletişim

Eğer scraper’larımız bir Redis kuyruğuna (BullMQ için) veya bir veritabanına bağlanacaksa, bu servisleri de Kubernetes’te çalıştırmak ve scraper Pod’larının bunlara erişebilmesini sağlamak gerekir. Örneğin, bir Redis Deployment’ı ve Service’i tanımlayabiliriz. Scraper Pod’ları, Redis Service’in adını kullanarak Redis’e bağlanabilirler (örneğin, redis-service:6379).

# redis-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  labels:
    app: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:6-alpine
        ports:
        - containerPort: 6379
---

redis-service.yaml

apiVersion: v1 kind: Service metadata: name: redis-service spec: selector: app: redis ports: - protocol: TCP port: 6379 targetPort: 6379

Scraper Pod’ları içinde Redis’e bağlanırken redis-service ana bilgisayar adını kullanabiliriz.

Horizontal Pod Autoscaler (HPA) ile Otomatik Ölçeklendirme

Kubernetes’in en güçlü özelliklerinden biri, iş yüküne göre Pod’ları otomatik olarak ölçeklendirebilmesidir. Horizontal Pod Autoscaler (HPA), Pod’ların CPU veya bellek kullanımına (veya özel metriklere) göre Deployment’ın replicas sayısını dinamik olarak ayarlar. Bu, scraping talebi arttığında otomatik olarak daha fazla worker Pod’unun başlatılmasını ve talepler azaldığında kaynak tasarrufu için Pod’ların azaltılmasını sağlar.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: scraper-worker-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: scraper-worker
  minReplicas: 3 # Minimum 3 Pod çalışsın
  maxReplicas: 10 # Maksimum 10 Pod'a kadar ölçeklenebilir
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70 # CPU kullanımı %70'i aştığında ölçekle
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80 # Bellek kullanımı %80'i aştığında ölçekle

Bu HPA tanımı, scraper-worker Deployment’ının CPU kullanımı %70’i veya bellek kullanımı %80’i aştığında Pod sayısını artıracak, minimum 3, maksimum 10 Pod arasında ölçeklendirme yapacaktır.

kubectl apply -f scraper-hpa.yaml

Persistent Storage (Kalıcı Depolama)

Eğer scraper’larımız çekilen verileri doğrudan Pod’un dosya sistemine kaydediyorsa (ki bu iyi bir uygulama değildir çünkü Pod’lar geçicidir), bu verilerin kaybolmaması için kalıcı depolama (Persistent Volume – PV ve Persistent Volume Claim – PVC) kullanmamız gerekir. Ancak, genellikle çekilen veriler doğrudan bir veritabanına veya bulut depolama hizmetine (AWS S3, Google Cloud Storage) gönderilir, bu da kalıcı depolama karmaşıklığını azaltır.

Yapılandırma Yönetimi

API anahtarları, veritabanı bağlantı dizeleri veya hedef sitelerin URL’leri gibi hassas veya sık değişen yapılandırma verilerini doğrudan Docker imajına gömmek yerine Kubernetes’in ConfigMaps ve Secrets kaynaklarını kullanmalıyız. ConfigMaps hassas olmayan veriler için, Secrets ise hassas veriler için kullanılır ve Pod’lara ortam değişkenleri veya dosya olarak enjekte edilebilir.

# scraper-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: scraper-config
data:
  TARGET_DOMAIN: "example.com"
  CONCURRENCY_LIMIT: "5" # Pod başına eş zamanlı tarayıcı sayısı
---

scraper-secret.yaml

apiVersion: v1 kind: Secret metadata: name: scraper-secrets type: Opaque stringData: API_KEY: "your_api_key_here"

Bu yapılandırmaları Pod tanımında kullanmak için:

# ... (scraper-deployment.yaml içinde container tanımı) ...
        env:
          - name: TARGET_DOMAIN
            valueFrom:
              configMapKeyRef:
                name: scraper-config
                key: TARGET_DOMAIN
          - name: CONCURRENCY_LIMIT
            valueFrom:
              configMapKeyRef:
                name: scraper-config
                key: CONCURRENCY_LIMIT
          - name: API_KEY
            valueFrom:
              secretKeyRef:
                name: scraper-secrets
                key: API_KEY

Güvenlik ve Etik Hususlar

Web scraping yaparken yasal ve etik sınırlamalara uymak çok önemlidir:

  • robots.txt dosyası: Bir siteyi taramadan önce her zaman robots.txt dosyasını kontrol edin. Bu dosya, sitenin hangi bölümlerinin taranıp taranamayacağını belirtir. Bu kurallara uymak, yasal sorunlardan kaçınmak ve iyi bir “internet vatandaşı” olmak için önemlidir.
  • Hizmet Şartları: Taramayı düşündüğünüz web sitesinin hizmet şartlarını okuyun. Bazı siteler scraping’i açıkça yasaklar.
  • Orantılılık ve Yük: Hedef sunuculara aşırı yük bindirmemeye dikkat edin. Agresif scraping, sunucuları yavaşlatabilir veya çökertebilir. Scraping hızınızı ve eş zamanlılık seviyenizi ayarlayarak sunucuya dostane davranın. Rate limiting (hız sınırlama) uygulayın.
  • IP Rotasyonu ve Proxy’ler: Aynı IP adresinden çok sayıda istek göndermek, IP adresinizin engellenmesine yol açabilir. Proxy sunucuları veya IP rotasyon servisleri kullanmak bu sorunu aşabilir.
  • Veri Gizliliği: Kişisel olarak tanımlanabilir bilgileri (PII) toplarken ve saklarken GDPR, KVKK gibi veri gizliliği düzenlemelerine kesinlikle uyun.

Sonuç ve Gelecek Adımlar

Bu makalede, Puppeteer ve Node.js’in gücünü Docker’ın konteynerleştirme yetenekleri ve Kubernetes’in orkestrasyon kabiliyetleriyle birleştirerek eş zamanlı ve ölçeklenebilir bir web scraper mimarisi inşa etmeyi ele aldık. Temel bir scraper uygulamasından başlayarak, eş zamanlılık yönetimi, Docker ile paketleme ve Kubernetes ile dağıtım, otomatik ölçeklendirme ve yapılandırma yönetimi gibi konuları detaylandırdık. Bu teknoloji yığını, modern web’in dinamik ve karmaşık yapısıyla başa çıkabilen, yüksek performanslı ve hataya dayanıklı bir scraping çözümü sunar.

Gelecekte bu mimariyi daha da geliştirmek için atılabilecek adımlar şunlar olabilir:

  • Gelişmiş İş Kuyrukları: BullMQ gibi daha sofistike bir iş kuyruğu sistemi entegre etmek, görevlerin önceliklendirilmesi, gecikmeli görevler ve başarısız görevler için yeniden deneme mantığı gibi gelişmiş özellikler sunar.
  • Proxy Yönetimi: Gelişmiş bir proxy rotasyon ve yönetimi sistemi entegre etmek, IP engellemelerini daha etkili bir şekilde atlatmaya yardımcı olabilir.
  • İzleme ve Günlükleme: Prometheus ve Grafana gibi araçlarla Kubernetes kümesini ve scraper Pod’larını izlemek, performans darboğazlarını ve hataları tespit etmek için kritik öneme sahiptir. Merkezi günlükleme (Elasticsearch, Fluentd, Kibana – EFK stack veya Loki) ile tüm Pod’lardan gelen günlükleri toplamak ve analiz etmek operasyonel verimliliği artırır.
  • Veritabanı Entegrasyonu: Çekilen verileri doğrudan bir veritabanına (MongoDB, PostgreSQL) kaydetmek, veri analizi ve kullanımı için daha esnek bir yapı sağlar.
  • CI/CD Entegrasyonu: Docker imajlarının otomatik olarak oluşturulması ve Kubernetes’e dağıtılması için bir Sürekli Entegrasyon/Sürekli Dağıtım (CI/CD) hattı kurmak, geliştirme ve dağıtım süreçlerini otomatikleştirecektir.

Bu adımlar, web scraping projenizi daha profesyonel, sürdürülebilir ve kurumsal düzeyde bir çözüme dönüştürecektir.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Exit mobile version