requests + BeautifulSoup vs. Scraping-API: wann DIY reicht
Für statische Seiten ohne Anti-Bot kosten requests und BeautifulSoup nichts und funktionieren gut. Hier ist genau der Punkt, ab dem das nicht mehr stimmt.

Es gibt eine Version dieses Posts, die versucht, dir eine API für ein Problem zu verkaufen, das requests und BeautifulSoup schon lösen. Das hier ist nicht dieser Post. Wenn die Seite, die du scrapst, statisches HTML ohne Anti-Bot davor ist, kostet der DIY-Weg nichts und funktioniert gut, und dafür solltest du nichts bezahlen.
Wann DIY wirklich reicht
Sag es klar: Für eine statische Seite, ein Skript und einen seltenen Job ist das schon die ganze Lösung.
import requests
from bs4 import BeautifulSoup
resp = requests.get("https://example.com/products", timeout=30)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
for card in soup.select(".product-card"):
name = card.select_one(".product-name").get_text(strip=True)
price = card.select_one(".price").get_text(strip=True)
print(name, price)
Keine Abhängigkeiten über zwei bekannte Bibliotheken hinaus, keine Proxy-Rechnung, keine Credits verbraucht. Das ist richtig für: eine Seite, die vollständiges HTML serverseitig rendert, kein CAPTCHA oder IP-basiertes Blocking nach ein paar Anfragen, und einen Job, den du gelegentlich statt dauerhaft gegen Tausende URLs laufen lässt. Ein einmaliges Skript für ein Uniprojekt, ein internes Tool, das ein paar Seiten am Tag prüft, eine private Preisbeobachtung für ein paar Produktseiten: Das ist alles DIY-Territorium, und eine API davorzuschalten ist reiner Overhead.
Wo es aufhört zu funktionieren
Vier Dinge bringen dieses Setup zum Kippen, und sie brechen unabhängig voneinander, weshalb “einfach Proxys dazu” oder “einfach ein wait() dazu” immer nur ein Symptom kaschiert:
Anti-Bot. Cloudflare-Challenges, PerimeterX, Rate-Limiting, das nach ein paar Anfragen zu einem CAPTCHA oder einem glatten 403 eskaliert. requests hat keinen Browser-Fingerprint, kein Cookie-Verhalten, das einer echten Session entspricht, und keine Möglichkeit, eine Challenge zu lösen. Das ist kein Header-Problem, das du durch Kopieren eines User-Agent-Strings löst; Seiten, die darauf achten, erkennen gezielt das Fehlen eines echten Browsers.
JS-Rendering. Wenn die Daten, die du willst, erst nach dem initialen HTML-Load per JavaScript eingefügt werden (eine React- oder Vue-App, Inhalte über einen XHR-Call im Hintergrund, Infinite Scroll), steht das schlicht nicht in resp.text. BeautifulSoup parst, was der Server geschickt hat, nicht was der Browser rendern würde. Das ist der häufigste einzelne Grund, warum ein funktionierender Scraper über Nacht leere Ergebnisse liefert: Die Seite hat nicht ihr Blocking geändert, sie hat ihr Rendering geändert.
Skalierung. Ein Dutzend Seiten am Tag ist ein Skript. Zehntausend Seiten am Tag sind Infrastruktur: Concurrency-Management, Retry-Logik, ein auf das Anfragevolumen abgestimmter Proxy-Pool, Monitoring gegen steigende Bann-Raten. Nichts davon ist einzeln schwer, aber zusammen ist es ein kleines Stück Infrastruktur, das jetzt dir gehört und am Laufen gehalten werden muss.
Wartung. Selektoren verrotten. Ein Redesign macht aus .product-card ein .product-tile, und dein Parser liefert stillschweigend nichts zurück, oder schlimmer, das falsche Feld unter dem richtigen Namen. Das ist der Preis, der sich in der ersten Woche nicht zeigt und nie ganz verschwindet.
Die Entscheidungstabelle
| Signal | DIY (requests + BeautifulSoup) | Scraping-API |
|---|---|---|
| Statisches HTML, kein Blocking, geringes Volumen | Ja | Übertrieben |
| Gelegentlicher / einmaliger Job | Ja | Übertrieben |
| Seite liefert 403/CAPTCHA nach ein paar Anfragen | Nein | Ja (premium=true) |
| Inhalt wird nach dem Laden per JavaScript nachgeladen | Nein | Ja (dynamic=true) |
| Tausende Seiten, unbeaufsichtigt, nach Zeitplan | Mühsam zu betreiben | Ja |
| Parser muss Redesigns überstehen, ohne dass du es merkst | Keine Garantie so oder so | Strukturierte JSON-Felder reduzieren das (beheben es nicht) |
Wenn du die Grenze überschreitest
Bei FastAF kostet /scrape 1 Credit für eine statische Seite, genau der Fall oben, nur ohne dass du die Anfrage selbst betreibst:
import requests
API_KEY = "faf_live_..."
resp = requests.get(
"https://api.fastaf.co/v1/scrape",
params={"url": "https://example.com/products"},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=60,
)
resp.raise_for_status()
Derselbe Call mit dynamic=true (5 Credits) rendert die Seite zuerst mit einem echten Browser, sodass per JavaScript eingefügte Inhalte in der Antwort landen. premium=true (10 Credits) leitet die Anfrage über ein residentielles Netzwerk für Seiten, die nach IP-Reputation blocken. Beides zusammen (25 Credits) deckt eine JS-gerenderte Seite ab, die zusätzlich wie ein echter residentieller Besucher aussehen muss, die schwierigste und teuerste Kombination, und die solltest du nur einsetzen, wenn die Seite es wirklich verlangt.
Der Sinn der Credit-Stufen ist, dass du genau für das Problem zahlst, das du hast. Eine statische Seite muss nie mehr als 1 Credit kosten, und für premium=true auf einer Seite ohne Anti-Bot zu zahlen, ist Geld für ein Problem, das du gar nicht hast.
Kurzversion
Starte mit requests und BeautifulSoup. Das ist kostenlos und reicht für einen großen Teil der Scraping-Aufgaben. Greife gezielt zu einer API, wenn du auf einen der vier Fehlermodi oben triffst, Anti-Bot, JS-Rendering, Skalierung oder eine Wartungslast, die du nicht besitzen willst, und zahle pro URL genau für dieses Problem, statt einen Proxy-Pool und eine Parser-Wartungsgewohnheit drumherum aufzubauen. Der Free Tier gibt dir 500 Credits ohne Karte, wenn du testen willst, wo deine tatsächlichen Ziele in dieser Tabelle landen.