Zurück zum Blog
Anleitungen
Mihai MaximLast updated on Mar 31, 202611 min read

Web-Scraping mit Scrapy: Der einfache Weg

Web-Scraping mit Scrapy: Der einfache Weg

Web-Scraping mit Scrapy

Scrapy ist eine leistungsstarke Python-Bibliothek zum Extrahieren von Daten aus Websites. Sie ist schnell, effizient und einfach zu bedienen – glaub mir, ich habe es selbst ausprobiert. Egal, ob du Datenwissenschaftler, Entwickler oder jemand bist, der gerne mit Daten spielt, Scrapy hat dir etwas zu bieten. Und das Beste daran: Es ist kostenlos und Open Source.

Scrapy-Projekte verfügen über eine Dateistruktur, die dir hilft, deinen Code und deine Daten zu organisieren. Das erleichtert das Erstellen und Warten von Web-Scrapern, daher lohnt es sich auf jeden Fall, Scrapy in Betracht zu ziehen, wenn du ernsthaftes Web-Scraping planst. Web-Scraping mit Scrapy Es ist, als hättest du einen hilfreichen Assistenten (wenn auch einen virtuellen) an deiner Seite, wenn du dich auf deine Reise der Datenextraktion begibst.

Was wir erstellen werden

Wenn man eine neue Fähigkeit erlernt, ist es eine Sache, darüber zu lesen, und eine andere, es tatsächlich zu tun. Deshalb haben wir beschlossen, gemeinsam einen Scraper zu erstellen, während wir diesen Leitfaden durchgehen. Das ist der beste Weg, um ein praktisches Verständnis dafür zu bekommen, wie Web-Scraping mit Scrapy funktioniert.

Was genau werden wir also erstellen? Wir werden einen Scraper erstellen, der Wortdefinitionen von der Website „Urban Dictionary“ ausliest. Das ist ein unterhaltsames Ziel für das Scraping und wird dazu beitragen, den Lernprozess angenehmer zu gestalten. Unser Scraper wird einfach sein – er wird die Definitionen für verschiedene Wörter ausgeben, die auf der Website von Urban Dictionary zu finden sind. Wir werden Scrapys integrierte Funktionen zur Auswahl und Extraktion von Daten aus HTML-Dokumenten nutzen, um die benötigten Definitionen herauszufiltern.

Also legen wir los! Im nächsten Abschnitt gehen wir die Voraussetzungen durch, die du benötigst, um diesem Tutorial folgen zu können. Bis dann!

Voraussetzungen

Bevor wir uns an die Erstellung unseres Scrapers machen, müssen Sie einige Dinge einrichten. In diesem Abschnitt behandeln wir die Installation von Scrapy und die Einrichtung einer virtuellen Umgebung für unser Projekt. Die Scrapy-Dokumentation empfiehlt, Scrapy in einer dedizierten virtuellen Umgebung zu installieren. Auf diese Weise vermeiden Sie Konflikte mit Ihren Systempaketen.

Ich verwende Scrapy unter Ubuntu 22.04.1 WSL (Windows Subsystem for Linux), daher werde ich eine virtuelle Umgebung für meinen Rechner konfigurieren.

Ich empfehle dir, das Kapitel „Understanding the Folder Structure“ durchzugehen, um die Tools, mit denen wir arbeiten, vollständig zu verstehen. Schau dir auch das Kapitel „Scrapy Shell“ an, es wird deine Entwicklungsarbeit erheblich erleichtern.

Einrichten einer virtuellen Python-Umgebung

Um eine virtuelle Umgebung für Python unter Ubuntu einzurichten, kannst du den Befehl mkvirtualenv verwenden. Stelle zunächst sicher, dass virtualenv und virtualenvwrapper installiert sind:

$ sudo apt-get install virtualenv virtualenvwrapper

Fügen Sie diese Zeilen am Ende Ihrer .bashrc-Datei ein:

export WORKON_HOME=$HOME/.virtualenvs

export PROJECT_HOME=$HOME/Deve

export VIRTUALENVWRAPPER_PYTHON='/usr/bin/python3'

