Anleitung zum Scrapen von Reddit in Python

In dieser Schritt-für-Schritt-Anleitung erfahren Sie, wie Sie Reddit mit Python scrapen und die Reddit-API-Gebühren vermeiden können.
16 min read
How to scrape Reddit in Python

In dieser Schritt-für-Schritt-Anleitung erfahren Sie, wie Sie Reddit mit Python scrapen können.

In dieser Anleitung geht es um Folgendes:

  • Neue Reddit-API-Richtlinie
  • Reddit-API im Vergleich zu Reddit-Scraping
  • Scrapen von Reddit mit Selenium

Neue Reddit-API-Richtlinie

Im April 2023 kündigte Reddit neue Gebühren für seine Daten-APIs an, die sich kleinere Unternehmen im Grunde nicht mehr leisten können. Zum Zeitpunkt der Erstellung dieses Artikels liegt die API-Gebühr bei 0,24 $ pro 1.000 Aufrufe. Wie Sie sich vorstellen können, kann sich dieser Betrag selbst bei geringer Nutzung schnell summieren. Das gilt vor allem, wenn man bedenkt, wie viele nutzergenerierte Inhalte auf Reddit zur Verfügung stehen und wie viele Aufrufe erforderlich sind, um sie abzurufen. Apollo, eine der am häufigsten genutzten Drittanbieter-Apps, die auf der Reddit-API aufbaut, musste aus diesem Grund eingestellt werden.

Bedeutet dies das Ende von Reddit als Quelle für Stimmungsanalysen, Nutzerfeedback und Trenddaten? Sicherlich nicht! Es gibt eine Lösung, die effektiver und kostengünstiger ist und nicht von heute auf morgen Entscheidungen des Unternehmens erfordert. Diese Lösung heißt Web Scraping. Lassen Sie uns herausfinden, warum!

Reddit-API im Vergleich zu Reddit-Scraping

Die API von Reddit ist die offizielle Methode zum Abrufen von Daten von der Webseite. In Anbetracht der jüngsten Änderungen der Richtlinien und der von der Plattform eingeschlagenen Richtung gibt es gute Gründe, warum Reddit-Scraping eine bessere Lösung ist:

  • Kosteneffizienz: In Anbetracht der neuen API-Kosten von Reddit kann das Scraping von Reddit eine viel günstigere Alternative sein. Wenn Sie einen Python-Reddit-Scraper entwickeln, können Sie Daten sammeln, ohne dass Ihnen zusätzliche Kosten für die API-Nutzung entstehen.
  • Verbesserte Datenerfassung: Beim Scrapen von Reddit haben Sie die Möglichkeit, den Datenextraktionscode so anzupassen, dass Sie nur die Informationen erhalten, die Ihren Anforderungen entsprechen. Diese Anpassung hilft Ihnen, die Einschränkungen des Datenformats, die Ratenbegrenzung und die Nutzungsbeschränkungen der API zu überwinden.
  • Zugang zu inoffiziellen Daten: Während die API von Reddit nur Zugang zu einer ausgewählten Auswahl an Informationen bietet, ermöglicht Scraping den Zugriff auf alle öffentlich zugänglichen Daten auf der Webseite.

Da Sie nun wissen, warum Scraping eine effektivere Option als der Aufruf von APIs ist, wollen wir uns ansehen, wie man einen Reddit-Scraper in Python erstellt. Bevor Sie zum nächsten Kapitel übergehen, sollten Sie sich unsere ausführliche Anleitung zum Web Scraping mit Python ansehen.

Scrapen von Reddit mit Selenium

In diesem Tutorial erfahren Sie Schritt für Schritt, wie Sie ein Python-Skript für das Web-Scraping von Reddit erstellen können.

Schritt 1: Einrichtung des Projekts

Stellen Sie zunächst sicher, dass Sie die folgenden Voraussetzungen erfüllen:

Initialisieren Sie mit den folgenden Befehlen ein Python-Projekt mit einer virtuellen Umgebung:


mkdir reddit-scraper
cd reddit-scraper
python -m venv env

