Anzeige
Archiv - Navigation
1688to1692
Aktuelles Verzeichnis
Verzeichnis Index
Übersicht Verzeichnisse
Vorheriger Thread
Rückwärts Blättern
Nächster Thread
Vorwärts blättern
Anzeige
HERBERS
Excel-Forum (Archiv)
20+ Jahre Excel-Kompetenz: Von Anwendern, für Anwender
Inhaltsverzeichnis

VBA web scraping nochmal

VBA web scraping nochmal
07.05.2019 14:10:01
Torsten
Hallo,
ich hatte schon vor kurzen mal um Hilfe bzgl. web scraping gebeten, dank der Hilfe von Zwenn konnte ich dann auch meinen Code so hinfummeln.
Danke dafür nochmal, sieh hier: https://www.herber.de/forum/archiv/1684to1688/t1686352.htm
Ich habe nun aber noch ein paar Probleme, würde mich freuen wenn mir nochmal geholfen werden würde.
Generell weiß ich leider nicht wie ich an die Daten komme, wenn die Struktur der Website und damit der VBA-Code stark von dem Beispiel von Zwenn abweicht.
Meine 1. Frage:
Ich möchte aus einer Seite, welche ich über eine Link mit TeamID erreiche, die darin enthaltenen SpielerID auslesen.
Hier eine Seite als Bsp.: https://trainingslager.onlineliga.de/team/overview/squad?userId=1693
Die Zeile wo die ID steht sieht so aus:
onclick="olAnchorNavigation.load('player/overview', { playerId: 35491 });"
Leider bekomm ich meinen Code jetzt nicht so geändert, das ich die SpielerID (nur die Zahl 35491) aus den Zeilen auslesen kann.
---
Meine 2. Frage:
Falls die TeamID unzulässig ist, also kein Team besteht, kommt folgende Meldung der Seite:
Sorry, the page you are looking for could not be found.
z.B. hier: https://trainingslager.onlineliga.de/team/overview/squad?userId=9000
Ich würde später gerne auch ein web scraping machen, welches guckt ob der Sorry, the page you are looking for could not be found. Text angezeigt wird. Leider komme ich auch hier nicht an die Daten, ich bin leider einfach noch nicht so fit mit VBA und web scraping.
Kann mir bitte jemand mit Code helfen an dem ich mich dann langhangeln und dazulernen kann.
Vielen Dank schonmal und ich freue mich über Antworten

6
Beiträge zum Forumthread
Beiträge zu diesem Forumthread

Betreff
Datum
Anwender
Anzeige
AW: VBA web scraping nochmal
07.05.2019 16:43:33
Zwenn
Hallo Torsten,
nur kurz zur Vorgehensweise, da ich grade noch im Büro sitze:
zu 1.
Das onClick-Ereignis steht in einem span-Tag, das die CSS-Klasse ol-player-name hat. Dieses Tag holst Du Dir z.B. mit der Zeile:
Set knoten = browser.document.getElementsByClassName("ol-player-name")(0)

Nach der Prüfung, ob es abgegriffen werden konnte, liest Du den HTML-Code von knoten als Text aus. Das geht so:
htmlText = knoten.outerHTML

In der Variable htmlText (die vom Datentyp String sein muss) steht nun die folgende Zeile:
<span onclick="olAnchorNavigation.load('player/overview', { playerId: 35491 });" class="ol-player-name">Carsten Gönül</span>

An dieser Stelle ist dass die kleinste Struktur, die wir sinnvoll direkt abgreifen können. Um an die ID selbst zu kommen, musst Du nun den Text so bearbeiten, dass nur die Zahl übrig bleibt.
Ich würde das mit Split() machen und als Delimeter die öffnende geschweifte Klammer setzen. Dann hat das sich ergebende Array nur zwei Elemente, wobei die ID im zweiten Element an einer Stelle steht, die wir nun ganz genau kennen. Also jagst Du das zweite Element nochmal durch Split() und nimmst diesmal das Freizeichen als Delimeter. Im entstehenden Array ist die ID nun auf jeden Fall das zweite Element, weil davor nur playerId: stehen konnte. Die Strings, die Du mit Split auseinander nehmen willst, kapselst Du direkt in Trim(), damit führende und schließende Leerzeichen vor dem Splitten elemeniert werden:
splitArray = Split(Trim(ZuSplittenderText), Delimeter)

