Screaming Frog is de go-to crawler voor de meeste SEO’s, maar je bent waarschijnlijk tegen de muren aangelopen: het 500-URL plafond op de gratis versie, RAM die volloopt op grote sites, of de wens om crawls te automatiseren zonder een GUI te bewaken. Scrapy is het open-source Python framework dat deze limieten wegneemt.

Als je npm install of git clone kunt uitvoeren, kun je Scrapy uitvoeren. De leercurve is echt maar beheersbaar, vooral als je al vertrouwd raakt met CLI tools via agentische coderingsworkflows.

Waarom Scrapy?

Belangrijkste Voordelen

Screaming Frog werkt goed voor snelle audits. Maar het heeft limieten:

Beperking Impact
500 URL gratis limiet Vereist $259/jaar licentie voor grotere sites
Geheugenhongerig Grote crawls kunnen 8GB+ RAM verbruiken
GUI-afhankelijk Moeilijk te automatiseren of plannen
Beperkte aanpassing Configuratieopties zijn vast

Scrapy lost deze op:

Scrapy Wat je krijgt
Gratis en open-source Geen URL-limieten, geen licentiekosten
Lagere geheugenvoetafdruk Schijfgebackte wachtrijen houden RAM onder controle
CLI-native Scriptbaar, cron-baar, CI/CD-klaar
Volledige Python-aanpassing Extraheer wat je nodig hebt, filter hoe je wilt
Pauzeren/Hervatten Stop en ga verder met grote crawls op elk moment
Scrapy zal Screaming Frog niet voor alles vervangen. Snelle audits zijn nog steeds sneller in een GUI. Maar voor grootschalige crawls, automatisering en aangepaste extractie is het de moeite waard om in je toolkit te hebben.

Installatie

Setup

Scrapy draait op Python. Gebruik een virtuele omgeving om dingen schoon te houden:

Debian/Ubuntu:

sudo apt install python3.11-venv
python3 -m venv venv
source venv/bin/activate
pip install scrapy

macOS:

python3 -m venv venv
source venv/bin/activate
pip install scrapy

Windows:

python -m venv venv
venv\Scripts\activate
pip install scrapy
Virtuele Omgevingen Zijn Belangrijk
Gebruik altijd een venv. Globaal installeren veroorzaakt afhankelijkheidsconflicten en breekt reproduceerbaarheid.

Een Project Maken

Met Scrapy geïnstalleerd:

scrapy startproject myproject
cd myproject
scrapy genspider sitename example.com

Dit creëert:

myproject/
    scrapy.cfg
    myproject/
        __init__.py
        items.py
        middlewares.py
        pipelines.py
        settings.py
        spiders/
            __init__.py
            sitename.py

Spider code gaat in spiders/sitename.py. Configuratie staat in settings.py.

Instellingen voor Beleefd Crawlen

Kritisch

Configureer settings.py voordat je iets uitvoert. Geblokkeerd worden verspilt meer tijd dan langzaam crawlen.

# Beleefd crawlen
CONCURRENT_REQUESTS_PER_DOMAIN = 5
DOWNLOAD_DELAY = 1
ROBOTSTXT_OBEY = True

# AutoThrottle - past snelheid aan op basis van serverrespons
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 5
AUTOTHROTTLE_MAX_DELAY = 60
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
AUTOTHROTTLE_DEBUG = True

# Veiligheidslimieten
CLOSESPIDER_PAGECOUNT = 10000

# Output
FEED_EXPORT_ENCODING = "utf-8"
Met AutoThrottle ingeschakeld is 5 gelijktijdige verzoeken een redelijk startpunt. AutoThrottle past zich automatisch aan als de server moeite heeft. Zonder AutoThrottle, begin lager met 1-3.

AutoThrottle

AutoThrottle monitort serverresponstijden en past de crawlsnelheid automatisch aan:

  • Snelle respons → versnelt
  • Langzame respons → vertraagt
  • Fouten/timeouts → vertraagt aanzienlijk

In tegenstelling tot de vaste vertragingen van Screaming Frog, past het zich aan aan werkelijke servercondities.