Der hier erstellte Ordner reddit-scraper ist der Projektordner für Ihr Python-Skript.

Öffnen Sie das Verzeichnis in der IDE, erstellen Sie eine scraper.py-Datei und initialisieren Sie sie wie unten beschrieben:

print('Hello, World!')

Im Moment gibt dieses Skript einfach „Hello, World!“ aus, aber es wird bald die Scraping-Logik enthalten.

Überprüfen Sie, ob das Programm funktioniert, indem Sie die Schaltfläche „Ausführen“ in Ihrer IDE drücken oder das Programm starten:

python scraper.py

Ihr Gerät sollte Folgendes angezeigen:

Hello, World!

Wunderbar! Sie haben jetzt ein Python-Projekt für Ihren Reddit-Scraper.

Schritt 2: Auswahl und Installation der Scraping-Bibliotheken

Wie Sie vielleicht bereits wissen, ist Reddit eine äußerst interaktive Plattform. Die Webseite lädt und rendert neue Daten dynamisch, je nachdem, wie die Nutzer durch Klicken und Scrollen mit den Seiten interagieren. Aus technischer Sicht bedeutet dies, dass Reddit stark auf JavaScript angewiesen ist.

Für das Scrapen von Reddit in Python ist daher ein Tool erforderlich, das Webseiten in einem Browser darstellen kann. Hier kommt Selenium ins Spiel! Dieses Tool ermöglicht das Scrapen von dynamischen Webseiten in Python und ermöglicht automatisierte Prozesse auf Webseiten in einem Browser.

Sie können Selenium und den Webdriver Manager zu den Abhängigkeiten Ihres Projekts mit hinzufügen:

pip install selenium webdriver-manager

Der Installationsprozess kann eine Weile dauern, haben Sie also etwas Geduld.

Das Paket webdriver-manager ist nicht unbedingt erforderlich, wird aber dringend empfohlen. Es ermöglicht Ihnen, das manuelle Herunterladen, Installieren und Konfigurieren von Web-Treibern in Selenium zu vermeiden. Die Bibliothek wird sich um alles für Sie kümmern.

Integrieren Sie Selenium in Ihre scraper.py-Datei:


from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options

# enable the headless mode
options = Options()
options.add_argument('--headless=new')

# initialize a web driver to control Chrome
driver = webdriver.Chrome(
    service=ChromeService(ChromeDriverManager().install()),
    options=options
)
# maxime the controlled browser window
driver.fullscreen_window()

# scraping logic...

# close the browser and free up the Selenium resources
driver.quit()

Dieses Skript erstellt ein Chrome WebDriver-Objekt, um ein Chrome-Fenster programmatisch zu steuern.

Standardmäßig öffnet Selenium den Browser in einem neuen GUI-Fenster. Dies ist nützlich, um zu überwachen, was das Skript auf den Seiten zum Debuggen tut. Gleichzeitig benötigt das Laden eines Webbrowsers mit seiner Benutzeroberfläche eine Menge Ressourcen. Es wird daher empfohlen, Chrome so zu konfigurieren, dass es im Headless-Modus läuft.. Insbesondere die Option --headless=new weist Chrome an, ohne Benutzeroberfläche zu starten.

Gut gemacht! Zeit für einen Besuch der Reddit-Zielseite!

Schritt 3: Mit Reddit verbinden

Hier erfahren Sie, wie Sie Daten aus dem Subreddit r/Technology extrahieren können. Denken Sie daran, dass jedes andere Subreddit ausreicht.

Nehmen wir an, Sie möchten die Seite mit den wichtigsten Beiträgen der Woche abrufen. Dies ist die URL der Zielseite:

https://www.reddit.com/r/technology/top/?t=week

Speichern Sie diese Zeichenfolge in einer Python-Variablen:

url = 'https://www.reddit.com/r/technology/top/?t=week'

Verwenden Sie dann Selenium, um die Seite aufzurufen:

driver.get(url)

Die Funktion get() weist den kontrollierten Browser an, sich mit der Seite zu verbinden, die durch die als Parameter übergebene URL identifiziert wird.

So sieht Ihr Reddit Web Scraper bis jetzt aus:


