Geonode logo
Geonode Team

Geonode Team

Güncellenme: 16 Ekim 2026

Yayınlanma: 16 Ekim 2026

Python ile Web Sitesi Değişikliklerini Algılama: Belgeler ve Politika Güncellemelerini İzleme

Web sitesi değişiklik tespiti, bir sağlayıcının hizmet şartları maddesini yeniden yazdığını, belgelerinde bir uç noktayı kullanımdan kaldırdığını veya kimse duyurmadan gizlilik politikasını sessizce düzenlediğini nasıl öğrenebileceğinizdir. Bu kılavuzun sonunda, Geonode ve Scraper API aracılığıyla sayfaların bir listesini Markdown formatında anlık görüntüleyen, gereksiz bilgileri ayıklayan, her sürümü SQLite'da depolayan, okunabilir bir fark raporu oluşturan ve yalnızca içerik gerçekten değiştiğinde Slack'e gönderi paylaşan bir Python betiğine sahip olacaksınız. Bu betik, cron veya GitHub Actions üzerinden çalışır ve istikrarsız veri alımlarına rağmen sorunsuz bir şekilde çalışmaya devam eder. Bu betik, kasıtlı olarak bir fiyat izleme aracı değildir. Fiyatlar, bilinen bir yerdeki sayılardır; belgeler ise, değişikliğin yeri ve şekli önceden bilinmeyen metinlerdir. Aşağıdaki tasarım seçimleri bu mantığa dayanmaktadır.

Web sitesi değişiklik tespiti, bir sağlayıcının hizmet şartları maddesini yeniden yazdığını, belgelerinde bir uç noktayı kullanımdan kaldırdığını veya kimse duyurmadan gizlilik politikasını sessizce düzenlediğini nasıl öğrenebileceğinizdir. Bu kılavuzun sonunda, Geonode ve Scraper API aracılığıyla sayfaların bir listesini Markdown formatında anlık görüntüleyen, gereksiz bilgileri ayıklayan, her sürümü SQLite'da depolayan, okunabilir bir fark raporu oluşturan ve yalnızca içerik gerçekten değiştiğinde Slack'e gönderi yapan bir Python betiğine sahip olacaksınız. Bu betik, cron veya GitHub Actions üzerinden çalışır ve istikrarsız veri alımlarına rağmen sorunsuz bir şekilde çalışmaya devam eder.

Bu betik, kasıtlı olarak bir fiyat izleme aracı değildir. Fiyatlar, bilinen bir yerdeki sayılardır; belgeler ise, değişikliğin yeri ve şekli önceden bilinmeyen metinlerdir. Aşağıdaki tasarım seçimleri bu mantığa dayanmaktadır.

Neleri izliyorsunuz ve neden Markdown?

İzlemeye değer sayfaların ortak özellikleri şunlardır: herkese açık, metin ağırlıklı, seyrek güncellenen ve güncellendiklerinde önemli sonuçlar doğuran sayfalar. API referans sayfaları, kimlik doğrulama kılavuzları, hız sınırı tabloları, şartlar ve koşullar, veri işleme sözleşmeleri, kabul edilebilir kullanım politikaları, değişiklik günlükleri ve durum sayfalarındaki olay geçmişleri bu kategoriye girer.

Bu sayfaların ham HTML kodlarını karşılaştırmak oldukça zahmetlidir. Varlık URL'lerindeki hash değerleri, CSRF belirteçleri, A/B testi sınıf adları ve eklenen analitik verileri her yüklemede değişir. Markdown'a dönüştürmek, bunların neredeyse tamamını ortadan kaldırır ve sadece cümleleri bırakır. /v1/extract uç noktası doğrudan Markdown döndürür, bu sayede komut dosyası hiçbir zaman bir HTML ayrıştırıcısına dokunmaz.

pip install requests ile tek bağımlılığı yükleyin. Geri kalan her şey standart kütüphanededir.

Anlık görüntü alma

Fetcher, Markdown formatını ister; varsayılan olarak JavaScript ile işlemeyi devre dışı tutar ve istemci tarafında işlenen doküman siteleri için bu özelliği sayfa bazında etkinleştirir. wait_config bloğu, yalnızca bir sayfanın içeriğinin yüklenmeden sonra doldurulması durumunda gereklidir; mevcut olduğunda işlemeyi otomatik olarak etkinleştirir.

