Allgemeine Spider-Vorlagen¶
Die meisten Crawls folgen einem von zwei Mustern: „Links verfolgen, die zu diesem Muster passen“ oder „alle URLs aus der Sitemap abrufen“. Scrapling bietet Vorlagen für beide. So müssen Sie nicht jedes Mal denselben Grundaufbau für parse() schreiben.
CrawlSpider und SitemapSpider bauen auf LinkExtractor auf. Dieser liest URLs aus einer Response oder prüft einzelne URLs mit matches(). SitemapSpider verarbeitet außerdem die Inhalte von sitemap.xml und sitemap_index.xml, mit oder ohne gzip-Komprimierung.
Sie können LinkExtractor direkt in jeder normalen Methode Spider.parse() verwenden. Die Vorlagen nehmen Ihnen nur das Verknüpfen der Teile ab.
CrawlSpider¶
CrawlSpider folgt Links automatisch anhand festgelegter Regeln.
from scrapling.spiders import CrawlSpider, CrawlRule, LinkExtractor
class QuotesSpider(CrawlSpider):
name = "blog"
start_urls = ["https://quotes.toscrape.com/"]
def rules(self):
return [
CrawlRule(LinkExtractor(allow=r"/author/"), callback=self.parse_author),
CrawlRule(LinkExtractor(allow=r"/page/\d+/")), # follow pagination, no callback
]
async def parse_author(self, response):
yield {
'.author-title': response.css('.author-title::text').get(),
"birthday": response.css('.author-born-date::text').get(),
"url": response.url,
}
result = QuotesSpider().start()
Eine CrawlRule verbindet einen LinkExtractor mit einem optionalen callback, einer gebundenen Methode des Spiders. Dazu kommen eine optionale priority für den erzeugten Request und ein optionales process_request. Letzteres ist eine gebundene Methode, die jeden Request vor der Ausgabe ändert. Die Standardmethode parse() wendet jede Regel auf jede Antwort an und liefert einen Request pro passender URL.
Hat eine Regel keinen Callback, werden die passenden URLs an die Standardmethode parse() des Spiders weitergegeben. Das ist praktisch für Folgeseiten: Sie extrahieren die Links zur nächsten Seite und setzen den Crawl fort, ohne eine eigene Methode dafür zu brauchen.
Reihenfolge des Crawls¶
Setzen Sie crawl_strategy auf CrawlSpider oder seinen Unterklassen, einschließlich ContentSpider, um die nächste offene Anfrage auszuwählen:
crawl_strategy |
Reihenfolge bei gleicher Request.priority |
|---|---|
"bfs" (Standard) |
Älteste Anfrage in der Warteschlange zuerst |
"dfs" |
Neueste Anfrage in der Warteschlange zuerst |
Der Scheduler nimmt offene Anfragen mit höherer Request.priority zuerst. Die Strategie legt nur die Reihenfolge bei gleicher Priorität fest.
Mit concurrent_requests = 1 wird jeweils eine Anfrage in der gewählten Reihenfolge verarbeitet. Doppelte URLs werden weiterhin gefiltert. Wird eine URL erneut gefunden, ändert sich die Position ihrer Anfrage nicht. Bei parallelen Anfragen können Seiten in anderer Reihenfolge fertig werden und sich Ebenen oder Zweige überschneiden.
Behalten Sie beim Fortsetzen aus einem Checkpoint dieselbe crawl_strategy bei.
Regeln mit eigener Logik verbinden¶
Überschreiben Sie parse() und rufen Sie super().parse(response) auf, um die Regeln zusammen mit Ihren eigenen Ausgaben zu verwenden:
class MySpider(CrawlSpider):
def rules(self):
return [CrawlRule(LinkExtractor(allow=r"/posts/"), callback=self.parse_post)]
async def parse(self, response):
yield {"page_url": response.url}
async for req in super().parse(response):
yield req
Anfragen mit process_request ändern¶
def add_priority(self, request, response):
request.priority = 10
return request
def rules(self):
return [CrawlRule(
LinkExtractor(allow=r"/posts/"),
callback=self.parse_post,
process_request=self.add_priority,
)]
SitemapSpider¶
SitemapSpider startet einen Crawl mit URLs aus sitemap.xml. Er verwendet dieselbe rules()-API wie CrawlSpider, sodass der Aufbau gleich bleibt.
from scrapling.spiders import SitemapSpider, CrawlRule, LinkExtractor
class MySitemap(SitemapSpider):
name = "sm"
sitemap_urls = ["https://example.com/sitemap.xml"]
def rules(self):
return [
CrawlRule(LinkExtractor(allow=r"/posts/"), callback=self.parse_post),
CrawlRule(LinkExtractor(allow=r"/products/"), callback=self.parse_product),
]
async def parse_post(self, response):
yield {"title": response.css("h1::text").get()}
async def parse_product(self, response):
yield {"sku": response.css(".sku::text").get()}
result = MySitemap().start()
URLs zuordnen¶
Für jede URL in der Sitemap prüft SitemapSpider nacheinander LinkExtractor.matches(url) jeder Regel. Die erste passende Regel gewinnt. Es wird ein Request mit dem Callback dieser Regel geliefert. Passt keine Regel und ist rules() nicht leer, wird die URL verworfen. Gibt rules() eine leere Liste zurück, wird jede URL an die Methode parse() des Spiders geleitet. Wenn Sie diese nicht überschreiben, löst sie standardmäßig NotImplementedError aus.
Sitemap-Indizes¶
Wenn SitemapSpider auf einen <sitemapindex> trifft, also eine Sitemap mit weiteren Sitemaps, folgt er diesen automatisch. Mit einem LinkExtractor in sitemap_follow filtern Sie, welchen untergeordneten Sitemaps er folgen soll:
class MySitemap(SitemapSpider):
name = "sm"
sitemap_urls = ["https://example.com/sitemap.xml"]
sitemap_follow = LinkExtractor(allow=r"/posts-sitemap-\d+\.xml") # only post sitemaps
Unterstützung für robots.txt¶
Tragen Sie eine robots.txt-URL direkt in sitemap_urls ein. SitemapSpider erkennt sie, extrahiert alle angegebenen Sitemaps und folgt ihnen:
URLs für andere Sprachen¶
Setzen Sie sitemap_alternate_links = True, um auch URLs aus <xhtml:link rel="alternate" hreflang="..."> anhand Ihrer Regeln zu verarbeiten.
XMLFeedSpider¶
XMLFeedSpider durchläuft die Knoten eines XML-Feeds, etwa RSS, Atom oder Produktfeeds. Setzen Sie itertag auf den gewünschten Knotennamen, standardmäßig "item". Überschreiben Sie parse_node(), das einmal pro passendem Knoten aufgerufen wird:
from scrapling.spiders import XMLFeedSpider
class RSSSpider(XMLFeedSpider):
name = "rss"
start_urls = ["https://example.com/feed.xml"]
itertag = "item"
async def parse_node(self, response, node):
yield {
"title": node.findtext("title"),
"link": node.findtext("link"),
"date": node.findtext("pubDate"),
}
result = RSSSpider().start()
Wie die anderen Callbacks kann parse_node() auch Request-Objekte liefern, zum Beispiel response.follow(node.findtext("link"), callback=self.parse_post). So ruft der Spider die Seiten auf, auf die der Feed verweist.
Knoten auswählen und verarbeiten¶
Jeder an parse_node() übergebene Knoten ist ein lxml-Element ohne Namespaces. Deshalb funktionieren node.findtext("title"), node.find("thumbnail").get("url") und node.xpath(...) mit jedem Feed ohne Namespace-Zuordnung. XPath unterscheidet dabei zwischen Groß- und Kleinschreibung. Ein einfaches itertag wie "entry" wählt Knoten nach ihrem Namen aus, unabhängig vom Namespace. Das passt für Atom und die meisten Feeds mit Namespaces. Für Knoten aus einem bestimmten Namespace verwenden Sie ein itertag mit Präfix und definieren dieses in namespaces:
class ThumbnailSpider(XMLFeedSpider):
name = "thumbs"
start_urls = ["https://example.com/feed.xml"]
itertag = "media:thumbnail"
namespaces = (("media", "http://search.yahoo.com/mrss/"),)
async def parse_node(self, response, node):
yield {"thumbnail": node.get("url")}
Mit gzip komprimierte Feeds, etwa .xml.gz oder Inhalte mit einem gzip-Content-Type, werden automatisch entpackt. Dabei gelten dieselben Schutzmaßnahmen wie beim Sitemap-Spider. Fehlerhaftes XML führt zu einer Warnung im Protokoll, statt den Crawl abzubrechen.
CSVFeedSpider¶
CSVFeedSpider durchläuft die Zeilen eines CSV-Feeds. Überschreiben Sie parse_row(). Die Methode erhält jede Zeile als Dictionary mit den Spaltennamen als Schlüsseln:
from scrapling.spiders import CSVFeedSpider
class PriceSpider(CSVFeedSpider):
name = "prices"
start_urls = ["https://example.com/products.csv"]
async def parse_row(self, response, row):
yield {"product": row["title"], "price": float(row["price"])}
result = PriceSpider().start()
Standardmäßig dient die erste Zeile als Kopfzeile. Hat der Feed keine Kopfzeile, geben Sie die Spaltennamen selbst in headers an. Für Feeds, die nicht das übliche Kommaformat verwenden, stellen Sie delimiter/quotechar ein:
class PriceSpider(CSVFeedSpider):
name = "prices"
start_urls = ["https://example.com/products.csv"]
headers = ["title", "price", "url"]
delimiter = ";"
Auch hier werden gzip-komprimierte Feeds automatisch entpackt, wie oben bei XMLFeedSpider beschrieben.
ContentSpider¶
ContentSpider baut auf CrawlSpider auf. Er durchsucht eine Website und wandelt jede Seite in bereinigtes Markdown für LLMs um. Er liefert einen Datensatz pro Seite und kann zusätzlich je Seite eine Markdown-Datei schreiben:
from scrapling.spiders import ContentSpider
class DocsSpider(ContentSpider):
name = "docs"
start_urls = ["https://example.com/docs/"]
allowed_domains = {"example.com"}
output_dir = "docs_markdown"
result = DocsSpider().start()
Er benötigt allowed_domains, damit der Crawl auf die Zielwebsite begrenzt bleibt. Die vollständige Anleitung mit allen Optionen und der Weitergabe an RAG-Pipelines finden Sie unter RAG-Systeme erstellen.
LinkExtractor direkt verwenden¶
Sie müssen die Vorlagen nicht verwenden. LinkExtractor funktioniert in jedem normalen Spider:
from scrapling.spiders import Spider, LinkExtractor
class CustomSpider(Spider):
name = "custom"
start_urls = ["https://example.com"]
def __init__(self):
super().__init__()
self._links = LinkExtractor(allow=r"/posts/", deny_domains="ads.example.com")
async def parse(self, response):
for url in self._links.extract(response):
yield response.follow(url, callback=self.parse_post)
async def parse_post(self, response):
yield {"title": response.css("h1::text").get()}
LinkExtractor-Referenz¶
| Argument | Standard | Beschreibung |
|---|---|---|
allow |
() |
Erlaubte URL-Muster. Leer bedeutet „alle“. String, kompiliertes Pattern oder Iterable aus beiden. |
deny |
() |
Zu verwerfende URL-Muster. Hat immer Vorrang vor allow. |
allow_domains |
() |
Erlaubte Hostnamen. Subdomains passen automatisch (example.com schließt api.example.com ein). |
deny_domains |
() |
Zu verwerfende Hostnamen. |
restrict_css |
() |
CSS-Selektoren, die die Extraktion aus dem DOM auf einen Bereich begrenzen. |
restrict_xpath |
() |
XPath-Selektoren, die die Extraktion aus dem DOM auf einen Bereich begrenzen. |
tags |
("a", "area") |
Element-Tags, in denen nach Links gesucht wird. |
attrs |
("href",) |
Attribute dieser Tags, aus denen URLs gelesen werden. |
canonicalize |
True |
Query-Parameter sortieren und den Pfad vereinheitlichen. |
strip |
True |
Leerraum am Anfang und Ende extrahierter URLs entfernen. |
keep_fragment |
False |
#fragment beim Vereinheitlichen beibehalten. |
deny_extensions |
IGNORED_EXTENSIONS |
Zu verwerfende Dateiendungen, etwa PDF, ZIP, Bilder und Videos. |
process |
None |
Optionale Funktion für jede extrahierte URL vor dem Filtern. Ein als falsch ausgewerteter Wert verwirft die URL. |
LinkExtractor.extract(response) gibt eine list[str] mit absoluten, gefilterten URLs ohne Duplikate zurück.
LinkExtractor.matches(url) gibt einen bool zurück. Die Methode filtert nur die URL nach erlaubten und ausgeschlossenen Mustern, Domain und Dateiendung. SitemapSpider verwendet sie, um URLs ohne Response zuzuordnen.