Im Rahmen eines POC (proof of concept), eines persönlichen Projekts oder einer statistischen Studie kann es notwendig werden, dass Sie eine Website scrapen müssen. Scraping ist die Automatisierung der Abfrage bestimmter Daten, die von einer Website bereitgestellt werden, mittels eines Skripts. In diesem Artikel gehen wir davon aus, dass Sie Grundkenntnisse im Web-Scraping haben.
Da die Gesetzgebung unklar und die Rechtsprechung im Bereich des Scrapings zögerlich ist, können wir Ihnen nur davon abraten, es zu kommerziellen Zwecken zu nutzen, aber wir zeigen Ihnen dennoch, wie Sie ein Scraping-System mit IP-Rotation mithilfe von
Python,
Scrapy und Scrapoxy.
Fall des Client Side Rendering: Die API nutzen
Für Websites, die in
Client-Side-Rendering, Sie können versuchen, die API direkt zu nutzen, die Rohdaten (oft im JSON- oder XML-Format) an Ihren Browser zurücksendet, welcher diese dann in das HTML einfügen wird. Dafür können Sie (im Entwicklungstool Ihres Browsers im Bereich „Netzwerk“) die XHR-Anfragen ansehen, die gesendet werden, wenn Sie die zu scrapende Webseite aktualisieren. Sobald Sie die Anfrage(n) gefunden haben, welche die Daten laden, die Sie abrufen möchten, müssen Sie nur noch versuchen, dieselbe Anfrage innerhalb eines Python-Skripts zu erstellen (zum Beispiel unter Verwendung der Bibliothek request). So ist kein eigentliches Scraping nötig (kein headless webdriver, parser, xpath usw.).
Hier zum Beispiel auf der IMDB-Webseite könnten wir versuchen, deren GraphQL-API zu nutzen.
Nur für den Fall, dass die API sich als zu schwierig zu nutzen erweist, setze ich die schwere Artillerie ein, um Daten zu scrapen, die mit Javascript angezeigt werden: Selenium.
Fall des Server-Side-Rendering
Für Webseiten mit Server-Side-Rendering (und die ohne API-Aufruf funktionieren), haben Sie keine andere Wahl, als das HTML der Seite über eine Parsing-Bibliothek wie
Beautiful Soup und die Daten über deren
xpath.
Wenn Ihr Scraping-Projekt umfangreich ist, empfehle ich Ihnen, sich an
Scrapy, ein ziemlich vollständiges Python-Framework für Crawling und Scraping, das es unter anderem ermöglicht, Ihre xpaths über ein Terminal zu testen oder diverse Plugins wie
Splash, was es ermöglicht, Javascript auf Webseiten zu aktivieren.
Unser Problem: die IP-Sperre
Manchmal verwenden Webseiten Methoden zur Prävention gegenüber Scraping-Bots. Eine der gefürchtetsten Methoden, die mir lange Zeit Kopfzerbrechen bereitet hat, ist die IP-Sperre. Die gescrapte Webseite analysiert live die Anfragen, die sie erhält. Wenn sie feststellt, dass eine IP zu viele Anfragen stellt, um ein normaler menschlicher Benutzer zu sein, kann sie diese IP sperren (für eine mehr oder weniger lange Dauer). So wird Ihr Skript bei der nächsten Anfrage einen Fehler aufgrund einer 429-Antwort des Servers auslösen.
Erste Lösung – Pausen einlegen sleep
Um diese Methode zu kontern, wäre die erste einfache Lösung, die man in Betracht ziehen könnte, „Sleeps“ zwischen unsere verschiedenen Anfragen einzufügen, um die Ausführung unseres Skripts zu verlangsamen und nicht von der gescrapten Webseite erkannt zu werden. Aber angenommen, wir hätten 30'000 Seiten zu scrapen mit 30 Sekunden Sleep zwischen zwei Anfragen, müsste das Skript über 10 Tage laufen, um das Ende der Seitenliste zu erreichen. Diese Lösung wird sicherlich mit einem ausreichend grossen „Sleep“-Wert funktionieren, würde aber bei der Ausführung endlos werden.
Zweite Lösung – IP-Rotation mit Scrapoxy
Eine andere, wesentlich robustere und schnellere Lösung in der Ausführung (da sie keine Sleeps enthält) ist die Verwendung des Open-Source-Dienstes Scrapoxy. Scrapoxy ermöglicht Scraping, indem es die Anfragen über eine Reihe von Proxys umleitet. Diese Proxys sind Serverinstanzen, die vom AWS EC2-Dienst gehostet werden.
Zusammenfassung der in diesem Artikel untersuchten Lösung
Ein Spider (Scrapy-Scraping-Instanz) bewegt sich von Link zu Link („crawlt“ eine Reihe von Seiten)
Die Scrapy-Anfragen an den Server der Zielseite werden nicht direkt von Ihrer Maschine ausgeführt, sondern dank des Scrapoxy-Dienstes an EC2-Container weitergeleitet.
Sobald ein Container seine IP blockiert sieht, startet Scrapoxy eine neue Armee von Containern, um die alten, unbrauchbar gewordenen zu ersetzen.
Voraussetzungen:
Um diesem Tutorial zu folgen, müssen Sie Python und das Dienstprogramm pip auf Ihrer Maschine installiert haben.
Erstellung und Konfiguration des AWS-Kontos
Kontoerstellung
Wenn Sie noch keines haben, erstellen Sie ein Konto bei
AWS um auf die verschiedenen in der AWS-Konsole verfügbaren Dienste zugreifen zu können. Insbesondere EC2, der Dienst, der Server auf Abruf bereitstellt und von Scrapoxy verwendet wird, um die Proxys zu erstellen, die unsere Anfragen weiterleiten werden. Um ein Konto zu erstellen, gehen Sie bitte
hier.
Hinweis: Sie müssen Ihre Kreditkarteninformationen eingeben. Seien Sie also vorsichtig bei der Nutzung des EC2-Dienstes, der einen guten Free Tier bietet (750 Stunden kumulierte Nutzung pro Monat über alle Ihre Instanzen hinweg), aber nicht unbegrenzt kostenlos ist. Wenn Sie also 10 EC2-Instanzen parallel betreiben, werden Ihnen die Kosten nach 750 / 10 = 75 Stunden in Rechnung gestellt.
Erstellung von IAM-Credentials
Damit Scrapoxy für Sie Ihre EC2-Instanzen verwalten darf, müssen Sie ein Schlüsselpaar generieren:
access key und
secret access key. Dafür gehen Sie in der AWS-Konsole zu Ihren
Sicherheitsanmeldeinformationen (
hier)
Klappen Sie den Bereich Access Key und klicken Sie auf Schlüssel hinzufügen. Behalten Sie den Wert deraccess key id. Klicken Sie danach auf „Show secret access key“ und notieren Sie sich den angegebenen Wert oder laden Sie die key file.
Achtung: Sie können ihn später nicht mehr anzeigen lassen.
Erstellung einer Sicherheitsgruppe
Sie müssen auch eine Sicherheitsgruppe erstellen. Dafür gehen Sie in der AWS-Konsole zum EC2-Dienst und genauer zu den Sicherheitsgruppen (unter dieser URL, indem Sie den Parameter region durch die von Ihnen verwendete Region ersetzen: https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#SecurityGroups)
Hinweis: Wir empfehlen Ihnen, die Region eu-west-1
Klicken Sie auf die Schaltfläche „Sicherheitsgruppe erstellen“ und dann:
Wahl eines AMI
Später, bei der Konfiguration von Scrapoxy, werden Sie einen «AMI» benötigen.
Wenn Sie sich in der Region eu-west-1 befinden, können Sie einen dieser drei AMI wählen, abhängig davon, welchen Instanztyp Sie für Ihr Skript erstellen möchten:
t1.micro ⇒ ami-c74d0db4
t2.micro ⇒ ami-485fbba5
t2.nano ⇒ ami-06220275
Merken Sie sich den Namen des gewählten AMI für später!
Tipp: Versuchen Sie es mit dem ersten (ami-c74d0db4), der t1.micro-Instanzen generiert (die kleinstmöglichen). Denn wir werden diese Server bitten, einfache HTTP-Anfragen zu stellen; es ist nicht nötig, Hochleistungsrechner zu starten.
Installation und Konfiguration von Scrapoxy
Installation von Scrapoxy
Um Scrapoxy zu installieren, geben Sie bitte die folgenden Befehle in ein Terminal ein: sudo apt-get install build-essential sudo npm install -g scrapoxy
Konfiguration von Scrapoxy
Um Scrapoxy zu verwenden und es mit Ihrem AWS-Konto zu verbinden, müssen Sie eine Konfigurationsdatei generieren und ausfüllen. Beginnen Sie, indem Sie den folgenden Befehl eingeben: scrapoxy init conf.json
Anschliessend in der Datei conf.json, die gerade generiert wurde:
ändern Sie das password im Abschnitt commander (geben Sie ein, was Sie möchten, dieses Passwort wird für den Zugriff auf die grafische Benutzeroberfläche von Scrapoxy verwendet)
im Abschnitt provider, behalten Sie nur das Objekt bei, dessen type : “awsec2”
ersetzen Sie dann die accessKeyId und secretAccessKey dieses Objekts durch die zuvor bei AWS abgerufenen Credentials
ersetzen Sie ebenfalls den Wert des AMI (Feld ImageId) durch den korrekten Wert
Sie müssen die Parameter bezüglich der Anzahl der EC2-Instanzen nicht ändern; das können Sie direkt über die grafische Benutzeroberfläche einstellen, nachdem Sie Scrapoxy gestartet haben.
Lokaler Start von Scrapoxy
Um eine Instanz von Scrapoxy auf Ihrem Rechner zu starten, geben Sie in einem neuen Terminal den Befehl ein: scrapoxy start conf.json -d
Dieses Terminal muss während der gesamten Ausführung des Skripts geöffnet bleiben, das wir Ihnen gleich vorstellen werden. Die Scrapoxy-Instanz hat die Aufgabe, die Anfragen zwischen den verschiedenen EC2-Containern zu verteilen, die Antworten zu analysieren und die Container neu zu starten, wenn eine Anfrage fehlschlägt.
Hinweis: Wenn Scrapoxy läuft, können Sie über die Adresse: http://localhost:8889/ auf eine grafische Benutzeroberfläche (GUI) zugreifen. Zudem wird der Dienst für Scrapy über die Adresse: http://localhost:8888/ zugänglich sein.
Installation und Nutzung von Scrapy
Scrapy installieren
Um zu installieren
Scrapy mit dem Dienstprogramm pip, geben Sie den Befehl ein:
pip install scrapy scrapoxyEin neues Scrapy-Projekt erstellen
Um ein neues Projekt (eine neue «Spider») zu erstellen, geben Sie in einem Terminal den Befehl ein: scrapy startproject myscraper
Dieser Befehl erstellt einen Ordner myscraper, der diese Verzeichnisstruktur aufweist:
Erstellen Sie einen Ordner data im Unterordner myscraper und eine Datei scraper.py im Unterordner spider, um diese Verzeichnisstruktur zu erhalten:
Scrapoxy in die Spider integrieren
Um Scrapoxy in Ihre Spider zu integrieren und damit die Anfragen von Ihren EC2-Instanzen anstatt von Ihrem Rechner gestellt werden, fügen Sie in myscraper/myscraper/settings.py die folgenden Zeilen hinzu: language=python CONCURRENT_REQUESTS_PER_DOMAIN = 1 RETRY_TIMES = 0 # PROXY PROXY = 'http://127.0.0.1:8888/?noconnect' # SCRAPOXY API_SCRAPOXY = 'http://127.0.0.1:8889/api' API_SCRAPOXY_PASSWORD = 'CHANGE_THIS_PASSWORD' # BLACKLISTING BLACKLIST_HTTP_STATUS_CODES = [ 429 ] DOWNLOADER_MIDDLEWARES = { 'scrapoxy.downloadmiddlewares.proxy.ProxyMiddleware': 100, 'scrapoxy.downloadmiddlewares.wait.WaitMiddleware': 101, 'scrapoxy.downloadmiddlewares.scale.ScaleMiddleware': 102, 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None, 'scrapoxy.downloadmiddlewares.blacklist.BlacklistDownloaderMiddleware': 950 }
Wenn die von Ihnen gescrapte Website einen Statuscode zurückgibt, der von 429 abweicht, um anzuzeigen, dass Ihre IP gesperrt wurde, können Sie dies angeben, indem Sie den von der Website zurückgegebenen Statuscodewert zur Variable BLACKLIST_HTTP_STATUS_CODES hinzufügen.
Erstellung der Spider
Kommen wir nun zum Kern des Scraping-Skripts: der Spider.
Eine Scrapy-Spider besteht aus mehreren Teilen:
Hier ist ein Beispielcode einer Spider mit der parse()-Methode und Beispielen, wie Daten auf einer geparsten Seite abgerufen werden können. Um mehr zu erfahren, konsultieren Sie direkt die Scrapy-Dokumentation oder informieren Sie sich über den Begriff xpath (
hier).
Sie können diesen Code in die Datei scraper.py kopieren, die Sie zuvor erstellt haben. Anschliessend müssen Sie nur den Code anpassen, um ihn an Ihre Zielseite anzupassen. language=python import scrapy import csv class Scraper(scrapy.Spider): name = u'scraper' custom_settings = { 'FEED_FORMAT': "csv", 'FEED_URI': './data.csv', 'FEED_EXPORT_ENCODING': 'utf-8' } allowed_domains = ['www.site-url.com'] start_urls = [f"www.site-url.com/page?id={id}" for id in range(100)] def start_requests(self): total = len(self.start_urls) for i in range(total): print(f"############################## {i}/{total} #############################") url = self.start_urls[i] yield scrapy.Request(url, self.parse, meta={"url": url}) def parse(self, response): # HTML einer Div vollständig abrufen (HINWEIS: Kopieren/Einfügen des Ergebnisses in einen HTML-Editor, # um die XPaths der zu sammelnden Daten einfacher zu finden) data_0 = response.xpath("//div[@id='main-content']").text # Das src-Attribut eines img-Tags abrufen data_1 = response.xpath("//div[@id='main-content']/div/div/div/div[2]/div/div/div/div/img/@src").extract_first() # Kategorietitel abrufen (Titel im title-Attribut des category-div-Tags) # und die IDs der in der Kategorie vorhandenen Elemente (IDs in der URL des Elements) data_2_raw = response.xpath("//category-div") data_2 = "" for data_2_item in data_2_raw: title = data_2_item.xpath("./@title").extract_first() data_2 += "{}:".format(title) links = data_2_item.xpath("./div/div/div[2]/a/@href").extract() for link in links: data_2 += "{}/".format(link.split("/")[-1].split(".")[0].split("-")[-1]) data_2 = data_2[:-1] data_2 = data_2[:-1] yield { "page_url": response.meta['url'], "data_0": data_0, "data_1": data_1, "data_2": data_2 }
Start der Spider
Um die Spider zu starten, führen Sie im Terminal im Stammverzeichnis des myscraper-Ordners den Befehl aus: scrapy crawl scraper
Crawling
Wenn Sie die Spider mit auf Ihrem Rechner aktiviertem Scrapoxy starten, müssen Sie zunächst zwei Minuten warten, bis die EC2-Container gestartet sind. (Sie können diese Wartezeit deaktivieren, um Ihr Skript einfacher über die Datei myscraper/myscraper/settings.py zu testen, indem Sie die Zeile auskommentieren language=python 'scrapoxy.downloadmiddlewares.wait.WaitMiddleware': 101)
Überwachen Sie anschliessend, dass alles wie erwartet abläuft: eine Reihe von Anfragen, die 200er-Antworten zurückgeben, und nach einer gewissen Zeit eine Anfrage, die aufgrund einer 429er-Antwort fehlschlägt, gefolgt von einem Sleep von 2-3 Minuten, um die EC2-Container neu zu starten.
Sie können die gewünschte Anzahl EC2-Instanzen jederzeit über die Scrapoxy-GUI ändern (normalerweise unter http://localhost:8889/ zugänglich). Auf dieser GUI können Sie auch die Statistiken Ihrer Anfragen seit dem Start des Dienstes einsehen.
Fazit
Wenn Sie drei Dinge aus diesem Artikel behalten sollten, sind es die folgenden:
Wenn Sie Daten von einer Website scrapen müssen, greifen Sie nicht direkt zu fortgeschrittenen Scraping-Tools! Versuchen Sie zunächst, die API der Website zu nutzen. Verwenden Sie dafür eine beliebige HTTP-Anfrage-Bibliothek Ihrer bevorzugten Sprache.
Wenn das nicht funktioniert, greifen Sie zum traditionelleren Scraping (Parsing von HTML, Headless Browser, ...).
Wenn die gescrapte Website Ihre IP blockiert, weil Sie zu viele Anfragen stellen, stehen Ihnen zwei Möglichkeiten offen:
Wenn Sie eine geringe Anzahl von Seiten scrapen müssen und/oder die Ausführungszeit des Skripts kein Problem darstellt, versuchen Sie, "sleep" in Ihren Code zwischen den Anfragen einzufügen.
Wenn das nicht funktioniert oder Sie zu viele Seiten scrapen müssen, als dass diese Technik praktikabel wäre (Skript-Ausführungszeit zu lang), dann konfigurieren Sie Scrapoxy, um die von Ihrem Skript verwendeten IPs für seine Anfragen zu wechseln.