import os
import requests

API = "https://scraper.geonode.io"
HEADERS = {"X-Api-Key": os.environ["GEONODE_API_KEY"]}

def snapshot(session: requests.Session, url: str, render_js: bool = False,
             wait_for: str | None = None) -> tuple[int, str]:
    """Return (origin_http_status, markdown). Raises on API-level errors."""
    body = {"url": url, "formats": ["markdown"], "render_js": render_js,
            "processing_mode": "sync", "timeout": 60000}
    if wait_for:
        body["wait_config"] = {"wait_until": "domcontentloaded", "wait_for": wait_for,
                               "wait_timeout": 2000}
        body.pop("render_js")          # wait_config implies rendering; sending false is rejected
    r = session.post(f"{API}/v1/extract", json=body, headers=HEADERS, timeout=90)
    if r.status_code == 429:
        err = r.json().get("error", r.json())
        raise RuntimeError(f"rate limited ({err.get('code')}): {err.get('message')}")
    r.raise_for_status()
    payload = r.json()
    return payload["metadata"]["http_status"], (payload.get("data") or {}).get("markdown") or ""

Kaynak durumu, içerik kadar önemlidir. 503 bakım sayfası, tamamen geçerli bir Markdown'a dönüştürülür; http_status'yu kontrol etmezseniz, bunu yeni bir sürüm olarak kaydeder ve bununla ilgili uyarı verirsiniz.

Gürültüyü Temizleme

Sırayla uygulanan iki mekanizma. Bir düzenli ifade (regex) geçişi, gereksiz olduğu bilinen satırları kaldırır: tarihler, “son güncelleme” başlıkları, çerez bildirimleri, telif hakkı altbilgileri, “bu sayfayı düzenle” bağlantıları. Ardından, bir bölüm izin listesi yalnızca sizin için önemli olan başlıkları tutar; bu sayede, Markdown dönüştürücüsünün gerçek metin oldukları için koruduğu gezinme menüleri, kenar çubukları ve pazarlama blokları kaldırılır.

import re

NOISE = [
    re.compile(r"(?im)^.*\b(last (updated|modified|reviewed)|effective date|updated on)\b.*$"),
    re.compile(r"(?im)^.*\b(cookie|cookies)\b.*\b(accept|consent|preferences)\b.*$"),
    re.compile(r"(?im)^.*(©|copyright|all rights reserved).*$"),
    re.compile(r"(?im)^.*\b(edit this page|was this page helpful|on this page)\b.*$"),
    re.compile(r"\b(?:\d{1,2}\s)?(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\.?\s+\d{1,2},?\s+\d{4}\b", re.I),
    re.compile(r"\b\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2})?\S*)?\b"),
    re.compile(r"!\[[^\]]*\]\([^)]*\)"),                 # images
]

def strip_noise(md: str, keep_sections: list[str] | None = None) -> str:
    for rx in NOISE:
        md = rx.sub("", md)
    if keep_sections:
        kept, keep = [], False
        for line in md.splitlines():
            if line.startswith("#"):
                title = line.lstrip("#").strip().lower()
                keep = any(s.lower() in title for s in keep_sections)
            if keep:
                kept.append(line)
        md = "\n".join(kept)
    return normalize_ws(md)

def normalize_ws(text: str) -> str:
    lines = (re.sub(r"[ \t]+", " ", ln).strip() for ln in text.splitlines())
    return "\n".join(ln for ln in lines if ln)

keep_sections, başlıklarla eşleşen alt dizelerin bir listesidir; dolayısıyla ["rate limit", "authentication"], H2/H3 bloklarını ve bir sonraki başlığa kadar bunların altındaki tüm içeriği korur. Herhangi bir maddenin önemli olduğu politika sayfaları için None'yu olduğu gibi bırakın.

Tarih düzenli ifadeleri kasıtlı olarak agresiftir. Bir politika sayfasındaki "1 Mart 2026'dan itibaren geçerlidir" satırı tam da görmek istediğiniz türden bir değişikliktir; bu sizin için önemliyse, ilk NOISE girişini kaldırın ve farkların bunu göstermesine izin verin.

Hashleme ve fark alma karşılaştırması

