Web Scraping Nedir: Yasal mı Değil mi ve Nasıl Yapılır

Veri toplama işlemleri söz konusu olduğunda, sistem yöneticilerinin ve DevOps mühendislerinin önüne kaçınılmaz olarak web scraping konusu geliyor. Monitoring scriptleri yazarken, rakip analizi için veri toplarken ya da üçüncü taraf bir API’nin olmadığı durumda işinizi halletmeye çalışırken bu teknolojiyle mutlaka karşılaşıyorsunuz. Ben de yıllarca hem kendi projelerimde hem de müşterilerimizin altyapılarında scraping çözümleri kurdum ve bu süreçte öğrendiğim en önemli şey şu: teknik kısım aslında kolay, asıl mesele neyi, nasıl ve hangi sınırlar içinde yapacağınızı bilmek.

Web Scraping Nedir ve Neden Önemli

Web scraping, web sitelerindeki verileri otomatik olarak çekip işleme yöntemidir. Tarayıcınız bir sayfayı yüklediğinde ne yapıyorsa, scraper da bunu programatik olarak yapar: HTTP isteği gönderir, HTML yanıtı alır, içeriği parse eder ve istediğiniz verileri çıkarır.

Sistem yöneticisi perspektifinden bakıldığında scraping birçok meşru senaryoda karşımıza çıkar. Örneğin bir e-ticaret sitesinin fiyat değişikliklerini takip etmek, SLA anlaşmazlıklarında kanıt toplamak için belirli aralıklarla anlık görüntü almak, ya da kendi altyapınızdaki servislerin dışarıya bakan yüzlerini test etmek bunların başında gelir. Bunların yanında competitive intelligence, pazar araştırması veya akademik veri toplama gibi kullanım alanları da mevcut.

Ancak şunu baştan netleştirelim: web scraping “hack” değildir, ama her durumda da tamamen masum değildir.

Yasal mı, Değil mi? Gerçek Cevap

Bu sorunun “her zaman yasal” ya da “her zaman yasadışı” gibi net bir cevabı yok. Türkiye’deki yasal çerçeveden ve uluslararası içtihatlardan bakıldığında tablo oldukça nüanslı.

Kötü Haber: Gri Alan Çok Geniş

robots.txt ihlali: Teknik olarak robots.txt dosyası bir sözleşme değildir, ancak bunu kasıtlı olarak ihlal etmek bazı yargı bölgelerinde hukuki sorumluluğa yol açabilir. 2019’da ABD’de görülen hiQ Labs vs. LinkedIn davasında mahkeme, kamuya açık verilerin scraping’inin CFAA kapsamında yasadışı olmadığına hükmetti. Ama bu Türkiye hukuku için bağlayıcı değil.

KVKK perspektifi: Eğer topladığınız veri kişisel veri içeriyorsa (ad, e-posta, telefon numarası), Kişisel Verilerin Korunması Kanunu devreye giriyor. Kamuya açık bir sayfadan bile kişisel veri toplamak ve işlemek için meşru bir hukuki dayanağınızın olması gerekiyor.

Telif hakkı: İçerik scraping’i yapıyorsanız, o içeriğin telif hakkı sahibi olabilir. Sadece veriyi çekmek değil, onu kendi platformunuzda yeniden yayımlamak sorun yaratır.

Ne Zaman Daha Güvende Olursunuz

  • Robots.txt’e uymak
  • Makul gecikmeler bırakmak (sunucuya DDoS yapmamak)
  • Kişisel veri toplamamak ya da toplandığında KVKK uyumlu işlemek
  • Ticari sırları veya ödeme duvarı arkasındaki içerikleri hedeflememek
  • Kendi altyapınızı veya açıkça izin verilen verileri scrape etmek

Özetle: kamuya açık, kişisel veri içermeyen, robots.txt tarafından engellenmeyen içerikleri makul hızda toplamak büyük ölçüde kabul edilebilir bir pratik. Geri kalan her şey için ya hukuki danışmanlık alın ya da API var mı diye araştırın.

Temel Araçlar ve Kurulum

Araçlar söz konusu olduğunda Python ekosistemi açık ara öne çıkıyor. Linux sunucularda kolayca kurulabilen, cron ile entegre edilebilen ve container’a alınabilen çözümler için ideal.

Gerekli Paketleri Kuralım