from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options

# enable the headless mode
options = Options()
options.add_argument('--headless=new')

# initialize a web driver to control Chrome
driver = webdriver.Chrome(
    service=ChromeService(ChromeDriverManager().install()),
    options=options
)
# maxime the controlled browser window
driver.fullscreen_window()

# the URL of the target page to scrape
url = 'https://www.reddit.com/r/technology/top/?t=week'
# connect to the target URL in Selenium
driver.get(url)

# scraping logic...

# close the browser and free up the Selenium resources
driver.quit()

Testen Sie Ihr Skript. Es öffnet das unten stehende Browserfenster für den Bruchteil einer Sekunde, bevor es aufgrund der quit()-Anweisung geschlossen wird:

Reddit Scraping

Werfen Sie einen Blick auf die Meldung „Chrome wird von einer automatisierten Testsoftware gesteuert“. Klasse! Dadurch wird sichergestellt, dass Selenium unter Chrome ordnungsgemäß funktioniert.

Schritt 4: Untersuchen Sie die Ziel-Website

Bevor Sie sich in den Code stürzen, müssen Sie die Zielseite untersuchen, um herauszufinden, welche Informationen sie bietet und wie Sie diese abrufen können. Insbesondere müssen Sie herausfinden, welche HTML-Elemente die gewünschten Daten enthalten und geeignete Auswahlstrategien entwickeln.

Öffnen Sie die Reddit-Seite im Modus „anonym“, um die Bedingungen zu simulieren, unter denen Selenium arbeitet, d. h. eine „normale“ Browsersitzung. Klicken Sie mit der rechten Maustaste auf einen Abschnitt auf der Seite, und wählen Sie „Inspect“, um die Chrome DevTools zu öffnen:

Dieses Tool hilft Ihnen, die DOM-Struktur der Seite zu verstehen. Wie Sie sehen können, stützt sich die Seite auf CSS-Klassen, die zum Zeitpunkt der Erstellung scheinbar zufällig generiert werden. Mit anderen Worten: Sie sollten Ihre Auswahlstrategien nicht auf diese Klassen stützen.

Reddit Scraping fortgesetzt

Glücklicherweise haben die wichtigsten Elemente der Webseite spezielle HTML-Attribute. Der Subreddit-Beschreibungsknoten hat zum Beispiel das folgende Attribut:

data-testid="no-edit-description-block"

Dies ist eine nützliche Information für den Aufbau einer effektiven HTML-Elementauswahllogik.

Analysieren Sie die Webseite weiterhin in den DevTools und machen Sie sich mit ihrem DOM vertraut, bis Sie bereit sind, Reddit in Python zu scrapen.

Schritt 5: Scrapen Sie die Hauptinformationen des Subreddits

Erstellen Sie zunächst ein Python-Wörterbuch, in dem Sie die gescrapten Daten speichern:

subreddit = {}

Beachten Sie dann, dass Sie den Namen des Subreddits aus dem

-Element oben auf der Seite abrufen können:

Rufen Sie es wie folgt ab:


name = driver \
    .find_element(By.TAG_NAME, 'h1') \
    .text

Wie Sie sicher schon bemerkt haben, befinden sich einige der interessantesten allgemeinen Informationen über den Subreddit in der Seitenleiste auf der rechten Seite:

Reddit Seitenleiste

Sie können die Textbeschreibung, das Erstellungsdatum und die Anzahl der Mitglieder mit abrufen:


description = driver \
    .find_element(By.CSS_SELECTOR, '[data-testid="no-edit-description-block"]') \
    .get_attribute('innerText')

creation_date = driver \
    .find_element(By.CSS_SELECTOR, '.icon-cake') \
    .find_element(By.XPATH, "following-sibling::*[1]") \
    .get_attribute('innerText') \
    .replace('Created ', '')

members = driver \
    .find_element(By.CSS_SELECTOR, '[id^="IdCard--Subscribers"]') \
    .find_element(By.XPATH, "preceding-sibling::*[1]") \
    .get_attribute('innerText')