source /usr/local/bin/virtualenvwrapper.sh

Ich habe die Datei mit Vim bearbeitet, aber du kannst jeden beliebigen Editor verwenden:

vim ~/.bashrc

// Use ctr + i to enter insert mode, use the down arrow to scroll to the bottom of the file.

// Paste the lines at the end of the file.

// Hit escape to exit insert mode, type wq and hit enter to save the changes and exit Vim.

Erstellen Sie dann mit mkvirtualenv eine neue virtuelle Umgebung:

$ mkvirtualenv scrapy_env

Nun sollte am Anfang Ihrer Terminalzeile ein (scrapy_env) stehen.

Um die virtuelle Umgebung zu verlassen, geben Sie $ deactivate ein

Um zur virtuellen Umgebung scrapy_env zurückzukehren, geben Sie $ workon scrapy_env ein

Scrapy installieren

Sie können Scrapy mit dem Paketmanager pip installieren:

$ pip install scrapy

Dadurch wird die neueste Version von Scrapy installiert.

Sie können ein neues Projekt mit dem Befehl `scrapy startproject` erstellen:

$ scrapy startproject myproject

Dadurch wird ein neues Scrapy-Projekt namens „myproject“ initialisiert. Es sollte die Standard-Projektstruktur enthalten.

Die Ordnerstruktur verstehen

Dies ist die Standard-Projektstruktur:

myproject

├── myproject

│ ├── __init__.py

│ ├── items.py

│ ├── middlewares.py

│ ├── pipelines.py

│ ├── settings.py

│ └── spiders

│ └── __init__.py

└── scrapy.cfg

items.py

items.py ist ein Modell für die extrahierten Daten. Dieses Modell wird verwendet, um die Daten zu speichern, die Sie von der Website extrahieren.

Beispiel:

import scrapy

class Product(scrapy.Item):

    name = scrapy.Field()

    price = scrapy.Field()

    description = scrapy.Field()

Hier haben wir ein Element namens „Product“ definiert. Es kann von einem Spider (siehe /spiders) verwendet werden, um Informationen über den Namen, den Preis und die Beschreibung eines Produkts zu speichern.

/spiders

/spiders ist ein Ordner, der Spider-Klassen enthält. In Scrapy sind Spiders Klassen, die definieren, wie eine Website gecrawlt werden soll.

Beispiel:

import scrapy

from myproject.items import Product

class MySpider(scrapy.Spider):

    name = 'myspider'

    start_urls = ['<example_website_url>']

    

    def parse(self, response):

        # Extract the data for each product

        for product_div in response.css('div.product'):

            product = Product()

            product['name'] = product_div.css('h3.name::text').get()

            product['price'] = product_div.css('span.price::text').get()

            product['description'] = product_div.css('p.description::text').get()

            yield product

Der „Spider“ durchläuft die start_urls, extrahiert den Namen, den Preis und die Beschreibung aller auf den Seiten gefundenen Produkte (mithilfe von CSS-Selektoren) und speichert die Daten im Item „Product“ (siehe items.py). Anschließend „gibt“ er diese Items ab, wodurch Scrapy sie an die nächste Komponente in der Pipeline weiterleitet (siehe pipelines.py).

„Yield“ ist ein Schlüsselwort in Python, das es einer Funktion ermöglicht, einen Wert zurückzugeben, ohne die Funktion zu beenden. Stattdessen erzeugt sie den Wert und unterbricht die Ausführung der Funktion, bis der nächste Wert angefordert wird.

Zum Beispiel:

def count_up_to(max):

    count = 1

    while count <= max:

        yield count

        count += 1

for number in count_up_to(5):

    print(number)

// returns 1 2 3 4 5 (each on a new line)  

pipelines.py

Pipelines sind für die Verarbeitung der Items (siehe items.py und /spiders) zuständig, die von den Spidern extrahiert werden. Sie können damit den HTML-Code bereinigen, die Daten validieren und sie in ein benutzerdefiniertes Format exportieren oder in einer Datenbank speichern.

Beispiel:

import pymongo