# Python virtual environment oluşturma
python3 -m venv scraper-env
source scraper-env/bin/activate

# Temel paketler
pip install requests beautifulsoup4 lxml

# JavaScript render eden siteler için
pip install selenium playwright

# Daha gelişmiş scraping için
pip install scrapy httpx parsel

Basit Bir HTTP Scraper

En temel formda bir scraper şöyle görünür:

import requests
from bs4 import BeautifulSoup
import time
import random

headers = {
    'User-Agent': 'Mozilla/5.0 (compatible; MyBot/1.0; +https://example.com/bot)',
    'Accept-Language': 'tr-TR,tr;q=0.9'
}

def scrape_page(url):
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'lxml')
        return soup
    except requests.RequestException as e:
        print(f"Hata: {e}")
        return None

def polite_scrape(url_list):
    results = []
    for url in url_list:
        soup = scrape_page(url)
        if soup:
            results.append(soup)
        # Sunucuya nazik olmak için bekleme
        time.sleep(random.uniform(2, 5))
    return results

Buradaki time.sleep(random.uniform(2, 5)) satırı önemsiz görünebilir ama aslında kritik. Sabit aralıklarla istek atmak bot olduğunuzu açıkça belli eder, rastgele aralıklar hem daha nazik hem daha gerçekçidir.

robots.txt Kontrolü

Scraping yapmadan önce robots.txt kontrolünü otomatize edin:

from urllib.robotparser import RobotFileParser
from urllib.parse import urlparse

def check_robots(url, user_agent='*'):
    parsed = urlparse(url)
    robots_url = f"{parsed.scheme}://{parsed.netloc}/robots.txt"
    
    rp = RobotFileParser()
    rp.set_url(robots_url)
    rp.read()
    
    can_fetch = rp.can_fetch(user_agent, url)
    crawl_delay = rp.crawl_delay(user_agent)
    
    return {
        'allowed': can_fetch,
        'crawl_delay': crawl_delay if crawl_delay else 2
    }

# Kullanım
result = check_robots('https://example.com/products')
if result['allowed']:
    print(f"Scraping yapılabilir, bekleme: {result['crawl_delay']}s")
else:
    print("robots.txt bu URL'i engelliyor, atlanıyor.")

JavaScript ile Render Edilen Siteler

Modern web sitelerinin büyük çoğunluğu SPA (Single Page Application) mimarisinde. Bu durumda requests ile sadece boş bir HTML iskeleti alırsınız, asıl içerik JavaScript tarafından yüklenir.

Playwright ile Headless Browser

from playwright.sync_api import sync_playwright
import time

def scrape_js_page(url):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        context = browser.new_context(
            user_agent='Mozilla/5.0 (compatible; ResearchBot/1.0)'
        )
        page = context.new_page()
        
        # Sayfayı yükle ve network trafiğinin durmasını bekle
        page.goto(url, wait_until='networkidle')
        
        # Belirli bir elementi bekle
        page.wait_for_selector('.product-list', timeout=10000)
        
        content = page.content()
        browser.close()
        return content

# Playwright kurulumu için
# playwright install chromium

Headless browser kullanmak kaynak tüketimi açısından ağırdır. Her zaman önce requests + BeautifulSoup ile deneyin. JavaScript render gerektiren bir durumla karşılaştığınızda headless browser’a geçin.

Scrapy ile Kurumsal Düzey Scraping

Tek sayfa değil, bir sitenin tamamını ya da düzenli olarak binlerce sayfayı scrape etmeniz gerekiyorsa Scrapy daha uygun bir çerçeve sunuyor.

# Yeni Scrapy projesi
scrapy startproject price_monitor
cd price_monitor

# Spider oluştur
scrapy genspider product_spider example-shop.com

Basit bir Scrapy spider’ı:

import scrapy
from scrapy.http import Request
import time

