Zurück zum Blog
Anleitungen
Robert SfichiLast updated on Mar 31, 20268 min read

Der ultimative Leitfaden für den Einstieg in das Web-Scraping mit Elixir

Der ultimative Leitfaden für den Einstieg in das Web-Scraping mit Elixir

Einleitung

Web-Scraping ist der Prozess der Datenextraktion aus öffentlich zugänglichen Websites wie Foren, Social-Media-Seiten, Nachrichtenportalen, E-Commerce-Plattformen usw. Um Ihnen einen kleinen Vorgeschmack darauf zu geben, was Sie heute erstellen werden, beschreibt dieser Artikel die Erstellung eines Web-Scrapers in Elixir.

Falls Ihnen die Definition von Web-Scraping noch nicht ganz klar ist, betrachten wir das Speichern eines Bildes von einer Website als manuelles Web-Scraping. Wenn Sie alle Bilder einer Website von Hand speichern möchten, kann dies je nach Komplexität der Website Stunden oder sogar Tage dauern. 

Sie können diesen Prozess automatisieren, indem Sie einen Web-Scraper erstellen.

Vielleicht fragen Sie sich, welche Anwendungsfälle es für einen Web-Scraper gibt. Hier sind die gängigsten:

Nachrichtenverfolgung

Sie können die neuesten Nachrichten von Ihrer bevorzugten Finanznachrichten-Website scrapen, einen Algorithmus zur Stimmungsanalyse ausführen und so schon Minuten vor Börsenbeginn wissen, in was Sie investieren sollten

Einblicke in soziale Medien

Sie können die Kommentare von Ihren Social-Media-Seiten scrapen und analysieren, worüber Ihre Follower sprechen und wie sie zu Ihrem Produkt oder Ihrer Dienstleistung stehen.

Preisüberwachung

Wenn Ihre Leidenschaft das Sammeln von Spielkonsolen und Videospielen ist, Sie aber kein Vermögen für die neueste PS5 ausgeben möchten, können Sie einen Web-Scraper erstellen, der die eBay-Angebote abruft und Ihnen eine Benachrichtigung sendet, sobald eine günstige Konsole auf dem Markt ist.

Training für maschinelles Lernen

Wenn du eine mobile App erstellen möchtest, die die Rasse einer Katze auf jedem beliebigen Bild identifizieren kann, benötigst du eine Menge Trainingsdaten; anstatt Hunderttausende von Bildern mit Katzen manuell zu speichern, um das Modell zu trainieren, kannst du einen Web-Scraper verwenden, um dies automatisch zu erledigen.

Wir werden unseren Web-Scraper in Elixir erstellen, einer Programmiersprache, die auf Erlang aufbaut und von José Valim, einem Mitglied des Kernteams von Ruby on Rails, entwickelt wurde. Die Programmiersprache übernimmt die einfache Syntax von Ruby und kombiniert sie mit Erlangs Fähigkeit, Systeme mit geringer Latenz, verteilte und fehlertolerante Systeme zu erstellen.

Voraussetzungen

Bevor du die erste Zeile Code schreibst, stelle sicher, dass Elixir auf deinem Computer installiert ist. Lade das Installationsprogramm für dein Betriebssystem herunter und befolge die Anweisungen auf der Installationsseite

Während der Installation werden Sie feststellen, dass auch die Programmiersprache Erlang erforderlich ist. Beachten Sie, dass Elixir in der Erlang-VM läuft, sodass Sie beide benötigen.

Erste Schritte

In diesem Artikel lernen Sie, wie Sie einen Web-Scraper in Elixir erstellen, die Produkte von eBay nach PS5-Angeboten durchsuchen und die extrahierten Daten (Name, URL, Preis) lokal speichern.

Das Ziel untersuchen

Es ist an der Zeit, die Suchergebnisse auf der eBay-Seite zu untersuchen und einige Selektoren zu sammeln.

Gehen Sie zu ebay.com, geben Sie den Begriff „PS5“ in das Suchfeld ein und klicken Sie auf die Schaltfläche „Suchen“. Sobald die Suchergebnisseite geladen ist, öffnen Sie das Inspektionswerkzeug Ihres Browsers (klicken Sie mit der rechten Maustaste auf eine beliebige Stelle auf der Seite und wählen Sie „Inspizieren“).

Sie müssen die folgenden Selektoren erfassen:

  • Produktlistenelement
  • Produkt-URL
  • Produktname
  • Produktpreis

Verwenden Sie das Selektor-Werkzeug und suchen Sie nach der Produktliste (ul) und dem Produktelement (li):