Man kann rein theoretisch auch beim ersten Split() schon das Leerzeichen als Delimeter setzen. Davon rate ich aber ab, weil man sich nie sicher sein kann, ob der ganze HTML-Abschnitt auf einer anderen Seite anders aufgebaut ist. Die öffnende geschweifte Klammer ist aber mit hoher wahrscheinlichkeit immer nur einmal an genau dieser Stelle vorhanden.
zu 2.
Wenn es geht, identifiziere einen gesuchten HTML-Abschnitt immer über die Struktur der Seite und möglichst nicht über Inhalte, die auch bei der Darstellung angezeigt werden. Der Inhalt ändert sich z.B. auf jeden Fall, wenn Du eine Seite in einer anderen Sprachversion aufrufst. Die Struktur bleibt dabei jedoch gleich.
Also prüfst Du nicht auf Sorry, the page you are looking for could not be found., sondern Du prüfst, ob ein Element bzw. Schlüsselwort im Quelltext vorhanden ist, dass es nur auf dieser Seite gibt, jedoch niemals auf einer Seite, die richtig angezeigt wird. Der angegebene Text steht in einem h1-Tag, welches widerum in einem div-Tag steckt. Dieses div-Tag hat meiner Meinung nach eine einmalige Funktion auf der Seite. Es besitzt nämlich die ID sf-resetcontent. Aber auf IDs zu prüfen funktioniert nicht richtig mit VBA, wenn eine ID nicht vorhanden ist. Also ich bekomme dann jedenfalls immer eine Fehlermeldung.
Das ist in diesem Fall aber nicht so schlimm, denn das div-Tag hat auch eine CSS-Klasse. Nämlich sf-reset. Alles was Du machen musst, um zu Prüfen ob Du auf der Fehlerseite gelandet bist, ist also zu prüfen, ob es ein HTML-Element mit dieser CSS-Klasse gibt. Dazu versuchst Du das entsprechende Objekt anhand des Klassennamens zu bilden. Wenn es vorhanden ist, bist Du auf der Fehlerseite:

Set knoten = browser.document.getElementsByClassName("sf-reset")(0)
If Not knoten Is Nothing then
'hier was machen, wenn das Element nicht gefunden wurde (richtige Seite)
Else
'Du bist auf der Fehlerseite
End If