class ProductSpider(scrapy.Spider):
    name = 'product_spider'
    
    custom_settings = {
        'DOWNLOAD_DELAY': 3,           # İstekler arası minimum bekleme
        'RANDOMIZE_DOWNLOAD_DELAY': True,
        'ROBOTSTXT_OBEY': True,        # robots.txt'e uy
        'CONCURRENT_REQUESTS': 2,      # Aynı anda max 2 istek
        'DEFAULT_REQUEST_HEADERS': {
            'Accept-Language': 'tr-TR,tr;q=0.9',
        }
    }
    
    start_urls = ['https://example-shop.com/kategori/']
    
    def parse(self, response):
        # Ürün linklerini topla
        for product_link in response.css('a.product-link::attr(href)').getall():
            yield Request(
                url=response.urljoin(product_link),
                callback=self.parse_product
            )
        
        # Bir sonraki sayfa
        next_page = response.css('a.pagination-next::attr(href)').get()
        if next_page:
            yield Request(url=response.urljoin(next_page), callback=self.parse)
    
    def parse_product(self, response):
        yield {
            'name': response.css('h1.product-name::text').get(),
            'price': response.css('span.price::text').get(),
            'url': response.url,
            'scraped_at': time.strftime('%Y-%m-%d %H:%M:%S')
        }
# Sonuçları JSON'a kaydet
scrapy crawl product_spider -o products.json

# Veya CSV'ye
scrapy crawl product_spider -o products.csv

ROBOTSTXT_OBEY: True ayarı Scrapy’de varsayılan olarak True’dur. Bunu False yapmak için gerçekten geçerli bir nedeniniz olmalı, bu ayarı değiştirmek istediğinizde bir adım geri çekilip “bu yasal ve etik mi?” sorusunu sormanızı öneririm.

Gerçek Dünya Senaryosu: Sunucu Fiyat Monitörü

Diyelim ki hosting firmalarının VPS fiyatlarını takip eden bir script yazmak istiyorsunuz. Bu tamamen meşru bir kullanım. İşte bunu sistematik hale getirmenin yolu:

#!/bin/bash
# /opt/scripts/price-monitor/run.sh

PYTHON_ENV="/opt/scripts/price-monitor/venv/bin/python"
SCRIPT_DIR="/opt/scripts/price-monitor"
LOG_FILE="/var/log/price-monitor/$(date +%Y%m%d).log"
DATA_DIR="/var/data/price-monitor"

# Log dizini yoksa oluştur
mkdir -p "$(dirname $LOG_FILE)" "$DATA_DIR"

echo "[$(date '+%Y-%m-%d %H:%M:%S')] Fiyat izleme başlatıldı" >> "$LOG_FILE"

$PYTHON_ENV "$SCRIPT_DIR/scraper.py" 
    --output "$DATA_DIR/prices_$(date +%Y%m%d_%H%M).json" 
    --log "$LOG_FILE" 
    2>&1

EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] HATA: Scraper başarısız, kod: $EXIT_CODE" >> "$LOG_FILE"
    # Alert gönderilebilir buradan
fi

Bu script’i cron’a ekleyin:

# Her gün sabah 9'da çalıştır
0 9 * * * /opt/scripts/price-monitor/run.sh

# Veya systemd timer ile daha modern bir yaklaşım
# /etc/systemd/system/price-monitor.timer

Rate Limiting ve Anti-Bot Korumaları

Birçok site scraper’ları tespit etmek ve engellemek için çeşitli teknikler kullanır. Bunları anlamak hem bypass etmek için değil, kendi sitelerinizi korumak için de faydalıdır.

Sık karşılaşılan engelleme mekanizmaları:

  • IP bazlı rate limiting: Belirli bir IP’den çok fazla istek gelirse engelleme
  • User-Agent kontrolü: Bilinen bot user-agent’larını filtreleme
  • Cookie ve session kontrolü: Oturum yoksa içeriği göstermeme
  • CAPTCHA: İnsan doğrulaması isteme
  • Honeypot linkleri: Gizli, sadece bot’ların takip edeceği linkler
  • TLS parmak izi: Tarayıcı mı, headless mi olduğunu anlama

Bunları bypass etmek yerine daha nazik bir scraper yazmak her zaman daha sürdürülebilir bir yol:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_session():
    session = requests.Session()
    
    # Retry stratejisi: 429 (rate limit) ve 503 durumunda bekle
    retry = Retry(
        total=3,
        backoff_factor=2,      # 1s, 2s, 4s bekleme
        status_forcelist=[429, 500, 502, 503, 504],
        respect_retry_after_header=True   # Sitenin söylediği süre kadar bekle
    )
    
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    
    return session

respect_retry_after_header=True parametresi özellikle önemli. Site size “429 Too Many Requests, 60 saniye bekle” dediğinde bunu dikkate alır. Bu hem etik hem de pratik açıdan doğru yaklaşım.