Hashleme, “herhangi bir değişiklik oldu mu?” sorusuna sabit sürede yanıt verir. Fark alma ise “ne değişti?” sorusuna, bir kişinin Slack mesajında okuyabileceği bir biçimde yanıt verir. Çoğu web sitesi değişiklik algılama aracı, hashleme aşamasında durur; her ikisini de kullanın: hashlemeyi filtreleme için, fark almayı raporlama için.

import difflib
import hashlib

def digest(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8")).hexdigest()

def unified(old: str, new: str, url: str, context: int = 2, max_lines: int = 120) -> str:
    diff = difflib.unified_diff(old.splitlines(), new.splitlines(),
                                fromfile=f"{url} (previous)", tofile=f"{url} (current)",
                                lineterm="", n=context)
    lines = list(diff)
    if len(lines) > max_lines:
        lines = lines[:max_lines] + [f"... {len(lines) - max_lines} more lines"]
    return "\n".join(lines)

difflib.unified_diff, standart kütüphanede bulunur ve docs.python.org adresinde belgelenmiştir. n argümanı bağlam satırlarını belirler; düz metin için iki satır yeterlidir. Tam sayfa yeniden yazımının 5.000 satırlık bir uyarı oluşturmaması için çıktı uzunluğunu sınırlayın.

Anlık görüntülerin saklanması

SQLite sadece son sürümü değil, tam geçmişi de saklar. Bu sayede son iki anlık görüntüyle karşılaştırma yapmak (bir sonraki bölüm) ve aylar sonra “bu cümle ilk ne zaman ortaya çıktı?” sorusuna yanıt vermek mümkün olur.

import sqlite3
import time

SCHEMA = """
CREATE TABLE IF NOT EXISTS snapshots (
  id          INTEGER PRIMARY KEY,
  url         TEXT NOT NULL,
  taken_at    REAL NOT NULL,
  http_status INTEGER,
  hash        TEXT,
  content     TEXT
);
CREATE INDEX IF NOT EXISTS idx_url_time ON snapshots(url, taken_at DESC);
"""

def open_db(path: str = "changes.sqlite") -> sqlite3.Connection:
    db = sqlite3.connect(path)
    db.executescript(SCHEMA)
    return db

def last_snapshots(db: sqlite3.Connection, url: str, n: int = 2) -> list[tuple[str, str]]:
    """Most recent first: [(hash, content), ...] for successful (200) fetches only."""
    return db.execute("SELECT hash, content FROM snapshots WHERE url=? AND http_status=200 "
                      "ORDER BY taken_at DESC LIMIT ?", (url, n)).fetchall()

def save(db: sqlite3.Connection, url: str, status: int, h: str | None, content: str | None) -> None:
    db.execute("INSERT INTO snapshots (url, taken_at, http_status, hash, content) VALUES (?,?,?,?,?)",
               (url, time.time(), status, h, content))
    db.commit()

Politika sayfaları küçüktür, bu nedenle her çalıştırma için tam içeriği saklamak maliyet açısından uygun olur. Saat başı yüzlerce uzun belgeyi izliyorsanız, yalnızca hash değeri değiştiğinde içeriği saklayın, aksi takdirde hash satırını tutun.

Kararsız veri alımlarıyla başa çıkma

Kararsız veri alımları, web sitesi değişiklik tespitinde yanlış pozitif sonuçların ana kaynağıdır; bu nedenle üç savunma yöntemi vardır. Vazgeçmeden önce, geri çekilme aralığı uygulayarak veri alımını yeniden deneyin. 200 dışında gelen her sonucu “veri yok” olarak değerlendirin, bu durumu kayıt için saklayın ve karşılaştırma yapmayın. Ayrıca, yeni hash değerini sadece son anlık görüntüyle değil, son iki geçerli anlık görüntüyle karşılaştırın: sayfa A, B, A şeklinde değiştiyse, B büyük olasılıkla geçici bir varyanttır (hala 200 yanıtı veren bir hata sayfası, eski bir sürümü sunan önbellek) ve bir uyarıyı hak etmez.

def fetch_with_retry(session: requests.Session, url: str, attempts: int = 3, **kw) -> tuple[int, str]:
    delay = 5
    for i in range(attempts):
        try:
            status, md = snapshot(session, url, **kw)
            if status < 500 and md.strip():
                return status, md
        except (requests.RequestException, RuntimeError) as exc:
            if i == attempts - 1:
                raise
        time.sleep(delay)
        delay *= 3
    return status, md

def check(db: sqlite3.Connection, session: requests.Session, url: str,
          keep_sections: list[str] | None = None, **kw) -> str | None:
    """Returns a unified diff if a real change was detected, else None."""
    status, raw = fetch_with_retry(session, url, **kw)
    if status != 200:
        save(db, url, status, None, None)
        return None
    clean = strip_noise(raw, keep_sections)
    h = digest(clean)
    previous = last_snapshots(db, url, n=2)
    save(db, url, status, h, clean)
    if not previous:
        return None                                   # first run, nothing to compare
    if any(h == old_hash for old_hash, _ in previous):
        return None                                   # unchanged, or a revert of a blip
    return unified(previous[0][1], clean, url)

İkinci anlık görüntü kuralının bir dezavantajı vardır: tek bir çalıştırma içinde geri alınan gerçek bir değişiklik rapor edilmez. Dokümantasyon ve politika izleme açısından bu, neredeyse her zaman doğru bir tercihtir.

Bildirim

Slack’e gelen webhook’lar, text alanını içeren bir JSON gövdesi kabul eder; farkların sabit genişlikli kalması için bunları bir kod bloğu içine alın. smtplib aracılığıyla e-posta gönderimi birkaç satır daha uzun sürer ve Slack’in kullanılamadığı durumlarda kullanışlıdır.

import json
import smtplib
from email.message import EmailMessage

SLACK_WEBHOOK = os.environ.get("SLACK_WEBHOOK_URL")

def notify_slack(url: str, diff: str) -> None:
    if not SLACK_WEBHOOK:
        return
    fence = "`" * 3                          # literal ``` would close the Markdown block
    text = f"*Change detected:* {url}\n{fence}\n{diff[:3500]}\n{fence}"
    requests.post(SLACK_WEBHOOK, data=json.dumps({"text": text}),
                  headers={"Content-Type": "application/json"}, timeout=15).raise_for_status()

def notify_email(url: str, diff: str, to: str) -> None:
    msg = EmailMessage()
    msg["Subject"] = f"Change detected: {url}"
    msg["From"] = os.environ["SMTP_FROM"]
    msg["To"] = to
    msg.set_content(diff)
    with smtplib.SMTP(os.environ["SMTP_HOST"], int(os.environ.get("SMTP_PORT", "587"))) as s:
        s.starttls()
        s.login(os.environ["SMTP_USER"], os.environ["SMTP_PASS"])
        s.send_message(msg)

Slack, uzun mesajları keser; bu nedenle fark metninde 3.500 karakter sınırı vardır. Uzun fark metinleri için ilk ekranı gönderin ve tam metni başka bir yere ekleyin ya da mesajda saklanmış bir anlık görüntü kimliğine yönlendirin.

İzleme listesi ve işleyici

Yapılandırma bir liste içinde tutulur; böylece onu depoda saklayabilir ve koddaki değişiklikleri incelediğiniz gibi buradaki değişiklikleri de inceleyebilirsiniz. Her giriş, URL’yi, görüntüleme gerekip gerekmediğini, beklenecek isteğe bağlı bir seçiciyi ve isteğe bağlı bir bölüm izin listesini içerir.

WATCHLIST = [
    {"url": "https://example.com/legal/terms"},
    {"url": "https://example.com/legal/privacy"},
    {"url": "https://example.com/docs/api/authentication",
     "keep_sections": ["authentication", "rate limit"]},
    {"url": "https://example.com/changelog", "render_js": True, "wait_for": "main article"},
]

def main() -> None:
    db = open_db()
    session = requests.Session()
    for item in WATCHLIST:
        url = item["url"]
        try:
            diff = check(db, session, url, keep_sections=item.get("keep_sections"),
                         render_js=item.get("render_js", False), wait_for=item.get("wait_for"))
        except Exception as exc:
            print(f"[skip] {url}: {exc}")
            continue
        if diff:
            print(f"[changed] {url}")
            notify_slack(url, diff)
        else:
            print(f"[ok] {url}")

if __name__ == "__main__":
    main()

İlk çalıştırma, veritabanını doldurur ve herhangi bir rapor vermez. İkinci çalıştırmadan itibaren, her gerçek değişiklik, farkları içeren bir Slack mesajı oluşturur.

Web sitesi değişikliklerinin algılanmasının zamanlanması

Altyapı gerektirmeyen iki seçenek. Python ve aşağıdaki ortam değişkenlerine sahip herhangi bir makinede bir cron satırı:

# daily at 06:10, log to a file
10 6 * * * cd /opt/change-monitor && /usr/bin/python3 monitor.py >> monitor.log 2>&1

Ya da bir GitHub Actions iş akışı; bu, SQLite dosyasının durum deposu olarak depoya geri kaydedilebilmesi ve gizli bilgilerin depo ayarlarında bulunması nedeniyle kullanışlıdır:

name: change-monitor
on:
  schedule:
    - cron: "10 6 * * *"
  workflow_dispatch:
jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install requests
      - run: python monitor.py
        env:
          GEONODE_API_KEY: ${{ secrets.GEONODE_API_KEY }}
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
      - run: |
          git config user.name "change-monitor"
          git config user.email "bot@users.noreply.github.com"
          git add changes.sqlite && git commit -m "snapshot $(date -u +%F)" || true
          git push

Değişiklik günlükleri için saatlik zamanlamalar yeterlidir; yasal sayfalar için ise günlük zamanlama çoğu ekibin ihtiyacını fazlasıyla karşılar. Aşağıdaki tablo makul bir başlangıç noktası sunar.

Sayfa türüÖnerilen sıklıkrender_jskeep_sections
Kullanım Koşulları, Gizlilik, Kullanım Kurallarıgünlükhayıryok (tüm sayfa)
API referansı6 saatte birdoküman motoruna bağlıevet, kullandığınız uç noktalar
Değişiklik günlüğü / sürüm notlarısaatliksıkyok
Durum sayfası olay geçmişisaatlikgenellikleyok

Kullanım koşulları ve robotlar hakkında bir not

Günde bir kez halka açık bir belgeyi veya politika sayfasını okumak, web'in olağan kullanım şeklidir ve tarayıcının yaptığı şeyin aynısıdır. Yine de, siteyi izleme listesine eklemeden önce sitenin şartlarını ve robots.txt'yu kontrol edin, sıklığı orantılı tutun ve size izin verilmeyen bir oturum açma bilgisi gerektiren sayfaları asla izlemeyin. Bir site RSS beslemesi veya değişiklik günlüğü API'si yayınlıyorsa, bunları tercih edin: bu her iki taraf için de daha ekonomiktir ve sayfa düzeni değiştiğinde bozulmaz.

Sık yapılan hatalar

Ham HTML’yi karşılaştırma

Modern bir doküman sitesinin her derlemesinde varlık hash’leri ve sınıf adları yeniden yazılır. Karşılaştırma sonuçları okunamaz hale gelir ve her dağıtımda bir uyarı tetiklenir. snapshot() örneğinde olduğu gibi, her şeyden önce Markdown’a dönüştürün.

İlk 200 olmayan durum için uyarı verme

Tek bir 503 hatası veya "Lütfen insan olduğunuzu doğrulayın" mesajıyla 200 kodunu döndüren bir bot doğrulama sayfası, saklanan bir sürüm veya uyarı haline getirilmemelidir. http_status'yu kontrol edin, içeriğin boş olmamasını şart koşun ve son iki geçerli anlık görüntüyle karşılaştırın.

Çok az veya çok fazla içerik çıkarılması

Karşılaştırma sonuçlarınızda yalnızca bir tarih görünüyorsa, bunu NOISE'ya ekleyin. keep_sections, altında bulunduğu başlığı kaldırdığı için bir koşul değişikliği gözden kaçmışsa, izin listesini genişletin. İzlemeyi bir hafta boyunca çalıştırın ve güvenmeden önce, ürettiği karşılaştırma sonuçlarına göre iki listeyi ayarlayın.

İzleme listesinin gerçeklerden sapmasına izin vermek

Yeni bir yola yönlendiren bir URL veya sürüm numaralı bir önek altına taşınan bir doküman sayfası, sessizce 404 hatasına veya farklı bir sayfaya dönüşür. 200 dışındaki durum kodlarını açıkça günlüğe kaydedin ve çıktıda yer alan "atla" satırlarını ara sıra gözden geçirin.

Sonraki adım

Yukarıda anlatılanların tamamı, standart Python kütüphanesi ve sayfa başına bir HTTP çağrısından ibarettir; bu da belgeler için web sitesi değişiklik algılamasının gerektirdiği tek şeydir. Kendi sunucunuzda barındırması zor olan kısım, veri alma işleminin kendisidir: JavaScript ile işlenen belge sitelerini destekleyen, kaynağın gerçek http_status değerini döndüren ve her sabah kırk satıcı sayfasını kontrol ettiğinizde engellenmeyen temiz bir Markdown dönüştürme işlemi. Geonode ve Scraper API tam da bu çağrıyı sağlar; ayrıca istemci tarafından işlenen sayfalar için render_js ve wait_config seçenekleri ile aylık 1.5K isteklik ücretsiz bir kademesi vardır ki bu, günlük izleme listesini rahatlıkla karşılar. Planlar için Scraper API sayfasına, ilgili kullanım örnekleri için ise web kazıma kullanım örneğine bakın.

SSS

Web sitesi değişiklik algılama nedir ve ne zaman yararlıdır?

Web sitesi değişiklik algılama, bir sayfayı belirli aralıklarla alıp önceki anlık görüntüyle karşılaştırma ve anlamlı içerik farklılığı olduğunda uyarı verme işlemidir. Bu, bağımlı olduğunuz ancak kontrol edemediğiniz sayfalar için en yararlıdır: API belgeleri, hizmet şartları, gizlilik politikaları, fiyatlandırma şartları ve satıcı değişiklik günlükleri gibi, sessiz bir düzenlemenin entegrasyonu veya uyumluluk varsayımını bozabileceği durumlar.

Neden ham HTML yerine Markdown karşılaştırılır?

HTML, metin değişmeden değişen mizanpaj, komut dosyaları, izleme parametreleri ve derleme hash'lerini içerir. Önce Markdown'a dönüştürmek, sayfayı içeriğine indirger; böylece fark analizi, yüz satırlık öznitelik değişikliği yerine düzenlenmiş cümleyi gösterir. Geonode Scraper API, Markdown'ı doğrudan döndürür; bu da iş akışını kısa tutar.

Tarihler, çerez bildirimleri ve gezinme öğelerinden kaynaklanan yanlış uyarıları nasıl önleyebilirim?

Küçük bir düzenli ifade kümesiyle (tarihler, "son güncelleme" satırları, çerez bildirimleri, altbilgiler) bilinen gürültüyü ayıklayın, ardından başlık izin listesi kullanarak yalnızca ilgilendiğiniz bölümleri tutun. Hash işlemi öncesinde boşlukları normalleştirin. Son olarak, geçici bir hata sayfasının tek başına bir uyarı tetiklememesi için son iki anlık görüntüyle karşılaştırın.

Sayfayı hashlemeli miyim yoksa farkını mı almalıyım?

Her ikisini de yapın. Hash, herhangi bir işlem yapılıp yapılmayacağına karar veren hızlı bir eşitlik kontrolüdür. Uyarıda insanın okuduğu şey, difflib'den alınan birleşik fark karşılaştırmasıdır. Önce hash işlemi yapın, yalnızca hash değeri farklıysa fark karşılaştırması yapın ve okuyucunun sayfayı açmadan değişikliği değerlendirebilmesi için fark karşılaştırmasını bildirimle birlikte gönderin.

Web sitesi değişiklik algılama işlemi ne sıklıkla çalıştırılmalıdır?

Sıklığı, sayfanın önemine ve değişim hızına göre ayarlayın. Yasal ve politika sayfaları nadiren yılda birkaç kereden fazla değişir, bu nedenle günlük kontrol yeterlidir. Aktif API belgeleri ve değişiklik günlükleri ise saatlik kontrolleri haklı kılar. Kontrolü cron veya GitHub Actions zamanlamasından çalıştırın ve aralığı, hatalı bir veri alımı rapor edilmeden önce yeniden denenebilecek kadar uzun tutun.

Uyumluluk ve sınırlamalarla ilgili nelere dikkat edilmelidir?

Halka açık belgeleri ve politika sayfalarını makul bir sıklıkta izlemek, web'in normal bir kullanım şeklidir; ancak sitenin hizmet şartlarını ve robots.txt dosyasını okuyun, bunlara uyun ve erişim izniniz olmayan, kimlik doğrulama gerektiren sayfaları izlemeyin. API tarafında, Scraper API ücretsiz kademesi aylık 1.5K istek kapsar; günde 40 sayfanın kontrol edilmesi bu sınırın içindedir.