Mit diesen beiden Elementen können Sie die für den Crawler zur Datenextraktion benötigten Klassen extrahieren:

  • .srp-results .s-item die untergeordneten Elemente des Produktlisten-Elements (ul)
  • .s-item__title span der Produkttitel
  • .s-item__link der Produktlink
  • .s-item__price der Produktpreis

Erstellen des Projekts

Erstellen wir ein Elixir-Projekt mit dem Befehl mix:

mix new elixir_spider --sup

Das Flag --sup generiert ein OTP-Anwendungsgerüst einschließlich eines Überwachungsbaums, einer notwendigen Funktion für eine Anwendung, die mehrere gleichzeitige Prozesse wie einen Crawler verwaltet.

Erstellen des temporären Ordners

Wechseln Sie in das Stammverzeichnis des Projekts:

cd elixir_spider

Erstellen Sie das Verzeichnis „temp“:

mkdir temp

Wir verwenden dieses Verzeichnis, um die gescrapten Elemente zu speichern.

Hinzufügen der Abhängigkeiten

Nachdem das Projekt erstellt wurde, müssen Sie die beiden Abhängigkeiten hinzufügen:

  • Crawly ist ein Anwendungsframework zum Crawlen von Websites und zum Extrahieren strukturierter Daten
  • Floki ist ein HTML-Parser, der die Suche nach Knoten mithilfe von CSS-Selektoren ermöglicht

Öffnen Sie die Datei „mix.exs“ und fügen Sie die Abhängigkeiten im „deps“-Block hinzu:

defp deps do
    [
        {:crawly, "~> 0.14.0"},
        {:floki, "~> 0.33.1"}
    ]
end

Rufen Sie die Abhängigkeiten mit folgendem Befehl ab:

mix deps.get

Erstellen Sie die Konfiguration

Erstellen Sie die Datei config/config.exs und fügen Sie diese Konfiguration ein:

import Config