In diesem Fall können Sie das Attribut text nicht verwenden, da die Textstrings in verschachtelten Knoten enthalten sind. Wenn Sie .text verwenden würden, bekämen Sie eine leere Zeichenfolge. Stattdessen müssen Sie die Methode get_attribute() aufrufen, um das Attribut innerText zu lesen, das den gerenderten Textinhalt eines Knotens und seiner Unterknoten zurückgibt.

Wenn Sie sich das Element Erstellungsdatum ansehen, werden Sie feststellen, dass es nicht einfach auszuwählen ist. Da es sich um den Knoten handelt, der auf das Kuchensymbol folgt, wählen Sie zunächst das Symbol mit .icon-cake aus und verwenden dann den XPath-Ausdruck following-sibling::*[1], um das nächste Element zu erhalten. Bereinigen Sie den gesammelten Text, um die Zeichenfolge „Created“ zu entfernen, indem Sie die Python-Methode replace() aufrufen.

Beim Element „subscriber member counter“ geschieht etwas Ähnliches. Der Hauptunterschied besteht darin, dass Sie in diesem Fall auf das vorhergehende Element zugreifen müssen.

Vergessen Sie nicht, die gescrapten Daten in das subreddit-Wörterbuch aufzunehmen:


subreddit['name'] = name
subreddit['description'] = description
subreddit['creation_date'] = creation_date
subreddit['members'] = members

Drucken Sie subreddit mit print(subreddit), und Sie werden Folgendes sehen:

{'name': '/r/Technology', 'description': 'Subreddit dedicated to the news and discussions about the creation and use of technology and its surrounding issues.', 'creation_date': 'Jan 25, 2008', 'members': '14.4m'}

Perfekt! Sie haben gerade Web Scraping in Python durchgeführt!

Schritt 6: Scrapen der Subreddit-Beiträge

Da ein Subreddit mehrere Beiträge enthält, benötigen Sie nun ein Array, um die gesammelten Daten zu speichern:

posts = []

Untersuchen Sie ein post-HTML-Element:

Hier können Sie feststellen, dass Sie alle Beiträge mit dem [data-testid="post-container"] CSS-Selektor auswählen können:


post_html_elements = driver \
    .find_elements(By.CSS_SELECTOR, '[data-testid="post-container"]')

Iterieren Sie über sie. Erstellen Sie für jedes Element ein Beitragswörterbuch, um die Daten der einzelnen Beiträge zu verfolgen:


for post_html_element in post_html_elements:
    post = {}

    # scraping logic...

Untersuchen Sie das Element „upvote“:

Upvote inspizieren

Sie können diese Informationen innerhalb der for-Schleife mit abrufen:


upvotes = post_html_element \
    .find_element(By.CSS_SELECTOR, '[data-click-id="upvote"]') \
    .find_element(By.XPATH, "following-sibling::*[1]") \
    .get_attribute('innerText')

Auch hier ist es am besten, die „Upvote“-Schaltfläche zu nehmen, die leicht auszuwählen ist, und dann auf das nächste Element zu verweisen, um die Zielinformationen abzurufen.

Untersuchen Sie die Elemente Autor und Titel des Beitrags:

Diese Daten sind etwas einfacher zu beschaffen:


author = post_html_element \
    .find_element(By.CSS_SELECTOR, '[data-testid="post_author_link"]') \
    .text

title = post_html_element \
    .find_element(By.TAG_NAME, 'h3') \
    .text

Dann können Sie die Anzahl der Kommentare und den ausgehenden Link erfassen:

Kommentare und ausgehende Links

try:
    outbound_link = post_html_element \
        .find_element(By.CSS_SELECTOR, '[data-testid="outbound-link"]') \
        .get_attribute('href')
except NoSuchElementException:
    outbound_link = None

comments = post_html_element \
    .find_element(By.CSS_SELECTOR, '[data-click-id="comments"]') \
    .get_attribute('innerText') \
    .replace(' Comments', '')

Da das Element für den ausgehenden Link optional ist, müssen Sie die Auswahllogik mit einem try-Block verpacken.