Statuscode Afhandeling

Standaard verwijdert Scrapy’s HttpErrorMiddleware stilletjes niet-2xx responses. Dit betekent dat 404’s, 301’s, 500’s worden weggegooid voordat ze je callback bereiken. Je crawl kan 100% 200 statuscodes tonen, niet omdat de site perfect is, maar omdat fouten worden gefilterd.

Voeg dit toe aan je spider class om alle statuscodes vast te leggen:

handle_httpstatus_list = [200, 301, 302, 403, 404, 500, 502, 503]

Screaming Frog legt standaard alle statuscodes vast. Deze instelling brengt Scrapy in lijn met dat gedrag.

Real-World Prestaties

Benchmarks

Werkelijke cijfers van een testcrawl met 5 gelijktijdige verzoeken en AutoThrottle ingeschakeld:

Crawl Voortgang Pagina’s/Minuut Opmerkingen
0-200 pagina’s 14-22 Opstart
200-500 pagina’s 10-12 Stabiliseren
500-1.000 pagina’s 7-10 AutoThrottle past aan
1.000+ pagina’s 5-7 Stabiele toestand
Snelheid vs. Betrouwbaarheid
Deze snelheden lijken langzaam. Dat is het punt. AutoThrottle geeft prioriteit aan servergezondheid boven ruwe snelheid. Geblokkeerd worden en opnieuw starten verspilt meer tijd dan een methodische crawl.

Functies Vergelijking

Functie Screaming Frog Scrapy
Kosten Gratis <500 URLs, ~$259/jaar Gratis, open source
Max crawlgrootte Geheugenbeperkt Schijfgebackte wachtrijen
Aanpassing Beperkte config opties Volledige Python code
Planning Handmatig of derde partij Native CLI, cron-baar
Pauzeren/Hervatten Ja Ja (met JOBDIR)
Leercurve Laag (GUI) Medium (code)
Rate limiting Basis vaste vertragingen AutoThrottle (adaptief)
JavaScript rendering Optioneel (Chrome) Optioneel (playwright/splash)
Statuscodes Standaard allemaal Vereist configuratie
Subdomein filtering GUI checkboxen Code (flexibele regex)
Export formaten CSV, Excel, etc. JSON, CSV, XML, aangepast
CI/CD integratie Moeilijk Native

URL Filtering

Precieze Controle

Screaming Frog gebruikt checkboxen. Scrapy gebruikt code. De afweging is leercurve voor precisie.

Internationale paden uitsluiten:

import re
from urllib.parse import urlparse

class MySiteSpider(scrapy.Spider):
    name = "mysite"
    allowed_domains = ["example.com", "www.example.com"]
    start_urls = ["https://www.example.com/"]

    # Skip internationale paden zoals /uk/, /fr/, /de/
    EXCLUDED_PATTERNS = re.compile(
        r"/(in|au|th|es|hk|sg|ph|my|ca|cn|uk|kr|id|fr|vn|de|jp|nl|it|tw)/"
    )

    def filter_links(self, links):
        filtered = []
        for link in links:
            hostname = urlparse(link.url).hostname or ""
            if hostname not in ("example.com", "www.example.com"):
                continue
            if self.EXCLUDED_PATTERNS.search(link.url):
                continue
            filtered.append(link)
        return filtered

Je kunt filteren op URL-patronen, queryparameters, responsheaders, pagina-inhoud of elke combinatie.

Pauzeren en Hervatten

Essentieel

Voor crawls van meer dan 1.000 pagina’s, schakel pauzeren/hervatten in met JOBDIR:

scrapy crawl myspider -o output.json -s JOBDIR=crawl_state

Scrapy slaat de status op in crawl_state/. Druk op Ctrl+C om te pauzeren. Voer dezelfde opdracht uit om te hervatten.

Gebruik altijd JOBDIR voor productiecrawls. Beschermt tegen netwerkproblemen, systeemherstarts, of gewoon moeten stoppen voor de dag.