class MongoPipeline(object):

    def __init__(self):

        self.conn = pymongo.MongoClient('localhost', 27017)

        self.db = self.conn['mydatabase']

        self.product_collection = self.db['products']

        self.other_collection = self.db['other']

        

    def process_item(self, item, spider):

        if spider.name == 'product_spider':

            //insert item in the product_collection

        elif spider.name == 'other_spider':

            //insert item in the other_collection

        return item

Wir haben eine Pipeline namens MongoPipeline erstellt. Sie stellt eine Verbindung zu zwei MongoDB-Sammlungen her (product_collection und other_collection). Die Pipeline empfängt Elemente (siehe items.py) von Spidern (siehe /spiders) und verarbeitet sie in der Funktion process_item. In diesem Beispiel fügt die Funktion process_item die Elemente zu den dafür vorgesehenen Sammlungen hinzu.

settings.py

settings.py speichert eine Vielzahl von Einstellungen, die das Verhalten des Scrapy-Projekts steuern, wie z. B. die zu verwendenden Pipelines, Middlewares und Erweiterungen sowie Einstellungen dazu, wie das Projekt Anfragen und Antworten verarbeiten soll.

Sie können damit beispielsweise die Ausführungsreihenfolge der Pipelines festlegen (siehe pipelines.py):

ITEM_PIPELINES = {

    'myproject.pipelines.MongoPipeline': 300,

    'myproject.pipelines.JsonPipeline': 302,

}

// MongoPipeline will execute before JsonPipeline, because it has a lower order number(300)

Oder einen Export-Container festlegen:

FEEDS = {

    'items': {'uri': 'file:///tmp/items.json', 'format': 'json'},

}

// this will save the scraped data to a items.json

scrappy.cfg

scrapy.cfg ist die Konfigurationsdatei für die Haupteinstellungen des Projekts.

[settings]

default = [name of the project].settings

[deploy]

#url = http://localhost:6800/

project = [name of the project]

middlewares.py

Es gibt zwei Arten von Middlewares in Scrapy: Downloader-Middlewares und Spider-Middlewares.

Downloader-Middlewares sind Komponenten, die verwendet werden können, um Anfragen und Antworten zu modifizieren, Fehler zu behandeln und benutzerdefinierte Download-Logik zu implementieren. Sie befinden sich zwischen dem Spider und dem Scrapy-Downloader.

Spider-Middlewares sind Komponenten, die zur Implementierung benutzerdefinierter Verarbeitungslogik genutzt werden können. Sie befinden sich zwischen der Engine und dem Spider.

Die Scrapy-Shell

Bevor wir uns auf die spannende Reise zur Implementierung unseres Urban-Dictionary-Scrapers begeben, sollten wir uns zunächst mit der Scrapy-Shell vertraut machen. Diese interaktive Konsole ermöglicht es uns, unsere Scraping-Logik zu testen und die Ergebnisse in Echtzeit zu sehen. Sie ist wie eine virtuelle Sandbox, in der wir herumprobieren und unseren Ansatz verfeinern können, bevor wir unseren Spider auf das Web loslassen. Glauben Sie mir, das wird Ihnen auf lange Sicht viel Zeit und Kopfzerbrechen ersparen. Also lasst uns ein bisschen Spaß haben und die Scrapy Shell kennenlernen.

Die Shell öffnen

Um die Scrapy Shell zu öffnen, müssen Sie zunächst in Ihrem Terminal in das Verzeichnis Ihres Scrapy-Projekts navigieren. Dann können Sie einfach den folgenden Befehl ausführen:

scrapy shell

Dadurch wird die Scrapy-Shell geöffnet und du erhältst eine Eingabeaufforderung, in die du Scrapy-Befehle eingeben und ausführen kannst. Du kannst dem Shell-Befehl auch eine URL als Argument übergeben, um eine Webseite direkt zu scrapen, etwa so:

scrapy shell <url>

Beispiel:

scrapy shell https://www.urbandictionary.com/define.php?term=YOLO

Gibt (im Antwortobjekt) den HTML-Code der Webseite zurück, die die Definitionen des Wortes YOLO (im Urban Dictionary) enthält.