Fügen Sie diese Daten zum post hinzu und fügen Sie sie nur dann an das posts-Array an, wenn der title vorhanden ist. Durch diese zusätzliche Prüfung wird verhindert, dass spezielle, von Reddit platzierte Werbebeiträge abgegriffen werden:


# populate the dictionary with the retrieved data
post['upvotes'] = upvotes
post['title'] = title
post['outbound_link'] = outbound_link
post['comments'] = comments

# to avoid adding ad posts 
# to the list of scraped posts
if title:
    posts.append(post)

Zum Schluss fügen Sie die posts zum subreddit-Wörterbuch hinzu:

subreddit['posts'] = posts

Das war’s! Sie haben nun alle gewünschten Reddit-Daten!

Schritt 7: Exportieren Sie die gescrapten Daten in JSON

Die gesammelten Daten befinden sich jetzt in einem Python-Wörterbuch. Dies ist nicht das beste Format, um sie mit anderen Teams zu teilen. Um das zu ändern, sollten Sie die Daten nach JSON exportieren:


import json

# ...

with open('subreddit.json', 'w') as file:
    json.dump(video, file)

Importieren Sie json aus der Python-Standardbibliothek, erstellen Sie eine subreddit.json-Datei mit open() und füllen Sie sie mit json.dump(). In unserem Leitfaden erfahren Sie mehr darüber, wie man JSON in Python parst..

Großartig! Sie haben mit Rohdaten in einer dynamischen HTML-Seite begonnen und verfügen nun über halbstrukturierte JSON-Daten. Sie können nun den gesamten Reddit-Scraper anzeigen.

Schritt 8: Fügen Sie alles zusammen

Hier ist das vollständige scraper.py-Skript:


from selenium import webdriver
from selenium.common import NoSuchElementException
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import json

# enable the headless mode
options = Options()
options.add_argument('--headless=new')

# initialize a web driver to control Chrome
driver = webdriver.Chrome(
    service=ChromeService(ChromeDriverManager().install()),
    options=options
)
# maxime the controlled browser window
driver.fullscreen_window()

# the URL of the target page to scrape
url = 'https://www.reddit.com/r/technology/top/?t=week'
# connect to the target URL in Selenium
driver.get(url)

# initialize the dictionary that will contain
# the subreddit scraped data
subreddit = {}

# subreddit scraping logic
name = driver \
    .find_element(By.TAG_NAME, 'h1') \
    .text

description = driver \
    .find_element(By.CSS_SELECTOR, '[data-testid="no-edit-description-block"]') \
    .get_attribute('innerText')

creation_date = driver \
    .find_element(By.CSS_SELECTOR, '.icon-cake') \
    .find_element(By.XPATH, "following-sibling::*[1]") \
    .get_attribute('innerText') \
    .replace('Created ', '')

members = driver \
    .find_element(By.CSS_SELECTOR, '[id^="IdCard--Subscribers"]') \
    .find_element(By.XPATH, "preceding-sibling::*[1]") \
    .get_attribute('innerText')

# add the scraped data to the dictionary
subreddit['name'] = name
subreddit['description'] = description
subreddit['creation_date'] = creation_date
subreddit['members'] = members

# to store the post scraped data
posts = []

# retrieve the list of post HTML elements
post_html_elements = driver \
    .find_elements(By.CSS_SELECTOR, '[data-testid="post-container"]')

for post_html_element in post_html_elements:
    # to store the data scraped from the
    # post HTML element
    post = {}

    # subreddit post scraping logic
    upvotes = post_html_element \
        .find_element(By.CSS_SELECTOR, '[data-click-id="upvote"]') \
        .find_element(By.XPATH, "following-sibling::*[1]") \
        .get_attribute('innerText')

    author = post_html_element \
        .find_element(By.CSS_SELECTOR, '[data-testid="post_author_link"]') \
        .text

    title = post_html_element \
        .find_element(By.TAG_NAME, 'h3') \
        .text

    try:
        outbound_link = post_html_element \
            .find_element(By.CSS_SELECTOR, '[data-testid="outbound-link"]') \
            .get_attribute('href')
    except NoSuchElementException:
        outbound_link = None

    comments = post_html_element \
        .find_element(By.CSS_SELECTOR, '[data-click-id="comments"]') \
        .get_attribute('innerText') \
        .replace(' Comments', '')

    # populate the dictionary with the retrieved data
    post['upvotes'] = upvotes
    post['title'] = title
    post['outbound_link'] = outbound_link
    post['comments'] = comments

    # to avoid adding ad posts 
    # to the list of scraped posts
    if title:
        posts.append(post)