Ist jetzt doch etwas länger geworden. Ich hoffe dafür verständlich.
Viele Grüße,
Zwenn
Anzeige
AW: VBA web scraping nochmal
08.05.2019 10:33:30
Torsten
Hallo Zwenn,
vielen Dank für Deine Hilfe, wenn ich Dich nicht hätte :)
Ich konnte mir jetzt anhand Deiner Ausführungen folgenden Code basteln.
Sub WerteAuslesen()
'Variablen deklarieren
Dim url As String
Dim browser As Object
Dim knoten As Object
Dim htmlText As String
Dim splitArray() As String
Dim ergebnis As String
'Adresse der auszulesenden Seite
url = "https://trainingslager.onlineliga.de/team/overview/squad?userId=196"
'Internet Explorer initialisieren, Sichtbarkeit festlegen,
'URL aufrufen und warten bis Seite vollständig geladen wurde
Set browser = CreateObject("internetexplorer.application")
browser.Visible = False
browser.Navigate url
Do Until browser.ReadyState = 4: DoEvents: Loop
'Knotenobjekt setzten
Set knoten = browser.document.getElementsByClassName("sf-reset")(0)
'Abfrage ob Seite mit TeamID auch SpielerIDs enthält
If Not knoten Is Nothing Then
'Wenn Klasse "sf-reset" vorhanden,
'dann keine TeamID vorhanden
MsgBox "Es gibt kein Team mit der angegebenen ID"
Else
'Wenn Klasse "sf-reset" ist nicht vorhanden,
'es scheint also eine TeamID zu existieren
'Knotenobjekt setzten
Set knoten = browser.document.getElementsByClassName("ol-player-name")(0)
'Bedingung wenn Knotenobjekt vorhanden
If Not knoten Is Nothing Then
'Text in Variable schreiben
htmlText = knoten.outerHTML
'Klammer im gefundenen Text umbenennen,
'damit die spätere Split-Funktion nur einen Delimiter hat
'Vordere Klammer umbennenen
htmlText = Replace(htmlText, "{", "@")
'Hintere Klammer umbennenen
htmlText = Replace(htmlText, "}", "@")
'Gefundenen Text splitten und in ein Array schreiben
splitArray = Split(htmlText, "@")
'Überflüssigen Text im richtigen Array durch Leerzeichen ersetzen,
'alle Leerzeichen wegschneiden,
'restlichen Text in Ergebnisvariable übergeben
ergebnis = Trim(Replace(splitArray(1), "playerId:", " "))
End If
'Ergebnis in MsgBox ausgeben
MsgBox ergebnis
End If
'Aufräumen
browser.Quit
Set browser = Nothing
Set knoten = Nothing
End Sub
Das funktioniert soweit schon ganz gut, ich muss aber noch ein bissel Testen.
Ich habe noch eine weitere Frage, evtl. könntest Du mir da nochmal helfen.
Ich möchte noch eine Schleife setzten, welche mit alle PlayerID bei vorhandener Seite ausgibt.
Also über diesem Codeteil.
 Else
'Wenn Klasse "sf-reset" ist nicht vorhanden,
'es scheint also eine TeamID zu existieren
'Knotenobjekt setzten
Set knoten = browser.document.getElementsByClassName("ol-player-name")(0)
'Bedingung wenn Knotenobjekt vorhanden
If Not knoten Is Nothing Then
'Text in Variable schreiben
htmlText = knoten.outerHTML
'Klammer im gefundenen Text umbenennen,
'damit die spätere Split-Funktion nur einen Delimiter hat
'Vordere Klammer umbennenen
htmlText = Replace(htmlText, "{", "@")
'Hintere Klammer umbennenen
htmlText = Replace(htmlText, "}", "@")
'Gefundenen Text splitten und in ein Array schreiben
splitArray = Split(htmlText, "@")
'Überflüssigen Text im richtigen Array durch Leerzeichen ersetzen,
'alle Leerzeichen wegschneiden,
'restlichen Text in Ergebnisvariable übergeben
ergebnis = Trim(Replace(splitArray(1), "playerId:", " "))
End If
'Ergebnis in MsgBox ausgeben
MsgBox ergebnis