Scraping Verilerini Depolama ve İşleme

Topladığınız veriyi düzgün saklamak ve işlemek de en az scraping kodu yazmak kadar önemli.

# SQLite ile basit bir veri tabanı oluşturma
sqlite3 /var/data/scraper/prices.db << 'EOF'
CREATE TABLE IF NOT EXISTS price_records (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    site TEXT NOT NULL,
    product_name TEXT NOT NULL,
    price REAL,
    currency TEXT DEFAULT 'TRY',
    scraped_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    url TEXT
);

CREATE INDEX IF NOT EXISTS idx_scraped_at ON price_records(scraped_at);
CREATE INDEX IF NOT EXISTS idx_site_product ON price_records(site, product_name);
EOF

echo "Veritabanı hazır"

Büyük hacimli scraping projelerinde PostgreSQL veya MongoDB daha uygun olacaktır. SQLite, günlük birkaç bin kayıt için gayet yeterli.

Kendi Sitenizi Scraping’e Karşı Korumak

Bir sysadmin olarak sadece scraping yapmakla kalmaz, aynı zamanda kendi altyapınızı aşırı scraping’den korumanız da gerekir. Nginx’te temel rate limiting:

# /etc/nginx/nginx.conf veya ilgili site konfigürasyonu
http {
    # Scraping'e karşı rate limiting zone tanımı
    limit_req_zone $binary_remote_addr zone=scraper_limit:10m rate=10r/m;
    
    server {
        location /api/ {
            limit_req zone=scraper_limit burst=20 nodelay;
            limit_req_status 429;
            
            # 429 durumunda Retry-After header'ı ekle
            add_header Retry-After 60 always;
        }
        
        # User-Agent bazlı engelleme (kaba yöntem, atlatılabilir)
        if ($http_user_agent ~* (python-requests|scrapy|wget/)) {
            return 403;
        }
    }
}

Şunu söyleyeyim: User-Agent kontrolü güvenlik önlemi değil, kibarca bir “hayır” mesajıdır. Gerçek koruma için uygun rate limiting, CAPTCHA ve davranış analizi gerekir.

Etik Scraping Kontrol Listesi

Bir scraping projesine başlamadan önce şu soruları kendinize sorun:

  • Hedef sitenin robots.txt’ini kontrol ettim mi ve buna uyacak mıyım?
  • Bu veri başka bir yoldan (API, veri satın alma, doğrudan iletişim) elde edilebilir mi?
  • Topladığım veri KVKK kapsamında kişisel veri içeriyor mu?
  • Sitenin sunucusuna makul düzeyde yük bindiriyor muyum?
  • Topladığım veriyi nasıl kullanacağım ve bu kullanım amaç itibarıyla meşru mu?
  • Scraping faaliyetimi log’a alıyor ve gerekirse açıklayabilecek durumda mıyım?

Bu soruların tamamına rahatça “evet/hayır” diyebiliyorsanız, devam edin. Eğer bazılarında tereddüt ediyorsanız durun ve bir hukuki görüş alın ya da alternatif yolları araştırın.

Sonuç

Web scraping, sistem yöneticileri ve DevOps mühendisleri için son derece işlevsel bir araç. Doğru kullanıldığında monitoring, veri analizi ve otomasyon süreçlerine muazzam katkı sağlıyor. Yanlış kullanıldığında ise hem hukuki hem teknik ciddi sorunlar çıkarabiliyor.

Teknik taraf Python ekosistemiyle birlikte oldukça olgunlaşmış durumda. requests + BeautifulSoup ikilisi basit işler için, Scrapy büyük ölçekli projeler için, Playwright ise JavaScript ağırlıklı siteler için ideal seçimler olmaya devam ediyor.

Ancak teknik yetkinlikten daha önce gelen şey doğru framework ile yaklaşmak. Robots.txt’e uymak, makul gecikme bırakmak, kişisel verilere dikkat etmek ve gerektiğinde API alternatifleri araştırmak, hem etik hem pratik açıdan uzun vadede işe yarar.

Kendi projelerimde “önce API var mı?” sorusunu sormayı bir kural haline getirdim. Bazen API yoktur ama bunu sormak beni birçok potansiyel sorundan korudu. Web scraping güçlü bir araç, ama güçlü araçlarla birlikte gelen sorumluluğu da beraberinde taşıyor.

Bir yanıt yazın

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