Status omvat wachtende URL’s, geziene URL’s en de verzoekwachtrij. Dit is robuuster dan Screaming Frog’s opslaan/laden functie omdat het bestandsgebaseerd is en systeemherstarts overleeft.

JavaScript Rendering

Scrapy haalt alleen ruwe HTML op. Het rendert geen JavaScript. Dit is hetzelfde als wat curl retourneert.

Voor de meeste SEO crawls is dit prima:

  • Meta tags, canonicals en h1’s staan meestal in de initiële HTML
  • Zoekmachines indexeren voornamelijk server-gerenderde content
  • De meeste e-commerce en content sites zijn server-gerenderd

Als je doelsite content client-side rendert, heb je opties:

Pakket Opmerkingen
scrapy-playwright Gebruikt Chromium/Firefox/WebKit. Aanbevolen voor moderne JS sites
scrapy-splash Lichtgewicht, Docker-gebaseerde renderer
scrapy-selenium Oudere aanpak, werkt nog steeds

JS rendering is aanzienlijk langzamer en resource-intensiever. Voeg het alleen toe als de site het vereist.

Screaming Frog heeft een vergelijkbare afweging. JavaScript rendering inschakelen gebruikt Chrome onder de motorkap en vertraagt crawls aanzienlijk.

Geheugenbeheer

Bij ~1.300 pagina’s met volledige veldextractie:

  • Geheugen: ~265 MB
  • CPU: ~4%

JOBDIR gebruiken verplaatst verzoekwachtrijen naar schijf, wat geheugen laag houdt. Voor zeer grote crawls (100k+ URL’s), voeg deze instellingen toe:

MEMUSAGE_LIMIT_MB = 1024
MEMUSAGE_WARNING_MB = 800
SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'

Dit beperkt geheugengebruik en forceert schijf-gebackte wachtrijen voor de scheduler.

Output Data

Aanpasbaar

Basis spider output:

{
    "url": "https://www.example.com/page/",
    "title": "Paginatitel Hier",
    "status": 200
}

Voor SEO crawls wil je velden vergelijkbaar met wat Screaming Frog exporteert:

def parse_page(self, response):
    yield {
        "url": response.url,
        "status": response.status,
        "title": response.css("title::text").get(),
        "meta_description": response.css("meta[name='description']::attr(content)").get(),
        "meta_robots": response.css("meta[name='robots']::attr(content)").get(),
        "h1": response.css("h1::text").get(),
        "canonical": response.css("link[rel='canonical']::attr(href)").get(),
        "og_title": response.css("meta[property='og:title']::attr(content)").get(),
        "og_description": response.css("meta[property='og:description']::attr(content)").get(),
        "word_count": len(response.text.split()) if response.status == 200 else None,
        "content_type": response.headers.get("Content-Type", b"").decode("utf-8", errors="ignore"),
    }

Voeg velden toe of verwijder ze op basis van wat je nodig hebt. CSS selectors werken voor elk on-page element.

Export formaten: JSON (-o output.json), JSON Lines (-o output.jsonl), CSV (-o output.csv), XML (-o output.xml).

JSON Lines is het beste voor grote crawls. Bestanden zijn regel voor regel geldig tijdens de crawl, dus je kunt monitoren met tail -f. Standaard JSON is pas geldig als de crawl compleet is.

Screaming Frog → Scrapy

Vertaalgids

SF workflows naar Scrapy mappen:

Screaming Frog Actie Scrapy Equivalent
Nieuwe crawl starten scrapy crawl spidername
Crawlvertraging instellen DOWNLOAD_DELAY in instellingen
Gelijktijdige threads beperken CONCURRENT_REQUESTS_PER_DOMAIN
robots.txt respecteren ROBOTSTXT_OBEY = True
Exporteren naar CSV -o output.csv
Crawl opslaan/laden -s JOBDIR=crawl_state
Subdomeinen filteren Code in spider (regex)
Aangepaste extractie CSS/XPath selectors in parse()

Mindsetveranderingen:

  1. Configuratie is code. Bewerk settings.py in plaats van checkboxen aan te vinken.
  2. Extractie is expliciet. Je schrijft welke data te vangen.
  3. Planning is native. Voeg commando’s toe aan cron of CI/CD.
  4. Debugging is logs. Schakel AUTOTHROTTLE_DEBUG in om te zien wat er gebeurt.

