Web Scraping verwandelt öffentliche Webseiten in strukturierte Daten, die Sie auswerten können, und der Parsing-Schritt entscheidet darüber, wie sauber diese Daten herauskommen. Python bietet mehrere Parsing-Bibliotheken, doch Parsel sticht heraus, weil es klein und schnell ist und um die beiden Selektorsprachen herum gebaut wurde, die die meisten Scraper ohnehin schon kennen: XPath und CSS. Es ist dieselbe Engine, die Scrapy antreibt, und sie funktioniert genauso gut für sich allein, wenn Sie rohes HTML zur Hand haben und in wenigen lesbaren Zeilen Felder daraus herausziehen möchten.
Dieser Leitfaden ist eine lauffähige Schritt-für-Schritt-Anleitung. Sie installieren Parsel, rufen eine gerenderte Seite über die Crawling API ab, laden das HTML in einen Selector und extrahieren Daten sowohl mit XPath als auch mit CSS über .get() und .getall(). Von dort aus iterieren Sie über eine Liste von Elementen, lesen Text und Attribute aus, bereinigen die Werte und exportieren das Ergebnis nach JSON und CSV. Das Beispielziel ist books.toscrape.com, eine öffentliche Sandbox, die eigens zum Üben von Scraping gebaut wurde, sodass Sie jeden Codeausschnitt von Anfang bis Ende ausführen können, ohne eine echte Produktivseite anzufassen.
Was Sie bauen werden
Ein kleines Python-Skript, das eine Katalogseite abruft, aus dem zurückgegebenen HTML einen Parsel-Selector aufbaut, über die Produktkarten iteriert und pro Element einen strukturierten Datensatz extrahiert. Aus jeder Buchkarte ziehen wir diese Felder heraus:
- Titel der Buchtitel, ausgelesen aus einem Link-Attribut.
- Preis der gelistete Preis, bereinigt zu einer Zahl.
- Verfügbarkeit der auf der Karte angezeigte Lagerbestand-Text.
- Bewertung die Sternebewertung, ausgelesen aus einer CSS-Klasse.
- Link die absolute URL der Detailseite des Buches.
Warum Parsel für das Parsing in Python
Parsel ist eine eigenständige Selektor-Bibliothek. Sie übergeben ihr eine HTML-Zeichenkette, sie baut daraus einen Baum auf, und Sie fragen diesen Baum mit XPath- oder CSS-Ausdrücken ab. Es nimmt einen nützlichen Mittelweg ein: leichter als ein vollständiges Framework wie Scrapy und stärker selektorgetrieben als BeautifulSoup, das statt auf Selektor-Zeichenketten auf Python-Methodenverkettung setzt. Die Gründe, warum es einen Platz im Scraping-Werkzeugkasten verdient, sind unkompliziert:
- Zwei Selektorsprachen. Nutzen Sie XPath, wenn Sie durch die Struktur navigieren oder auf Text abgleichen müssen, und CSS, wenn ein kurzer Klassen- oder Tag-Selektor klarer lesbar ist. Parsel unterstützt beide auf demselben Objekt.
- Klein und schnell. Es baut auf lxml auf, sodass das Parsen großer Dokumente flott bleibt, und es ist kaum mehr Einrichtung nötig als der Import einer einzigen Klasse.
-
Saubere Syntax.
.get()gibt den ersten Treffer zurück,.getall()gibt jeden Treffer zurück, und verkettete Selektoren halten den Extraktionscode kurz und leicht wartbar.
Für eine tiefergehende Referenz zu den Selektorsprachen selbst behandelt der Beitrag über XPath- und CSS-Selektoren die Syntax im Detail. Hier konzentrieren wir uns darauf, sie mit Parsel zum Einsatz zu bringen.
Warum über die Crawling API abrufen
Parsel parst HTML; es ruft keine Seiten ab. Sie brauchen weiterhin etwas, das das Markup zuerst beschafft, und dieser Abrufschritt ist die Stelle, an der die meisten Scraper in Schwierigkeiten geraten. Eine nackte HTTP-Anfrage funktioniert bei einer einfachen statischen Seite einwandfrei, doch viele moderne Websites rendern ihre Inhalte mit JavaScript, sodass die rohe Antwort eine dünne Hülle ist, in der die eigentlichen Daten fehlen. Andere achten auf automatisierten Datenverkehr und drosseln oder blockieren Anfragen, die nicht wie ein echter Browser aussehen.
Der Abruf über die Crawling API löst beide Probleme in einem einzigen Aufruf. Sie senden ihr eine URL, sie rendert die Seite bei Bedarf, leitet die Anfrage über eine vertrauenswürdige rotierende IP und gibt fertiges HTML zurück, das Sie direkt in einen Parsel-Selector einspeisen. So bleiben die Abruf-Belange (Rendering, Rotation, Blockierung) von den Parse-Belangen (Selektoren, Felder) getrennt, und genau diese Trennung hält einen Scraper wartbar.
Voraussetzungen
Python-Grundkenntnisse. Sie sollten damit vertraut sein, ein Skript auszuführen und Pakete mit pip zu installieren. Vorkenntnisse in Parsel sind nicht erforderlich; dieser Leitfaden führt die API nach und nach ein.
Python 3.8 oder neuer. Prüfen Sie Ihre Version mit python --version. Falls Sie kein Python haben, installieren Sie es von python.org und stellen Sie sicher, dass es in Ihrem PATH liegt.
Ein Crawlbase-Konto und Token. Melden Sie sich an, öffnen Sie Ihr Dashboard und kopieren Sie Ihren Request-Token. Crawlbase enthält zum Start bis zu 20.000 kostenlose Anfragen, was mehr als genug ist, um diesen Leitfaden durchzuarbeiten. Behandeln Sie den Token wie ein Passwort und halten Sie ihn aus der Versionsverwaltung heraus.
Das Projekt einrichten
Erstellen Sie eine virtuelle Umgebung, damit die Abhängigkeiten des Projekts isoliert bleiben, und installieren Sie dann die beiden Bibliotheken, die das Skript benötigt.
python --version python -m venv parsel_env source parsel_env/bin/activate pip install parsel crawlbase
Unter Windows aktivieren Sie die Umgebung mit parsel_env\Scripts\activate anstelle der source-Zeile. parsel übernimmt die Extraktion, und crawlbase ist der offizielle Client, der gerenderte Seiten für Sie abruft. Die Module json und csv sind Teil der Standardbibliothek, sodass für den Exportschritt nichts weiter zu installieren ist.
Schritt 1: Eine Seite abrufen und einen Selector aufbauen
Beginnen Sie damit, eine Katalogseite über die Crawling API abzurufen und ihr HTML in einen Parsel-Selector zu laden. Importieren Sie CrawlingAPI, initialisieren Sie ihn mit Ihrem Token, fordern Sie die URL an und prüfen Sie den cb_status (legacy pc_status)-Header, bevor Sie parsen, damit Fehler sichtbar bleiben statt stillschweigend aufzutreten.
from crawlbase import CrawlingAPI from parsel import Selector api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def fetch_html(page_url): response = api.get(page_url) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None if __name__ == "__main__": url = "https://books.toscrape.com/catalogue/page-1.html" html = fetch_html(url) if html: selector = Selector(text=html) print(selector.xpath("//title/text()").get())
Selector(text=html) ist der Einstiegspunkt zu allem, was folgt: Es parst die Zeichenkette einmal und gibt Ihnen ein Objekt, das Sie mit .xpath() und .css() abfragen. Die letzte Zeile liest den Seitentitel mit einem XPath-Ausdruck, wobei /text() den Textknoten auswählt und .get() den ersten Treffer als Zeichenkette zurückgibt. Führen Sie die Datei aus, und Sie sollten den Titel der Katalogseite ausgegeben sehen, was bestätigt, dass Abruf und Parsing beide funktionieren, bevor Sie einen einzigen Feldselektor schreiben.
Der fetch_html-Schritt oben ist genau der Teil, den Parsel nicht allein leisten kann, und bei einem echten Ziel ist das die Stelle, an der Rendering und Blockierung schwierig werden. Die Crawling API nimmt Ihren Token, rendert JavaScript-Seiten bei Bedarf, rotiert serverseitig durch Residential-IPs und liefert fertiges HTML zurück, sodass Sie es direkt in einen Selector einspeisen können, ohne selbst eine Flotte von Headless-Browsern oder einen Proxy-Pool zu betreiben. Beginnen Sie im kostenlosen Tarif mit bis zu 20.000 Anfragen.
Schritt 2: Mit XPath und CSS extrahieren
Parsel lässt Sie denselben Selector mit beiden Sprachen abfragen. XPath steht für XML Path Language und navigiert den Dokumentbaum anhand der Struktur, während CSS-Selektoren Elemente nach Tag, Klasse oder id ansprechen, so wie es ein Stylesheet tut. Die beiden folgenden Beispiele ziehen dieselbe Art von Wert heraus, sodass Sie die Stile direkt vergleichen können.
# XPath: select the text of the first h1 heading = selector.xpath("//h1/text()").get() # CSS: select the text inside a known element price = selector.css("p.price_color::text").get() # Attributes: @attr in XPath, ::attr() in CSS link_xpath = selector.xpath("//article//h3/a/@href").get() link_css = selector.css("article h3 a::attr(href)").get()
Zwei Muster tragen den Großteil der Arbeit. Um Text zu lesen, verwenden Sie /text() in XPath oder ::text in CSS. Um ein Attribut wie href oder src zu lesen, verwenden Sie @attribute in XPath oder ::attr(attribute) in CSS. In jedem Fall gibt .get() den ersten Treffer zurück oder None, wenn nichts passt, sodass ein fehlendes Element keinen Fehler auslöst.
.get() gibt den ersten passenden Wert als Zeichenkette zurück. .getall() gibt eine Liste mit jedem Treffer zurück. Greifen Sie zu .get(), wenn Sie einen einzelnen Wert wie einen Preis erwarten, und zu .getall(), wenn Sie eine ganze Spalte möchten, etwa jeden Titel auf der Seite.
Schritt 3: Über eine Liste von Elementen iterieren
Echte Seiten enthalten viele wiederholte Elemente, nicht nur eines. Das Muster besteht darin, den sich wiederholenden Container einmal auszuwählen und dann zu iterieren, wobei man gegen jedes Element gescopte Selektoren laufen lässt, um pro Element einen Datensatz aufzubauen. In der Bücher-Sandbox ist jedes Produkt ein <article class="product_pod">, also ist das der Container, über den wir iterieren.
def parse_books(selector): books = [] for card in selector.css("article.product_pod"): title = card.css("h3 a::attr(title)").get() price = card.css("p.price_color::text").get() availability = card.css("p.instock.availability::text").getall() rating = card.css("p.star-rating::attr(class)").get() href = card.css("h3 a::attr(href)").get() books.append({ "title": title, "price": price, "availability": availability, "rating": rating, "href": href, }) return books
Der Aufruf von .css("article.product_pod") gibt eine SelectorList zurück, über die Sie iterieren können; jede card ist selbst ein Selector, sodass die inneren .css()-Aufrufe nur gegen diese eine Karte laufen. Der Titel steckt im title-Attribut des Links, der Preis in einem price_color-Absatz, und die Bewertung in einer Klasse wie star-rating Three, weshalb wir das gesamte class-Attribut auslesen und es im nächsten Schritt bereinigen. Das Feld availability verwendet .getall(), weil sein Text über Whitespace-Knoten verteilt ist; sie zusammenzufügen und zu trimmen ergibt eine einzige saubere Zeichenkette.
Schritt 4: Die Werte bereinigen und normalisieren
Die rohe Selektorausgabe braucht in der Regel einen leichten Durchgang, bevor sie nützlich ist. Preise tragen ein Währungssymbol, die Bewertung kommt als zweiteilige Klasse zurück, und der Verfügbarkeitstext trifft mit umgebendem Whitespace ein. Ein paar Standard-Stringoperationen verwandeln jeden davon in einen sauberen Wert.
BASE = "https://books.toscrape.com/catalogue/" WORDS = {"One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5} def clean_book(card): price_text = card.css("p.price_color::text").get(default="") price = float(price_text.replace("£", "").strip() or 0) rating_class = card.css("p.star-rating::attr(class)").get(default="") rating_word = rating_class.replace("star-rating", "").strip() rating = WORDS.get(rating_word) stock = " ".join(card.css("p.instock.availability::text").getall()) href = card.css("h3 a::attr(href)").get(default="") return { "title": card.css("h3 a::attr(title)").get(), "price": price, "availability": stock.strip(), "rating": rating, "link": BASE + href, }
Zwei kleine Gewohnheiten machen diesen Code robust. Erstens liefert .get(default="") einen Fallback, sodass ein fehlendes Element eine leere Zeichenkette statt None ergibt, was die nachgelagerten Aufrufe von .replace() und .strip() davon abhält, einen Fehler zu werfen. Zweitens entfernt der Preis-Parser das Währungssymbol (die £-Escape-Sequenz ist das Pfundzeichen) und konvertiert in float, sodass der Wert als Zahl sortiert und gefiltert wird. Die Bewertung bildet das Wort in der Klasse auf eine Ganzzahl ab, und der relative href wird an die Basis-URL angehängt, um einen absoluten Link zu erzeugen.
Schritt 5: Das vollständige Skript zusammensetzen
Verdrahten Sie nun die Teile zu einem lauffähigen Skript: die Seite abrufen, den Selector aufbauen, über die Karten mittels clean_book iterieren und die Datensätze sowohl nach JSON als auch nach CSV exportieren.
import csv import json from crawlbase import CrawlingAPI from parsel import Selector api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) BASE = "https://books.toscrape.com/catalogue/" WORDS = {"One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5} def fetch_html(page_url): response = api.get(page_url) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None def clean_book(card): price_text = card.css("p.price_color::text").get(default="") price = float(price_text.replace("£", "").strip() or 0) rating_class = card.css("p.star-rating::attr(class)").get(default="") rating = WORDS.get(rating_class.replace("star-rating", "").strip()) stock = " ".join(card.css("p.instock.availability::text").getall()) href = card.css("h3 a::attr(href)").get(default="") return { "title": card.css("h3 a::attr(title)").get(), "price": price, "availability": stock.strip(), "rating": rating, "link": BASE + href, } def parse_books(html): selector = Selector(text=html) return [clean_book(card) for card in selector.css("article.product_pod")] def save_outputs(records): with open("books.json", "w") as f: json.dump(records, f, indent=2) if not records: return with open("books.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=records[0].keys()) writer.writeheader() writer.writerows(records) def main(): url = "https://books.toscrape.com/catalogue/page-1.html" html = fetch_html(url) if not html: return records = parse_books(html) save_outputs(records) print(f"Saved {len(records)} books") if __name__ == "__main__": main()
parse_books baut den Selector einmal auf und gibt über eine List Comprehension über die Karten eine Liste bereinigter Datensätze zurück. save_outputs schreibt eine JSON-Datei und eine CSV, die die Schlüssel des ersten Datensatzes als Kopfzeile verwendet, sodass Sie die Daten in der Form bekommen, die Ihr nachgelagertes Werkzeug verlangt. Um den gesamten Katalog abzudecken, hüllen Sie main in eine Schleife über page-1.html bis page-50.html und erweitern eine kombinierte Liste; die Parse-Logik ändert sich dabei nicht.
Wie die Ausgabe aussieht
Führen Sie das Skript mit python books_scraper.py aus, und Sie erhalten pro Buch einen sauberen strukturierten Datensatz, bereit für die Analyse, eine Datenbank oder eine Tabellenkalkulation.
[ { "title": "A Light in the Attic", "price": 51.77, "availability": "In stock", "rating": 3, "link": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html" }, { "title": "Tipping the Velvet", "price": 53.74, "availability": "In stock", "rating": 1, "link": "https://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html" } ]
Die zugehörige CSV trägt dieselben Spalten, eine Zeile pro Buch, was direkt in pandas oder eine beliebige Tabellenkalkulation einfließt, um nach Preis zu sortieren oder nach Bewertung zu filtern.
Häufige Fehler, die man vermeiden sollte
Ein paar Gewohnheiten unterscheiden einen Scraper, der standhält, von einem, der beim nächsten Lauf zerbricht.
- Untersuchen Sie die Seite, bevor Sie Selektoren schreiben. Öffnen Sie die Seite in den Entwicklertools Ihres Browsers und bestätigen Sie die Klassennamen und die Struktur. Ein Selektor, der auf ein nicht existierendes Element zielt, liefert nichts zurück, und das ist der häufigste Grund, warum ein Scrape leer zurückkommt.
-
Behandeln Sie fehlende Daten stets. Verwenden Sie
.get(default="")oder sichern Sie gegenNoneab, damit ein einzelnes fehlendes Feld nicht die gesamte Schleife zum Absturz bringt. Seiten sind selten so einheitlich, wie sie aussehen. -
Text trimmen und normalisieren. Web-Text trägt verirrten Whitespace und Währungssymbole. Bereinigen Sie ihn beim Parsen mit
.strip()und.replace(), damit Ihre gespeicherten Werte konsistent sind. - Drosseln Sie Ihre Anfragen. Seiten in einer engen Schleife abzurufen ist der schnellste Weg, gedrosselt zu werden. Fügen Sie zwischen den Anfragen eine kurze Verzögerung ein und halten Sie Ihr Volumen vernünftig.
Verantwortungsvoll scrapen
Parsel parst nur HTML, das Sie bereits besitzen, aber wie Sie an dieses HTML gelangen, spielt dennoch eine Rolle. Ein paar Grundsätze halten jedes Scraping-Projekt auf der richtigen Seite der Grenze, ganz gleich, was das Ziel ist.
Prüfen Sie die Nutzungsbedingungen der Website und ihre robots.txt, bevor Sie irgendetwas sammeln, und behandeln Sie beide als Grenzen statt als Empfehlungen. Bleiben Sie bei öffentlichen Daten, die jeder Besucher ohne Anmeldung sehen kann, und halten Sie Ihre Anfragerate vernünftig, damit Sie die Server der Website nicht belasten. Wenn ein Projekt personenbezogene Daten berührt, wachsen die Pflichten: Vorschriften wie die DSGVO und der CCPA regeln, wie personenbezogene Informationen erhoben und genutzt werden dürfen, gehen Sie solche Fälle also mit besonderer Sorgfalt an oder vermeiden Sie sie ganz. Das Beispiel hier verwendet eine zum Üben gebaute Sandbox, gerade damit Sie die Mechanik erlernen können, ohne sich um diese Belange zu sorgen, und dieselbe Disziplin überträgt sich, wenn Sie Ihren Scraper auf eine echte Website richten. Mehr dazu, wie man innerhalb der Grenzen einer Website agiert, finden Sie unter wie man Websites scrapt, ohne blockiert zu werden.
Wichtigste Erkenntnisse
-
Parsel ist selektor-orientiert. Bauen Sie einen
Selector(text=html)auf und fragen Sie ihn mit XPath oder CSS ab, je nachdem, was sich für das jeweilige Element klarer liest. -
get und getall decken den Großteil der Extraktion ab.
.get()gibt den ersten Treffer als Zeichenkette zurück,.getall()gibt jeden Treffer als Liste zurück, und.get(default="")verhindert, dass fehlende Felder den Lauf zum Absturz bringen. -
Text und Attribute haben ein festes Muster. Lesen Sie Text mit
/text()oder::textund Attribute mit@attroder::attr(), in XPath bzw. CSS. - Iterieren Sie über einen Container, nicht über die ganze Seite. Wählen Sie das sich wiederholende Element einmal aus, lassen Sie dann gegen jedes Element gescopte Selektoren laufen, um pro Element einen sauberen Datensatz aufzubauen, und exportieren Sie nach JSON und CSV.
- Trennen Sie Abruf von Parsing. Lassen Sie die Crawling API Rendering, Rotation und Blockierung übernehmen und übergeben Sie dann das fertige HTML an Parsel, damit Ihr Extraktionscode einfach bleibt.
Häufig gestellte Fragen
Was ist Parsel und warum sollte man es für Web Scraping verwenden?
Parsel ist eine kleine, schnelle Python-Bibliothek zum Extrahieren von Daten aus HTML und XML mithilfe von XPath- und CSS-Selektoren. Es ist dieselbe Selektor-Engine, die Scrapy verwendet, und es eignet sich gut als eigenständiges Werkzeug, wenn Sie das HTML bereits haben und Felder daraus herausziehen möchten. Man wählt es wegen der sauberen Syntax, der Unterstützung beider Selektorsprachen auf demselben Objekt und der Leichtigkeit, mit der es sich in eine bestehende Pipeline einfügt.
Was ist der Unterschied zwischen Parsel und BeautifulSoup?
Beide parsen HTML, aber sie unterscheiden sich im Stil. Parsel ist selektorgetrieben: Sie schreiben XPath- oder CSS-Ausdrücke und rufen .get() oder .getall() auf. BeautifulSoup setzt auf Python-Methodenverkettung wie find und find_all. Parsel unterstützt zudem XPath nativ, was BeautifulSoup nicht tut. Wählen Sie, was dazu passt, wie Sie Ihre Auswahlen ausdrücken möchten.
Was ist der Unterschied zwischen get und getall in Parsel?
.get() gibt den ersten passenden Wert als Zeichenkette zurück oder None, wenn nichts passt. .getall() gibt eine Liste mit jedem passenden Wert zurück. Verwenden Sie .get() für ein einzelnes Feld wie einen Preis oder einen Titel und .getall(), wenn Sie eine ganze Menge möchten, etwa jeden Link auf einer Seite. Die Übergabe von .get(default="value") liefert einen Fallback für fehlende Elemente.
Wie gehe ich mit Seiten um, die Inhalte mit JavaScript laden?
Parsel parst, welches HTML Sie ihm auch immer geben, die Frage ist also, wie Sie dieses HTML abrufen. Wenn eine Seite ihre Inhalte mit JavaScript rendert, gibt eine rohe Anfrage eine dünne Hülle zurück, in der die Daten fehlen. Der Abruf über die Crawling API rendert die Seite zuerst und gibt fertiges HTML zurück, das Sie dann genau so in einen Selector laden, wie hier gezeigt. Der Parsing-Code ändert sich dabei nicht.
Kann ich Parsel-Ergebnisse nach JSON oder CSV exportieren?
Ja. Parsel gibt Ihnen reine Python-Werte zurück, sodass Sie, sobald Sie eine Liste von Dictionaries aufgebaut haben, JSON mit dem standardmäßigen json-Modul und CSV mit csv.DictWriter schreiben, wie es das vollständige Skript tut. Von dort fließen die Daten ohne weitere Konvertierung in pandas oder eine Datenbank ein.
Warum Parsel mit der Crawling API statt einer einfachen Anfrage verwenden?
Eine einfache Anfrage scheitert oft, bevor Parsel überhaupt läuft: Die Seite rendert möglicherweise clientseitig, oder die Website blockiert vielleicht Datenverkehr, der nicht wie ein echter Browser aussieht. Die Crawling API übernimmt Rendering, IP-Rotation und CAPTCHA-Herausforderungen und gibt dann sauberes HTML zurück. Das hält die Abruf-Belange aus Ihrem Parsing-Code heraus, sodass Parsel sich auf das eine konzentrieren kann, was es gut beherrscht, nämlich HTML in strukturierte Felder zu verwandeln.
Crawlen Sie jede Website im großen Maßstab, ohne gegen die Infrastruktur zu kämpfen.
Crawlbase übernimmt Proxys, Fingerprints und CAPTCHAs, damit Ihr Team Datenpipelines ausliefert, statt Crawl-Infrastruktur zu pflegen. 1.000 Anfragen kostenlos, keine Karte erforderlich.
