ImmobilienScout24 Mining Tutorial: Der Web Scraper
EDIT: Da Immoscout mittlerweile Bot Blocker nutzt, funktioniert der Web Scraper leider nicht mehr.
Wie versprochen gibt es hier das Tutorial zur Artikelreihe über ImmobilienScout24 Mining. Hier erfährst du genau, wie du den Web Scraper baust, mit dem die Daten geschürft wurden.
Auchtung! Um dieses Tutorial genau nachvollziehen zu können, sind zumindest grundlegende Programmierkenntnisse wünschenswert. Den Scraper habe ich in Python geschrieben. Hier ist das komplette Skript als HTML: ImmobilienScout24 Mining
Der Scraper: Grundidee und Vorgehensweise
Bevor wir detailliert in die Beschreibung des Codes einsteigen, möchte ich kurz zusammenfassen, was der Scraper überhaupt macht.
Als erstes besucht der Scraper genau diese Seite. Sieht aus wie die normale erste Ergebnisseite ohne sonstige Suchkriterien. Das stimmt auch fast. Alle auf ImmobilienScour24 zu findenden Wohnungen in Deutschland werden vorgeschlagen. Allerdings sind die Angebote hier schon so sortiert, dass die neuesten ganz oben stehen. Wichtig! Ändert man die Art der Sortierung nämlich nicht und bleibt bei der Standardsortierung, dann sind die Wohnungen zuerst zu sehen, die eine bezahlte Premiumplatzierung haben. Mit dieser Sortierung würden wir immer wieder dieselben Daten speichern.
Nachdem der Scraper auf der Seite angelangt ist, durchsucht er den Quellcode nach URLs, die den Text „/expose/“ enthalten und speichert diese in einer Liste. Die gespeicherten URLs führen nämlich zu den einzelnen Wohnungsangeboten. Insgesamt sind auf der ersten Ergebnisseite 20 Wohnungen zu sehen. Nachdem die Liste mit den URLs befüllt wurde, geht der Scraper diese Schritt für Schritt durch. In jedem Schritt besucht der Scraper eine der URLs und durchforstet auch hier den Quellcode. Die relevanten Stellen werden ausgelesen und mit diesen Daten wird eine Tabelle befüllt. Mit jeder URL wird unsere Datenbasis eine Zeile länger. Wenn die Daten aller 20 Wohnungen in der Tabelle gespeichert wurden, wird diese als CSV-Datei in einem Ordner abgelegt.
So weit, so gut. Und dann?
Da wir nicht nur von 20 Wohnungen die Daten speichern wollen, durchläuft der Scraper eine (Endlos-)Schleife. Nach jeden Durchlauf „schläft“ der Scraper für eine gewisse Zeit, damit die Seite sich aktualisieren kann. Dann speichert er erneut die aktuellsten 20 Angebote. Je kürzer die Pause, desto weniger Wohnungen entwischen dem Programm. Damit kann jedoch auch der Prozentsatz der Duplikate größer werden. Wählt man die Schlafzeit hingegen sehr großzügig, dann sind gar keine Duplikate im Datensatz enthalten. Dafür sind einem mit Sicherheit einige Daten durch die Lappen gegangen.
Ich habe mich für eine Pause von einer Minute entschieden. Allerdings befürchte ich, dass vor allem nachts sehr viele Duplikate in den Datensatz fließen, da wahrscheinlich nur die wenigsten Makler und Privatpersonen Angebote um diese Uhrzeit posten. Nach einem 24-stündigen Testlauf betrug der Prozentsatz eindeutiger Wohnungen unter allen Angeboten nur rund 10 Prozent. Trotzdem habe ich mich dafür entschieden, die Schlafzeit des Scrapers nicht zu verlängern, da die Duplikate im endgültigen Datensatz ohne weiteres wieder gelöscht werden können. Man sollte natürlich immer den noch verfügbaren Speicherplatz im Auge behalten. Bei sehr großen Datensätzen bzw. geringem Speicherplatz kann es sich lohnen, die Zahl der Duplikate möglichst klein zu halten.
Und jetzt zum Code!
Hallo,
danke für die detaillierte Erklärung der einzelnen Codezeilen. Beim nachbauen habe ich allerdings die Orientierung verloren, welche COdezeile zu welchem Block (Try, For, etc.) gehört. Könntest Du vielleicht nochmal den kompletten Code posten, damit man sieht, wie die Einrückungen sind?
Danke
ein Pythonanfänger
… jetzt habe ich es selbst gefunden. Sorry.
Kein Ding! Hat’s funktioniert? Bei weiteren Fragen kannst du auch gern eine Mail schreiben.
Danke für das Tutorial! Sehr gut erklärt.
Ich möchte mit der gesamelten Data noch ein paar Rechnungen innerhalb des Skriptes einbauen.
Wie könnte ich z.B. Python jetzt sagen „mit obj_purchasePrice und obj_livingSpace rechne mir den m² -Preis“? Leider bin ich Anfänger und komme hier nicht weiter.
Hi Matthias,
danke für das Feedback!
Zu deiner Frage: Wenn du das Tutorial abgeschlossen hast, liegen ja alle Daten gesammelt in einem DataFrame vor. Mit den Spalten des DataFrames kannst du natürlich auch weitere Berechnungen durchführen. Um mit Hilfe der Merkmale „obj_purchasePrice“ und „obj_livingSpace“ eine neue Spalte mit dem Quadratmeterpreis zu erstellen, machst du folgendes:
df[„qm_preis“] = df.obj_purchasePrice/df.obj_livingSpace
oder
df[„qm_preis“] = df[„obj_purchasePrice“]/df[„obj_livingSpace“]
Beide Befehle äquivalent zueinander. In derselben Weise kannst du mit „*“,“+“ und „-“ auch Spalten miteinander multiplizieren, addieren und voneinander subtrahieren.
Sag Bescheid, wenn’s geklappt hat! 🙂
Hallo Cris
ist es auch möglich nicht nur aktuelle Daten auszulesen, oder besteht die Möglichkeit auch historische
Immobiliendaten beim Scout auszulesen.
Finde deine Projekte extrem spannend.
LG
Klaus
Hi Klaus,
danke für dein Feedback! 🙂
Natürlich kannst du generell nur das scrapen, was sich auch auf der Seite finden lässt. Allerdings gibt es die Möglichkeit, nicht nur immer die neuen Angebote auf der ersten Seite zu scrapen, sondern alle Angebote auszulesen, die im Moment online sind. Genau das habe ich im Beitrag zu Häusern auf ImmobilienScout24 gemacht.
Der Web Scraper für Häuser
Bei Fragen schreib mir gern auf meiner Facebookseite eine Nachricht. -> StatisQuo auf Facebook
Viele Grüße
Chris
Hallo Chris,
vielen Dank für die gute Vorlage und die entsprechenden Analysen, wirklich sehr spannend!
Eine Frage zu dem Code: nachdem die keyValues ja doch recht umfangreich sind, wie würde man denn den Code dahingehend anpassen, dass nur einige der interessanteren features ausgelesen werden (e.g. „obj_yearConstructed“, „obj_totalRent“, …)?
Viele Grüße,
Simon
Hallo,
was eigentlich noch fehlt wäre das auslesen, auf welcher Seite man sich auf immobilienscout befindet. Bei mehr Treffer als 20 gibt es ja mehrere Seiten, auf denen die Ergebnisse angezeigt werden.
Hi Patrick,
man befindet sich immer auf Seite 1.
Hallo Chris,
danke erst mal für super spannenden Blogeinträge! Mir ist aufgefallen, dass über die “keyValues” leider keine Variable für das Hausgeld gefunden wird. Hast du eine Idee woran das liegen könnte und wie ich die Variable zu dem Scraper hinzufügen kann? Bei den meisten Postings auf Immobilienscout 24 scheint das Hausgeld auf jeden Fall angegeben zu sein und brauche diese Variable sehr dringend.
Beste Grüße
Jannis
Hallo alle,
ich scrape seit einigen Monaten regelmäßig die Immoscout seite. Dabei mache ich eine Umkreis-Suche. Die Seite mit den Suchergebnissen lässt sich seit etwa einer Woche nicht mehr scrapen. Habt ihr ähnliche Probleme und könnte es dafür eine Lösung geben?
Danke
Laura
Hi Laura,
Immoscout scheint Scraper jetzt zu blockieren, siehe die unteren Kommentare unter dem Beitrag zu den Häuserdaten.
Die Alternative dazu ist entweder mit Selenium einen Browser zu simulieren oder die API zu nutzen. Hättest du Interesse an einem Tutorial für die API? Vorweg: Um die zu nutzen, braucht man einen Developer Account bei Immoscout.
Viele Grüße
Chris
Auch falls jemand anderes eine Idee hat, wie ich das Hausgeld zu den „keyValues“ hinzufügen kann, wäre ich sehr dankbar. 🙂
Beste Grüße
Jannis
Hallo Chris,
ich wollte mich auch gerade ans scrapen machen und musste das gleiche feststellen. Also habe ich mir mal die API Geschichte angesehen und auch einen Zugang bekommen. Für mich sieht es aber danach aus als könnte man nur grundlegende Infos Abfragen zu konkreten Exposés und keine generelle Suche starten. Oder geht das doch?
Ansonsten gehe ich das Thema mit selenium an.
Viele Grüße,
Marius
Hi Marius,
zu Selenium: Hab ich letztens ausprobiert, man wird quasi direkt als Bot erkannt. Ich konnte einmal in der Suche ein paar Exposé IDs abgreifen, aber dann nicht weiter machen…
Also kannst du die API auch als nicht-Content-Partner benutzen? Was sind denn so die Infos zu den Exposés? Weniger als beim Scraper?
Echt schade, dass Immoscout uns das Leben so schwer macht.
Viele Grüße
Chris
Das ist ja eine sehr geniale Darstellung. Danke dafür!
Ich hätte aber irgendwie ein Problem am Anfang:
Wenn ich den Loop ausführe, dann kommt eine für mich nicht klare EOF parcing – Fehlermeldung
File „“, line 9
soup = bs.BeautifulSoup(urllib.request.urlopen(‚https://www.immobilienscout24.de/Suche/S-2/Wohnung-Miete‘).read(), ‚lxml‘)
^
SyntaxError: unexpected EOF while parsing
Kannst Du bitte einen Tipp geben, woran das liegen kann?
Hi Roman,
den Fehler kann ich leider nicht reproduzieren. Bist du sicher, dass du den Code 1:1 kopiert hast? Ist eigentlich ein einfacher Syntaxfehler. Könnte sein, dass eine Zeile fehlt.
Aber da Immobilienscout jetzt Scraper blockt, dürfte der Code eh nicht mehr funktionieren leider, siehe Kommentaren unter dem anderen Beitrag…
Viele Grüße
Chris
Hallo Chris,
ich habe deinen Code bei immowelt.de verwendet. Dazu musste ich ihn bei links etc. anpassen.
Dabei habe ich festgestellt, dass ich immer nur den Inhalt der ersten Seite bekomme.
Wenn ich dem Link aus der Abfrage folge, dann wird auch immer nur die erste Seite angezeigt.
Kannst Du mir eine Empfehlung geben, wie ich das Problem behebe?
Unten der Codeteil, an dem ich jetzt arbeite.
import bs4 as bs
import urllib.request
import time
from datetime import datetime
import pandas as pd
import json
for seite in range(1,5):
print(„Loop “ + str(seite) + “ startet.“)
df = pd.DataFrame()
l=[]
soup = bs.BeautifulSoup(urllib.request.urlopen(„https://www.immowelt.de/gewerbeliste/bl-bayern/renditeobjekte/kaufen?sd=DESC&sf=RELEVANCE&sp“+str(seite)).read(),’lxml‘)
print(„Aktuelle Seite: „+“https://www.immowelt.de/gewerbeliste/bl-bayern/renditeobjekte/kaufen?sd=DESC&sf=RELEVANCE&sp“+str(seite))
for paragraph in soup.find_all(„a“):
if r“/expose/“ in str(paragraph.get(„href“)):
l.append(paragraph.get(„href“).split(„#“)[0])
#l = list(set(l))
Ich ziehe meine Frage zurück. Es funktioniert. Es war nur etwas tricky, weil die das letzte Zeichen in dem Link sich änderte. 🙂
woran ich aber jetzt tatsächlich gescheitert bin, ist der Fehler unten:
ID https://www.immowelt.de/expose/2w7m84g entfernt.
2020-09-04 00:10:31.919064: could not broadcast input array from shape (22) into shape (1)
es liegt wohl an der Beschreibung der Merkmale, die in Form einer Liste einzeln gespeichert werden.
Wenn das Programm versucht, diese zu parsen, schafft es das nicht, weil die Länge anders ist.
Was könnte man hier machen? Kann mir jemand einen Tipp geben?
Hallo Chris,
verstehe ich das nun richtig, dass es damit gerade keine Lösung gibt?
API kriegt ich keinen Zugriff. Selenium und BS wird beides blockiert.
Weist du noch eine Alternative oder hab ich etwas übersehen?
Viele Grüße
Dominik
Hi Chris,
ich wurde wegen dem Scraping blockiert.
Obwohl ich für jeden Loop einen anderen Proxy benutze, habe ich keinen Zugang zur Webseite mehr.
Hast du einen Tipp für mich?
Danke
Nicola
Hi, wenn ich scrape komm keine „a“ mit expose. Also die Liste „l“ ist leer!