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?
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 |
Installatie
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
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
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"
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
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 |
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
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
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.
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
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
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:
- Configuratie is code. Bewerk
settings.pyin plaats van checkboxen aan te vinken. - Extractie is expliciet. Je schrijft welke data te vangen.
- Planning is native. Voeg commando’s toe aan cron of CI/CD.
- Debugging is logs. Schakel
AUTOTHROTTLE_DEBUGin 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:
- Snelle audits onder 500 URLs
- Resultaten nodig binnen minuten
- Visuele site-exploratie
- Niet comfortabel met CLI
- Screaming Frog-data gebruiken met Redirects.net
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.