subreddit['posts'] = posts

# close the browser and free up the Selenium resources
driver.quit()

# export the scraped data to a JSON file
with open('subreddit.json', 'w', encoding='utf-8') as file:
    json.dump(subreddit, file, indent=4, ensure_ascii=False)

Verblüffend! Sie können einen Python Reddit Web Scraper mit etwas mehr als 100 Zeilen Code erstellen!

Starten Sie das Skript, und die folgende subreddit.json-Datei wird im Stammverzeichnis Ihres Projekts erscheinen:


{
    "name": "/r/Technology",
    "description": "Subreddit dedicated to the news and discussions about the creation and use of technology and its surrounding issues.",
    "creation_date": "Jan 25, 2008",
    "members": "14.4m",
    "posts": [
        {
            "upvotes": "63.2k",
            "title": "Mojang exits Reddit, says they '\"no longer feel that Reddit is an appropriate place to post official content or refer [its] players to\".",
            "outbound_link": "https://www.pcgamer.com/minecrafts-devs-exit-its-7-million-strong-subreddit-after-reddits-ham-fisted-crackdown-on-protest/",
            "comments": "2.9k"
        },
        {
            "upvotes": "35.7k",
            "title": "JP Morgan accidentally deletes evidence in multi-million record retention screwup",
            "outbound_link": "https://www.theregister.com/2023/06/26/jp_morgan_fined_for_deleting/",
            "comments": "2.0k"
        },
        # omitted for brevity ...        
        {
            "upvotes": "3.6k",
            "title": "Facebook content moderators in Kenya call the work 'torture.' Their lawsuit may ripple worldwide",
            "outbound_link": "https://techxplore.com/news/2023-06-facebook-content-moderators-kenya-torture.html",
            "comments": "188"
        },
        {
            "upvotes": "3.6k",
            "title": "Reddit is telling protesting mods their communities ‘will not’ stay private",
            "outbound_link": "https://www.theverge.com/2023/6/28/23777195/reddit-protesting-moderators-communities-subreddits-private-reopen",
            "comments": "713"
        }
    ]
}

Herzlichen Glückwunsch! Sie haben gerade gelernt, wie man Reddit in Python scrapen kann!

Fazit

Das Scrapen von Reddit ist eine bessere Möglichkeit, Daten zu erhalten, als die API zu nutzen, insbesondere nach den neuen Richtlinien. In diesem Tutorial haben Sie Schritt für Schritt gelernt, wie man einen Scraper in Python erstellt, um Subreddit-Daten abzurufen. Wie hier gezeigt, sind dafür nur wenige Zeilen Code erforderlich.

Gleichzeitig hat Reddit über Nacht seine API-Richtlinien geändert und wird möglicherweise bald strenge Anti-Scraping-Maßnahmen einführen. Das Extrahieren von Daten aus Reddit würde dann zu einem Kunststück werden, aber es gibt eine Lösung! Der Scraping-Browser von Bright Data ist ein Tool, das JavaScript genau wie Selenium rendern kann und dabei automatisch durch Fingerprinting, CAPTCHAs und Anti-Scraping verursachte Hindernisse für Sie aus dem Weg räumt.

Wenn das nicht Ihr Ding ist, haben wir einen Reddit Scraper entwickelt, der Ihren Bedürfnissen gerecht wird. Dank dieser zuverlässigen und einfach zu bedienenden Lösung können Sie alle Reddit-Daten, die Sie benötigen, ohne weiteres abrufen.

Sie wollen sich nicht mit dem Web-Scraping von Reddit beschäftigen, sind aber an Subreddit-Daten interessiert? Kaufen Sie einen Reddit-Datensatz.