Alternativ kannst du, sobald du die Shell aufgerufen hast, den Befehl „fetch“ verwenden, um eine Webseite abzurufen.

fetch('https://www.urbandictionary.com/define.php?term=YOLO')

Sie können die Shell auch mit dem Parameter „nolog“ starten, damit keine Protokolle angezeigt werden:

scrapy shell --nolog

Arbeiten mit der Shell

In diesem Beispiel habe ich die URL „https://www.urbandictionary.com/define.php?term=YOLO“ abgerufen und den HTML-Code in der Datei test_output.html gespeichert.

(scrapy_env) mihai@DESKTOP-0RN92KH:~/myproject$ scrapy shell --nolog

[s] Available Scrapy objects:

[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)

[s] crawler <scrapy.crawler.Crawler object at 0x7f1eef80f6a0>

[s] item {}

[s] settings <scrapy.settings.Settings object at 0x7f1eef80f4c0>

[s] Useful shortcuts:

[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)

[s] fetch(req) Fetch a scrapy.Request and update local objects

[s] shelp() Shell help (print this help)

[s] view(response) View response in a browser

>>> response // response is empty

>>> fetch('https://www.urbandictionary.com/define.php?term=YOLO')

>>> response

<200 https://www.urbandictionary.com/define.php?term=Yolo>

>>> with open('test_output.html', 'w') as f:

... f.write(response.text)

...

118260

Sehen wir uns nun test_output.html an und identifizieren wir die Selektoren, die wir benötigen, um die Daten für unseren Urban-Dictionary-Scraper zu extrahieren.

Wir können Folgendes feststellen:

  • Jeder Container für eine Wortdefinition die Klasse „definition“ hat.
  • Die Bedeutung des Wortes befindet sich innerhalb des div-Elements mit der Klasse „meaning“.
  • Beispiele für das Wort befinden sich im Div mit der Klasse „example“.
  • Informationen zum Autor des Beitrags und zum Datum befinden sich im Div mit der Klasse „contributor“.

Testen wir nun einige Selektoren in der Scrapy-Shell:

Um Verweise auf alle Definitionscontainer zu erhalten, können wir CSS- oder XPath-Selektoren verwenden:

Mehr über XPath-Selektoren erfährst du hier: https://www.webscrapingapi.com/the-ultimate-xpath-cheat-sheet

definitions = response.css('div.definition')

definitions = response.xpath('//div[contains(@class,"definition")]')

Wir sollten die Bedeutung, das Beispiel und die Beitragsinformationen aus jedem Definitionscontainer extrahieren. Testen wir einige Selektoren mit dem ersten Container:

>>> first_def = definitions[0]

>>> meaning = first_def.css('div.meaning').xpath(".//text()").extract()

>>> meaning

['Yolo ', 'means', ', '', 'You Only Live Once', ''.']

>>> meaning = "".join(meaning)

>>> meaning

'Yolo means, 'You Only Live Once'.'

>>> example = first_def.css('div.example').xpath(".//text()").extract()

>>> example = "".join(example)

>>> example

'"Put your seatbelt on." Jessica said.\r"HAH, YOLO!" Replies Anna.\r(They then proceed to have a car crash. Long story short...Wear a seatbelt.)'

>>> post_data = first_def.css('div.contributor').xpath(".//text()").extract()

>>> post_data

['by ', 'Soy ugly', ' April 24, 2019']

Mithilfe der Scrapy-Shell konnten wir schnell einen allgemeinen Selektor finden, der unseren Anforderungen entspricht.

definition.css('div.<meaning|example|contributor>').xpath(".//text()").extract()

// returns an array with all the text found inside the <meaning|example|contributor>

ex: ['Yolo ', 'means', ', '', 'You Only Live Once', ''.']

Weitere Informationen zu Scrapy-Selektoren finden Sie in der Dokumentation. https://docs.scrapy.org/en/latest/topics/selectors.html

Implementierung des Urban-Dictionary-Scrapers