config :crawly,
    closespider_timeout: 10,
    concurrent_requests_per_domain: 8,
    closespider_itemcount: 100,

    middlewares: [
            Crawly.Middlewares.DomainFilter,
            Crawly.Middlewares.UniqueRequest,
            {Crawly.Middlewares.UserAgent, user_agents: ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0"]}
    ],
    pipelines: [
            {Crawly.Pipelines.Validate, fields: [:url, :title, :price]},
            {Crawly.Pipelines.DuplicatesFilter, item_id: :title},
            Crawly.Pipelines.JSONEncoder,
            {Crawly.Pipelines.WriteToFile, extension: "jl", folder: "./temp"}
    ]

Allgemeine Optionen

Gehen wir die einzelnen Eigenschaften durch und erklären wir sie:

  • closespider_timeout: Ganzzahl, die maximale Anzahl von Sekunden, die der Spider offen bleibt
  • concurrent_requests_per_domain: die maximale Anzahl von Anfragen, die für jede gescrapte Domain gestellt werden
  • closespider_itemcount: die maximale Anzahl von Elementen, die durch die Item-Pipeline geleitet werden

User Agent

Durch die Einstellung des User-Agents verbessern Sie die Scraping-Ergebnisse, indem Sie einen echten Browser imitieren. Websites mögen keine Scraper und versuchen, alle User-Agents zu blockieren, die nicht echt wirken. Sie können ein Tool wie dieses verwenden, um den User-Agent Ihres Browsers zu ermitteln.

WebScrapingAPI wechselt den User-Agent und die IP-Adresse bei jeder Anfrage und implementiert zudem zahlreiche Umgehungsstrategien, um solche Situationen zu verhindern. Ihre Anfragen werden nicht blockiert, und die Implementierung eines Wiederholungsmechanismus liefert Ihnen hervorragende Ergebnisse.

Pipelines

Die Pipelines sind Befehle, die von oben nach unten verarbeitet werden und die Bearbeitung der verarbeiteten Elemente ermöglichen. Wir verwenden die folgenden Pipelines:

  • Felder validieren (Titel, Preis, URL): Prüft, ob das Element über die gescrapten Felder verfügt
  • Duplikatsfilter: prüft auf doppelte Artikel anhand des Titels
  • JSON-Encoder: Kodiert die Strukturen in ein JSON-Objekt
  • In Datei schreiben: schreibt die Elemente in den Ordner ./temp

Erstellen des Spiders

Ein Web-Crawler oder Spider ist eine Art Bot, der eine Website durchläuft und mithilfe von benutzerdefinierten Feldern über CSS-Selektoren Daten extrahiert. Ein Crawler kann alle Links einer Seite extrahieren und bestimmte davon (wie Paginierungslinks) nutzen, um weitere Daten zu crawlen. 

Es ist an der Zeit, die Grundlage für den Crawler zu schaffen: Erstellen Sie die Datei ebay_scraper.ex im Ordner lib/elixir_spider und fügen Sie den folgenden Code ein:

# lib/elixir_spider/ebay.ex
defmodule EbayScraper do
    use Crawly.Spider

    @impl Crawly.Spider
    def base_url(), do: ""

    @impl Crawly.Spider
    def init() do

    end

    @impl Crawly.Spider
    def parse_item(response) do

    end
 end

Dies ist lediglich das Grundgerüst der Datei; sie wird noch nicht ausgeführt und liefert keine Ergebnisse. Lassen Sie uns zunächst die einzelnen Funktionen besprechen und sie dann nacheinander ausfüllen.

Die Funktion `base_url()` wird einmal aufgerufen und gibt die Basis-URL der Zielwebsite zurück, die der Crawler scrapen soll; sie wird auch verwendet, um externe Links zu filtern und zu verhindern, dass der Crawler ihnen folgt. Du willst ja nicht das gesamte Internet scrapen.

@impl Crawly.Spider
def base_url(), do: "https://www.ebay.com/"

Die Funktion init() wird einmal aufgerufen und dient dazu, den Standardzustand für den Crawler zu initialisieren; in diesem Fall gibt die Funktion die start_url zurück, von der aus das Scraping beginnen soll.

Ersetze deine leere Funktion durch diese:

@impl Crawly.Spider
def init() do
[start_urls: ["https://www.ebay.com/sch/i.html?_nkw=ps5"]]
end

Die gesamte Magie der Datenextraktion findet in der Funktion `parse_item()` statt. Diese Funktion wird für jede gecrawlte URL aufgerufen. Innerhalb dieser Funktion verwenden wir den Floki-HTML-Parser, um die benötigten Felder zu extrahieren: Titel, URL und Preis.

Die Funktion sieht wie folgt aus:

@impl Crawly.Spider
def parse_item(response) do
    # Parse response body to document
    {:ok, document} = Floki.parse_document(response.body)

    # Create item (for pages where items exists)
    items =
        document
        |> Floki.find(".srp-results .s-item")
        |> Enum.map(fn x ->
         %{
           title: Floki.find(x, ".s-item__title span") |> Floki.text(),
           price: Floki.find(x, ".s-item__price") |> Floki.text(),
           url: Floki.find(x, ".s-item__link") |> Floki.attribute("href") |> Floki.text(),
         }
    end)

    %{items: items}
end

Wie du vielleicht bemerkt hast, verwenden wir die Klassen, die wir im Abschnitt „Erste Schritte – Das Ziel untersuchen“ gefunden haben, um die benötigten Daten aus den DOM-Elementen zu extrahieren.

Den Spider ausführen

Es ist an der Zeit, den Code zu testen und sicherzustellen, dass er funktioniert. Führen Sie im Stammverzeichnis des Projekts diesen Befehl aus:

iex -S mix run -e "Crawly.Engine.start_spider(EbayScraper)"

Wenn Sie PowerShell verwenden, stellen Sie sicher, dass Sie „iex“ durch „iex.bat“ ersetzen, da Sie sonst eine Fehlermeldung wegen des nicht vorhandenen Parameters „-S“ erhalten. Verwenden Sie diesen Befehl für PowerShell:

iex.bat -S mix run -e "Crawly.Engine.start_spider(EbayScraper)"

Überprüfen der Ergebnisse

Öffne den Ordner ./temp und überprüfe die .jl-Datei. Du solltest eine Textdatei sehen, die eine Liste von JSON-Objekten enthält, eines pro Zeile. Jedes Objekt enthält die Informationen, die wir aus der eBay-Produktliste benötigten: Titel, Preis und URL.

So sollte das Produkt-Objekt aussehen:

{"url":"https://www.ebay.com/itm/204096893295?epid=19040936896&hash=item2f851f716f:g:3G8AAOSwNslhoSZW&amdata=enc%3AAQAHAAAA0Nq2ODU0vEdnTBtnKgiVKIcOMvqJDPem%2BrNHrG4nsY9c3Ny1bzsybI0zClPHX1w4URLWSfXWX%2FeKXpdgpOe%2BF8IO%2FCh77%2FycTnMxDQNr5JfvTQZTF4%2Fu450uJ3RC7c%2B9ze0JHQ%2BWrbWP4yvDJnsTTWmjSONi2Cw71QMP6BnpfHBkn2mNzJ7j3Y1%2FSTIqcZ%2F8akkVNhUT0SQN7%2FBD38ue9kiUNDw9YDTUI1PhY14VbXB6ZMWZkN4hCt6gCDCl5mM7ZRpfYiDaVjaWVCbxUIm3rIg%3D%7Ctkp%3ABFBMwpvFwvRg","title":"PS5 Sony PlayStation 5 Console Disc Version! US VERSION!","price":"$669.99"}

Verbessern Sie den Spider

Wir haben alle Produkte von der ersten Seite der Produktliste abgerufen, aber das reicht nicht aus. Es ist an der Zeit, eine Paginierung zu implementieren und den Crawler alle verfügbaren Produkte extrahieren zu lassen.

Ändern wir die Funktion `parse_item()` und fügen einen neuen Block hinzu, der eine `requests`-Struktur mit dem Link zur nächsten Seite erstellt. Füge diesen Code nach dem Code für die Elemente ein:

# Extract the next page link and convert it to a request
requests =
  document
  |> Floki.find(".s-pagination a.pagination__next")
  |> Floki.attribute("href")
  |> Crawly.Utils.build_absolute_urls(response.request_url)
  |> Crawly.Utils.requests_from_urls()

Aktualisieren Sie die return-Anweisung der Funktion parse_item(), um auch die nächsten Anfragen einzubeziehen. Die Struktur sieht dann wie folgt aus:

%{
  :requests => requests,
  :items => items
}

Führen Sie den Crawler erneut aus, aber bereiten Sie sich diesmal einen Kaffee zu. Das Scrapen aller Seiten mit den PS5-Angeboten wird einige Minuten dauern.

Nachdem der Crawler seine Arbeit beendet hat, überprüfen Sie den Ordner ./temp auf die gescrapten Ergebnisse. Sie haben eBay erfolgreich nach PS5-Konsolen durchsucht und verfügen nun über eine Liste mit deren Preisen. Sie können diesen Crawler erweitern, um beliebige andere Produkte zu scrapen.

Fazit

In diesem Artikel hast du gelernt, was ein Web-Scraper ist, welche Anwendungsfälle diese Crawler haben, wie du mit Hilfe vorgefertigter Bibliotheken in wenigen Minuten einen Scraper mit Elixir einrichten kannst und wie du die Daten ausführst und extrahierst.

Falls du das Gefühl hattest, dass dies viel Arbeit war, muss ich dir eine nicht ganz so gute Nachricht überbringen: Wir haben gerade erst an der Oberfläche gekratzt. Wenn du diesen Scraper über einen längeren Zeitraum laufen lässt, wird dies mehr Probleme verursachen, als du dir vorstellen kannst.

eBay wird Ihre Aktivität erkennen und als verdächtig einstufen; der Crawler wird Captcha-Patches erhalten; Sie müssen die Crawler-Funktionalität erweitern, um Captchas zu lösen

Die Erkennungssysteme von eBay könnten Ihre IP-Adresse markieren und Ihnen den Zugriff auf die Website verweigern; Sie müssen sich einen Proxy-Pool besorgen und die IP-Adressen bei jeder Anfrage wechseln. 

Schwirrt Ihnen schon der Kopf? Lassen Sie uns noch über ein weiteres Problem sprechen: den User-Agent. Sie müssen eine große Datenbank mit User-Agents aufbauen und diesen Wert bei jeder Anfrage wechseln. Erkennungssysteme blockieren Scraper anhand der IP-Adresse und des User-Agents.

Wenn Sie sich mehr auf die geschäftliche Seite konzentrieren und Ihre Zeit in die Datenextraktion investieren möchten, anstatt die Erkennungsprobleme zu lösen, ist die Nutzung eines Scrapers als Dienstleistung die bessere Wahl. Eine Lösung wie WebScrapingAPI behebt alle oben genannten Probleme und noch viele weitere. 

Über den Autor
Robert Sfichi, Full-Stack-Entwickler @ WebScrapingAPI
Robert SfichiFull-Stack-Entwickler

Robert Sfichi ist Teammitglied bei WebScrapingAPI, wo er an der Produktentwicklung mitwirkt und dabei hilft, zuverlässige Lösungen zu entwickeln, die die Plattform und ihre Nutzer unterstützen.

Los geht’s

Sind Sie bereit, Ihre Datenerfassung zu erweitern?

Schließen Sie sich den über 2.000 Unternehmen an, die WebScrapingAPI nutzen, um Webdaten im Unternehmensmaßstab ohne zusätzlichen Infrastrukturaufwand zu extrahieren.