Volledige Workflow

Met de standaardinstellingen hierboven kun je Scrapy geïnstalleerd en crawlend hebben in minder dan 15 minuten:

python3 -m venv venv
source venv/bin/activate  # venv\Scripts\activate op Windows
pip install scrapy
scrapy startproject urlcrawler
cd urlcrawler
scrapy genspider mysite example.com
# Bewerk settings.py met beleefde crawl config
# Bewerk spiders/mysite.py met je parse logica
scrapy crawl mysite -o urls.jsonl -s JOBDIR=crawl_state

Scrapy Shell

Terwijl je aangepaste configuraties bouwt, gebruik Scrapy Shell om je selectors en instellingen interactief te testen:

scrapy shell "https://example.com"

Dit opent een interactieve Python console met de response al geladen. Test CSS en XPath selectors in realtime voordat je ze aan je spider toevoegt:

>>> response.css('title::text').get()
'Example Domain'
>>> response.xpath('//h1/text()').get()
'Example Domain'

Scrapy Shell vermindert iteratietijd aanzienlijk. Valideer extractielogica zonder volledige crawls uit te voeren.

Complete Spider Template

Een productie-klare spider met URL filtering, statuscode afhandeling en volledige SEO veldextractie:

import re
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from urllib.parse import urlparse


class SEOSpider(CrawlSpider):
    name = "seospider"
    allowed_domains = ["example.com"]
    start_urls = ["https://www.example.com"]

    # Capture all HTTP status codes, not just 2xx
    handle_httpstatus_list = [200, 301, 302, 403, 404, 500, 502, 503]

    # URL patterns to exclude
    EXCLUDED_PATTERNS = re.compile(
        r"/(in|au|th|es|hk|sg|ph|my|ca|cn|uk|kr|id|fr|vn|de|jp|nl|it|tw)/"
    )

    rules = (
        Rule(
            LinkExtractor(allow=()),
            callback="parse_page",
            follow=True,
            process_links="filter_links",
        ),
    )

    def filter_links(self, links):
        filtered = []
        for link in links:
            parsed = urlparse(link.url)
            hostname = parsed.hostname or ""

            if hostname not in ("example.com", "www.example.com"):
                continue

            if self.EXCLUDED_PATTERNS.search(link.url):
                continue

            filtered.append(link)
        return filtered

    def parse_page(self, response):
        yield {
            "url": response.url,
            "status": response.status,
            "title": response.css("title::text").get(),
            "meta_description": response.css("meta[name='description']::attr(content)").get(),
            "meta_robots": response.css("meta[name='robots']::attr(content)").get(),
            "h1": response.css("h1::text").get(),
            "canonical": response.css("link[rel='canonical']::attr(href)").get(),
            "og_title": response.css("meta[property='og:title']::attr(content)").get(),
            "og_description": response.css("meta[property='og:description']::attr(content)").get(),
            "word_count": len(response.text.split()) if response.status == 200 else None,
            "content_type": response.headers.get("Content-Type", b"").decode("utf-8", errors="ignore"),
        }

Vervang example.com met je doeldomein. Pas EXCLUDED_PATTERNS aan voor de URL-structuur van je site.

Wanneer Welke Gebruiken

Screaming Frog:

Scrapy:

  • Sites van meer dan 10.000 URLs
  • Geautomatiseerde, geplande crawls
  • Aangepaste extractiebehoeften
  • CI/CD integratie
  • Geheugenbeperkingen
  • Versiebeheerde configs

De Conclusie

Scrapy heeft een steilere setupcurve dan Screaming Frog, maar het verwijdert de praktische limieten die GUI crawlers opleggen. Geen URL-plafonds, geen licentiekosten, lager geheugengebruik en native automatisering.

Begin klein. Crawl een site die je kent. Gebruik conservatieve instellingen. Vergelijk output met Screaming Frog. De data zal overeenkomen, maar je hebt een tool die schaalt.