Tolle Arbeit! Jetzt, da du den Dreh mit der Scrapy-Shell raus hast und die Funktionsweise eines Scrapy-Projekts verstehst, ist es an der Zeit, uns mit der Implementierung unseres Urban-Dictionary-Scrapers zu befassen. Inzwischen solltest du dich sicher fühlen und bereit sein, die Aufgabe anzugehen, all diese urkomischen (und manchmal fragwürdigen) Wortdefinitionen aus dem Web zu extrahieren. Also, ohne weitere Umschweife: Lass uns mit der Erstellung unseres Scrapers beginnen!

Ein Item definieren

Zunächst implementieren wir ein Item: (siehe items.py)

class UrbanDictionaryItem(scrapy.Item):

    meaning = scrapy.Field()

    author = scrapy.Field()

    date = scrapy.Field()

    example = scrapy.Field()

Diese Struktur wird die vom Spider gescrapten Daten enthalten.

Definition eines Spiders

So definieren wir unseren Spider (siehe /spiders):

import scrapy

from ..items import UrbanDictionaryItem

class UrbanDictionarySpider(scrapy.Spider):

name = 'urban_dictionary'

start_urls = ['https://www.urbandictionary.com/define.php?term=Yolo']

def parse(self, response):

definitions = response.css('div.definition')

for definition in definitions:

item = UrbanDictionaryItem()

item['meaning'] = definition.css('div.meaning').xpath(".//text()").extract()

item['example'] = definition.css('div.example').xpath(".//text()").extract()

author = definition.css('div.contributor').xpath(".//text()").extract()

item['date'] = author[2]

item['author'] = author[1]

yield item

Um den urban_dictionary-Spider auszuführen, verwenden Sie den folgenden Befehl:

scrapy crawl urban_dictionary

// the results should appear in the console (most probably at the top of the logs)

Erstellen einer Pipeline

Zu diesem Zeitpunkt sind die Daten noch nicht bereinigt.

{'author': 'Soy ugly',

 'date': ' April 24, 2019',

 'example': ['“Put your ',

             'seatbelt',

             ' on.” Jessica said.\n',

             '“HAH, YOLO!” Replies Anna.\n',

             '(They then proceed to have a ',

             'car crash',

             '. ',

             'Long story short',

             '...Wear a seatbelt.)'],

 'meaning': ['Yolo ', 'means', ', ‘', 'You Only Live Once', '’.']}

Wir möchten die Felder „example“ und „meaning“ so ändern, dass sie Zeichenfolgen statt Arrays enthalten. Dazu schreiben wir eine Pipeline (siehe pipelines.py), die die Arrays durch Verknüpfung der Wörter in Zeichenfolgen umwandelt.

class SanitizePipeline:

    def process_item(self, item, spider):

        # Sanitize the 'meaning' field

        item['meaning'] = "".join(item['meaning'])

        # Sanitize the 'example' field

        item['example'] = "".join(item['example'])

       

        # Sanitize the 'date' field

        item['date'] = item['date'].strip() 

        return item

 //ex: ['Yolo ', 'means', ', ‘', 'You Only Live Once', '’.'] turns to 

       'Yolo means, ‘You Only Live Once’.'

Aktivieren der Pipeline

Nachdem wir die Pipeline definiert haben, müssen wir sie aktivieren. Tun wir dies nicht, werden unsere UrbanDictionaryItem-Objekte nicht bereinigt. Fügen Sie dazu folgende Zeile in Ihre Datei settings.py (siehe settings.py) ein:

 ITEM_PIPELINES = {

     'myproject.pipelines.SanitizePipeline': 1,

 }

Wenn Sie schon dabei sind, können Sie auch eine Ausgabedatei angeben, in der die gescrapten Daten gespeichert werden sollen.

 FEEDS = {

     'items': {'uri': 'file:///tmp/items.json', 'format': 'json'},

 }

  // this will put the scraped data in an items.json file.

Optional: Implementierung einer Proxy-Middleware