Beim letzten mal (https://www.herber.de/forum/archiv/1684to1688/t1686352.htm) lief das über For Each obj in obj. bis next.
Leider hab ich das hier ja nun nicht so. Ich hab schon probiert das über noch eine IF oder DO UNTIL zu lösen, aber der Code funktioniert dann nicht mehr oder läuft endlos ins leere.
Kannst Du mir noch einen Tip geben?
Und kannst Du mir auch anfängerfreundliche Literatur zu dem Thema VBA web scraping empfehlen?
Ich muss momentan leider immer alles mal hier und mal da googlen oder andere um Hilfe bitten, ich würde lieber gerne anhand eines "roten Fadens" Stück für Stück lernen. Selbst wenn ich jetzt den obrigen Code hingebastelt bekommen habe, verstehe ich leider noch nicht zu 100% warum manchmal was wie und wo steht.
Anzeige
AW: VBA web scraping nochmal
09.05.2019 11:14:52
Zwenn
Hallo Torsten,
wenn Du alle PlayerIDs durchgehen willst, funktioniert das auch mit For Each. Um Alle PlayerIDs in einer NodeList einzusammeln, musst Du den direkten Zugriff auf ein bestimmtes Element der Liste weglassen. Mit (0) am Ende greifst Du direkt auf das erste Element zu und bildest nur damit das Objekt knoten. Wenn Du (0) weglässt, wird eine Liste mit allen Elementen der Klasse ol-player-name gebildet, die Du dann mit For Each durchgehen kannst.
Die nachgestellte (0) ist schon der Index auf das erste Element. Wenn Du die Zeile mit so einem Index schreibst, wird genau dieses Element zurückgeliefert.
Für Dein spezifisches Beispiel heißt das:

Set knoten = browser.document.getElementsByClassName("ol-player-name")(0)
Ergibt Spieler 1:
<span onclick="olAnchorNavigation.load('player/overview', { playerId: 3701 });" class="ol- _
player-name">Spieler 1</span>

Set knoten = browser.document.getElementsByClassName("ol-player-name")(1)
Ergibt Spieler 2:
<span onclick="olAnchorNavigation.load('player/overview', { playerId: 3702 });" class="ol- _
player-name">Spieler 2</span>

Set knoten = browser.document.getElementsByClassName("ol-player-name")(4)
Ergibt Spieler 5:
<span onclick="olAnchorNavigation.load('player/overview', { playerId: 27219 });" class="ol- _
player-name">Spieler 5</span>


Das Prinzip sollte klar sein. Wenn Du nun die ganze Liste brauchst, dann geht das so:

Set knoten = browser.document.getElementsByClassName("ol-player-name")
Ergibt Liste von Spieler 1 bis Spieler 28 mit den Indizes 0 bis 27:
<span onclick="olAnchorNavigation.load('player/overview', { playerId: 3701 });" class="ol- _
player-name">Spieler 1</span>
<span onclick="olAnchorNavigation.load('player/overview', { playerId: 3702 });" class="ol- _
player-name">Spieler 2</span>
<span onclick="olAnchorNavigation.load('player/overview', { playerId: 52869 });" class="ol- _
player-name">Spieler 28l</span>


Im letzten Posting habe ich Dir erklärt, wie Du eine Seite eindeutig identifizieren kannst und Du hast das auch gut umgesetzt. Du brauchst diese Identifizierung aber nur, wenn Du mit der identifizierten Seite etwas anstellen willst. Das willst Du in Deinem Fall nicht. Also kannst Du Dein Makro vereinfachen, indem Du direkt auf die CSS-Klasse ol-player-name abfragst. Wenn sie vorhanden ist, wird das Objekt knoten gebildet, wenn nicht, dann nicht.
Naja, jedenfalls dachte ich das bisher. Ich habe das Makro unten so geschrieben, wie ich es grade erklärt habe. Es wird aber komischerweise auch ein Objekt knoten gebildet, wenn kein Tag diese CSS-Klasse hat. Ich weiß nicht warum, aber man bekommt in diesem Fall eine leere NodeList zurückgeliefert. Die Länge einer NodeList kann man jedoch abfragen, was wir hier für die Prüfung ausnutzen.
Wir nehmen in diesem Fall statt:
If Not knoten Is Nothing then

die Zeile
If knoten.Length > 0 then


Deine Stringbearbeitung habe ich so gelassen, sie funktioniert ja super. Ich habe lediglich die Zusammensetzung der Variable ergebnis so angepasst, dass alle PlayerIDs untereinander ausgegeben werden:

Sub WerteAuslesen()
'Variablen deklarieren
Dim url As String
Dim browser As Object
Dim knoten As Object
Dim knotenPlayer As Object
Dim htmlText As String
Dim splitArray() As String
Dim ergebnis As String
'Adresse der auszulesenden Seite
url = "https://trainingslager.onlineliga.de/team/overview/squad?userId=196"
'Internet Explorer initialisieren, Sichtbarkeit festlegen,
'URL aufrufen und warten bis Seite vollständig geladen wurde
Set browser = CreateObject("internetexplorer.application")
browser.Visible = False
browser.Navigate url
Do Until browser.ReadyState = 4: DoEvents: Loop
'Knotenobjekt setzten
Set knoten = browser.document.getElementsByClassName("ol-player-name")
'Abfrage ob Seite mit TeamID auch SpielerIDs enthält
If knoten.Length > 0 Then
'Wenn Klasse "ol-player-name" vorhanden ist,
'existiert die abgefragte TeamID
'Alle PlayerIDs zur TeamID auslesen
For Each knotenPlayer In knoten
'Text in Variable schreiben
htmlText = knotenPlayer.outerHTML
'Klammer im gefundenen Text umbenennen,
'damit die spätere Split-Funktion nur einen Delimiter hat
'Vordere Klammer umbennenen
htmlText = Replace(htmlText, "{", "@")
'Hintere Klammer umbennenen
htmlText = Replace(htmlText, "}", "@")
'Gefundenen Text splitten und in ein Array schreiben
splitArray = Split(htmlText, "@")
'Überflüssigen Text im richtigen Array durch Leerzeichen ersetzen,
'alle Leerzeichen wegschneiden,
'restlichen Text in Ergebnisvariable übergeben
ergebnis = ergebnis & Trim(Replace(splitArray(1), "playerId:", " ")) & Chr(13)
Next knotenPlayer
Else
'Wenn Klasse "ol-player-name" nicht vorhanden ist,
'ist die abgefragte TeamID nicht vorhanden
ergebnis = "Es gibt kein Team mit der angegebenen ID"
End If
'Ergebnis in MsgBox ausgeben
MsgBox ergebnis
'Aufräumen
ergebnis = ""
browser.Quit
Set browser = Nothing
Set knoten = Nothing
Set knotenPlayer = Nothing
End Sub


Viele Grüße,
Zwenn
Anzeige
AW: VBA web scraping nochmal
09.05.2019 14:09:49
Torsten
Hallo Zwenn,
Ich bedanke mich abermals für die Hilfe.
Dein Code macht genau das, was ich möchte und ich verstehe jetzt auch wieder ein Stück mehr.
Leider gibt es aber eine Fehlermeldung wenn man den Code nochmals kurz nach dem Durchlauf ausführt.
Laufzeitfehler'-2147023706 (800704a6)
Automatisierungsfehler
Das Herunterfahren des Systems wurde bereits geplant.

Bein Debuggen bleibt der Code in der Zeile kleben.
Set browser = CreateObject("internetexplorer.application")
Wartet man ein paar Sekunden, dann läuft alles wieder.
Ich wollte die Ausleseprozedur später in eine Schleife packen um eine ganze Reihe von Seiten auszulesen, das klappt dann ja nicht wenn der Laufzeitfehler auftritt.
Kann man da irgendwas ändern um den Fehler zu beheben?
Anzeige
AW: VBA web scraping nochmal
09.05.2019 15:08:52
Torsten
Hallo,
ich nochmal.
Ich habe mal wieder etwas gegoogled und konnte mir mit dem Laufzeitfehler selber helfen.
Nur zur Info falls jemand das gleiche Problem hat:
https://stackoverflow.com/questions/27626253/excel-vba-force-shutdown-ie/feed/
Man muss den Einstellungen des IE das automatische Löschen des Browserverlaufs abschalten, anscheinend benötigt der Browser sonst sehr lange um sich wieder frisch zu machen.
AW: VBA web scraping nochmal
09.05.2019 16:13:10
Zwenn
Hallo Torsten,
gut dass es bisher nun so läuft, wie Du es möchtest :-) Dass der Automatisierungsfehler am IE liegt habe ich gewußt. Was ich nicht gewußt habe ist allerdings, dass es mit dem automatischen Leeren des IE-Caches zu hat. So habe ich auch wieder etwas gelernt.
Ich dachte mir, dass Du mehrere TeamIDs in einer Schleife auslesen möchtest. Deshalb habe ich unter Aufräumen bereits ergebnis = "" aufgenommen. Lege die Schleife unbedingt von vor der URL (für die Du dann die TeamID bei jedem Schleifendurchlauf aus einer Tabelle holst) bis hinter die Aufräumarbeiten. Hintergrund ist auch hier wieder der IE. Der zickt nämlich auch sehr gerne rum, wenn man ihn für viele Seitenaufrufe geöffnet lässt. Das kann man direkt umgehen, wenn man ihn vor jedem neuen Schleifendurchlauf einfach schließt.
Du fragtest noch nach Literatur für Web Scraping. Da kann ich Dir leider gar nix empfehlen. Wie Du jetzt, habe ich anfangs auch für jedes kleine Problem gegoogelt. Nach einer Zeit wird dann klar, dass man vor allem in der Dokumentation des DOM selbst viel findet. Außerdem in Foren wie diesem und auch viel in StackOverflow.
Insgesamt ist es hilfreich, wenn man ganz abseits des DOM programmieren kann. Man sollte sich mindestens mit den Kontrollstrukturen auskennen. Also Schleifen (For Next, For Each, Do Loop) und Entscheidungen (If Then Else, Select Case). Außerdem mit Variablen, Konstanten, dazu den Datentypen und dann ist es auch sehr hilfreich sich mit Prozeduren (Sub) und Funktionen (Function) vertraut zu machen. Dazu gehören dann Sachen wie Übergabeparameter, Rückgabewerte, wie die Teile aufgerufen werden, usw.
Je mehr Praxis man bekommt, desto mehr durchdringt man auch, wie das Handwerk funktioniert und wie man die einzelnen Elemente sinnvoll miteinander einsetzen kann. Das geht dann in Richtung gut lesbaren und gut wartbaren Code. Dafür hilft es auch sehr, wenn man sich mit dem Code anderer (guter) Entwicklung auseinander setzt. Als regelmäßiger Forennutzer findet man schnell raus, wessen Beiträge echte Nuggets enthalten.
Als Ausgangspunkt für den Umgang mit dem DOM empfehle ich Dir die folgende Seite:
http://developer.mozilla.org/en-US/docs/Web/API/Element/
Dort findest Du alles, was Du bisher verwendet hast entweder unter Properties (z.B. outerHTML) oder Methods (z.B. die get-Methoden). Allein auf der Seite findest Du noch einiges mehr. Wenn Du in der URL Ebene mal einen Schritt weiter nach vorne gehst, dann ist die grade verlinkte Seite ein einziger Link, nämlich Element im Abschnitt Interfaces auf dieser Seite, die dann noch so einige andere Infos bereit hält:
http://developer.mozilla.org/en-US/docs/Web/API/
Aber keine Panik, für Web Scraping kommt man mit der Seite Element schon sehr weit. Hier ist noch ein Link auf die Startseite der Dokumentation auf Deutsch. Ich weiß aber nicht, wie umfangreich die ist, da ich die noch nie benutzt habe:
http://developer.mozilla.org/de/docs/DOM
Die Microsoft DOM Dokumentation findest Du hier (direkt für Element). Allerdings erscheint sie seit einiger Zeit unvollständig. Unter Methoden fehlen z.B. getElementsByClassName() und getElementByID(). Keine Ahnung wo sie die versteckt haben. MS hat die Doku allerdings auch mal umgebogen auf diesen NET Kram. Früher gabs die direkt zum IE. Aber die Links dazu haben sie irgendwann auf die Mozilla Seiten umgeleitet. So bin ich da gelandet.
http://docs.microsoft.com/de-de/dotnet/api/system.windows.forms.htmlelement?view=netframework-4.8
Was Dir meistens begegnen wird ist der Bezug auf JavaScript. Das ist auch sehr naheliegend, denn das ist nunmal die Sprache für Internetseiten. VBA ist im Bezug aufs DOM ja eher ein Exot. Aber die Vorgehensweise ist gleich. Man muss es nur an die Sprache anpassen.
Viele Grüße,
Zwenn
Anzeige

39 Forumthreads zu ähnlichen Themen

Anzeige
Anzeige

Links zu Excel-Dialogen

Beliebteste Forumthreads (12 Monate)

Anzeige

Beliebteste Forumthreads (12 Monate)

Anzeige
Anzeige
Anzeige