Web-Scraping kann etwas mühsam sein. Eines der Hauptprobleme, auf das wir oft stoßen, ist, dass viele Websites JavaScript-Rendering erfordern, um ihre Inhalte vollständig anzuzeigen. Dies kann für uns als Web-Scraper zu großen Problemen führen, da unsere Tools oft nicht in der Lage sind, JavaScript wie ein normaler Webbrowser auszuführen. Dies kann dazu führen, dass unvollständige Daten extrahiert werden oder, schlimmer noch, dass unsere IP-Adresse von der Website gesperrt wird, weil wir in kurzer Zeit zu viele Anfragen stellen.

Unsere Lösung für dieses Problem ist WebScrapingApi. Mit unserem Dienst können Sie einfach Anfragen an die API stellen, und diese übernimmt die ganze Arbeit für Sie. Sie führt JavaScript aus, wechselt Proxys und bewältigt sogar CAPTCHAs, sodass Sie selbst die hartnäckigsten Websites mühelos scrapen können.

Eine Proxy-Middleware leitet jede von Scrapy gestellte Abrufanfrage an den Proxy-Server weiter. Der Proxy-Server stellt dann die Anfrage für uns und gibt das Ergebnis zurück.

Zunächst definieren wir eine ProxyMiddleware-Klasse in der Datei middlewares.py.


import base64

class ProxyMiddleware:

    def process_request(self, request, spider):

        # Set the proxy for the request

        request.meta['proxy'] = "http://proxy.webscrapingapi.com:80"

        request.meta['verify'] = False

        # Set the proxy authentication for the request

        proxy_user_pass =  "webscrapingapi.proxy_type=residential.render_js=1:<API_KEY>"

        encoded_user_pass = base64.b64encode(proxy_user_pass.encode()).decode()

        request.headers['Proxy-Authorization'] = f'Basic {encoded_user_pass}'


In diesem Beispiel haben wir den WebScrapingApi-Proxyserver verwendet.

webscrapingapi.proxy_type=residential.render_js=1 ist der Benutzername für die Proxy-Authentifizierung, <API_KEY> das Passwort.

Sie können einen kostenlosen API_KEY erhalten, indem Sie ein neues Konto unter https://www.webscrapingapi.com/ erstellen

Nachdem wir die ProxyMiddleware definiert haben, müssen wir sie nur noch als DOWNLOAD_MIDDLEWARE in der Datei settings.py aktivieren.

DOWNLOADER_MIDDLEWARES = {

    'myproject.middlewares.ProxyMiddleware': 1,

}

Und das war’s schon. Wie Sie sehen, kann Web-Scraping mit Scrapy unsere Arbeit erheblich vereinfachen.

Zusammenfassung

Wir haben es geschafft! Wir haben einen Scraper erstellt, der Definitionen aus Urban Dictionary extrahieren kann, und dabei viel über Scrapy gelernt. Von der Erstellung benutzerdefinierter Elemente bis hin zur Nutzung von Middlewares und Pipelines haben wir gezeigt, wie leistungsstark und vielseitig Web-Scraping mit Scrapy sein kann. Und das Beste daran? Es gibt noch so viel mehr zu entdecken.

Herzlichen Glückwunsch, dass Sie diese Reise mit mir bis zum Ende durchgehalten haben. Sie sollten sich nun sicher fühlen, jedes Web-Scraping-Projekt anzugehen, das Ihnen begegnet. Denken Sie einfach daran: Web-Scraping muss nicht einschüchternd oder überwältigend sein. Mit den richtigen Tools und dem richtigen Wissen kann es eine unterhaltsame und lohnende Erfahrung sein. Wenn Sie jemals Hilfe benötigen, zögern Sie nicht, uns hier unter https://www.webscrapingapi.com/ zu kontaktieren. Wir kennen uns bestens mit Web-Scraping aus und helfen Ihnen gerne auf jede erdenkliche Weise weiter.

Über den Autor
Mihai Maxim, Full-Stack-Entwickler @ WebScrapingAPI
Mihai MaximFull-Stack-Entwickler

Mihai Maxim ist Full-Stack-Entwickler bei WebScrapingAPI, wo er in verschiedenen Bereichen des Produkts mitwirkt und an der Entwicklung zuverlässiger Tools und Funktionen für die Plattform mitarbeitet.

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.