AW: mit PQ im Web logIn + Datenimport
17.02.2022 13:57:42
Zwenn
Hallo Fred,
gestern habe ich mich lange mit dem Problem beschäftigt und habe es nicht geschafft, den LogIn aufrecht zu erhalten sozusagen. Dazu gleich mehr.
Ob ein LogIn mit Power Query möglich ist kann ich nicht sagen, weil ich mich mit PQ nicht auskenne. Am ehesten kann Dir dazu wahrscheinlich Günther etwas sagen, der aber nicht in diesem Excel Forum aktiv ist, soweit ich weiß. Er betreibt aber eine Seite. Rechts im Menü kannst Du sehen, dass er sich viel mit PQ beschäftigt:
http://www.excel-ist-sexy.de/
Dein Ansatz, es über die Shell zu machen funktioniert so nicht. Das habe ich auch gar nicht weiter verfolgt, außer einmal Dein Makro aufzurufen. Ich kenne niemanden, der es geschafft hat den Speichern Dialog eines Browsers aus einem VBA Makro heraus zu steuern, außer den des Internet Explorers. Den sollte man aber inzwischen endgültig nicht mehr einsetzen, weil er aktiv ausgemustert wird und mit Windows 11 auch gar nicht mehr ausgeliefert wird.
Mein Ansatz war ein XML-HTTP-Request oder kurz xhr. Das ging erstmal überhaupt nicht, denn ich habe die Übergabe von Username und Passwort im ersten Versuch so aufbereitet, wie Michael hier:
https://www.herber.de/forum/messages/1870869.html
Im weiteren Verlauf bin ich dann auf eine andere Methode gekommen, für die zusätztlich eine Codierung nach Base64 notwendig ist. Das hat aber auch alles nicht funktioniert. Deshalb bin ich sehr neugierig geworden und habe Stunden damit verbracht zunächst rauszufinden, was die Webseite eigentlich wie macht und das dann aus einem Makro quasi zu simulieren.
Die Anmeldung selbst funktioniert gar nicht über einen HTTP-Get-Request, sondern ausschließlich über einen HTTP-Post-Request. Es reicht auch nicht, den Usernamen und das Passwort zu übergeben, sondern zusätzlich muss ein bei jedem Seitenaufruf zufällig generierter CSRF Token mit in die Anmeldung übernommen werden. CSRF steht für Cross-Site-Request-Forgery:
https://de.wikipedia.org/wiki/Cross-Site-Request-Forgery
Ich habe den Token im HTML Code der LogInSeite an zwei Stellen gefunden. In einem meta-Tag und in einem hidden-input-Tag. Man kann den dann einfach auslesen und in den sogenannten Payload für den Post-Request integrieren. Die Anmeldung klappt so auch, aber sie verfällt sofort. Eine anschließend aufgerufene Seite liefert nämlich den HTTP-Status 419 zurück, mit dem Hinweis Page Expired.
Das habe ich mir natürlich angesehen und bin zusätzlich auf einen Seassion Key im Response Header der Seite gestoßen. Auch den kann man auslesen und im anschließenden zweiten Request mit übermitteln. Es klappt aber trotzdem nicht. Es gibt auch noch einen zweiten Token im Response Header, der mich aber auch nicht weitergebracht hat. (Beide Tokens stehen auch genauso im Cookie und können bei einer weiteren Anfrage als Cookie im Request Header übergeben werden.)
Ich nehme an, das hat zwei Gründe:
1. Der Zeitstempel für den Verfall beider Tokens ist identisch mit dem aktuellen Zeitpunkt aus Datum und Uhrzeit. Schaut man weiter, stellt man fest, dass auch diese Tokens bei jedem Seitenaufruf neu generiert werden.
2. Es gibt normalerweise die Möglichkeit einer geöffneten HTTP-Verbindung den Parameter Keep Alive mitzugeben. HTTP ist vom Design her ein verbindunsloses Protokoll. Das bedeutet, der Server weiß bei einer zweiten Anfrage nicht mehr, dass man 2 Sekunden vorher schonmal zu Besuch war. Deshalb gibt es überhaupt sowas wie Seasson Keys. Soweit ich es recherchiert habe, funktioniert Keep Alive nicht mit xhr aus VBA heraus.
Das bedeutet: Die Anmeldung besteht wahrscheinlich nur für den Bruchteil einer Sekunde.
Um Dein Problem zu lösen ist es vermutlich am ratsamsten, Selenuium Basic zu verwenden. Damit kann man den Chrome Browser aus VBA heraus steuern. Im Browser selbst wird das gesamte Token und Cookie Handling dann automatisch im Hintergrund übernommen. Zu Selenium Basic habe ich hier mal etwas geschrieben:
https://www.herber.de/forum/cgi-bin/callthread.pl?index=1831729#1831851
Auch wenn das jetzt nicht das ist, was Du lesen wolltest, hast Du doch wenigstens ein paar Erklärungen, warum die "herkömmlichen" Methoden bisher alle gescheitert sind.
Hier ist noch mein Versuchs-Code, falls da jemand reinschauen möchte:
Sub InPlayGuru()
Const url As String = "https://inplayguru.com/login"
Dim doc As Object
Dim respHeader() As String
Dim nodeHiddenToken As Object
Dim payload As String
Set doc = CreateObject("htmlFile")
With CreateObject("MSXML2.XMLHTTP.6.0")
.Open "GET", url, False
.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0"
.setRequestHeader "Connection", "keep-alive"
.setRequestHeader "cache-control", "no-cache"
.send
respHeader = Split(.GetAllResponseHeaders, Chr(13))
respHeader(4) = Split(Mid(respHeader(4), 14), ";")(0)
respHeader(5) = Split(Mid(respHeader(5), 14), ";")(0)
Debug.Print .GetAllResponseHeaders
Debug.Print respHeader(4)
Debug.Print respHeader(5)
If .Status = 200 Then
doc.body.innerHTML = .responseText
Set nodeHiddenToken = doc.getElementsByTagName("input")
If nodeHiddenToken.Length > 0 Then
payload = "_token=" & nodeHiddenToken(0).Value & "&email=woppelx%40gmail.com&password=Hannover1896"
End If
If payload "" Then
.Open "POST", url, False
.setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0"
.setRequestHeader "Connection", "keep-alive"
.setRequestHeader "cache-control", "no-cache"
.setRequestHeader "Cookie", respHeader(4)
.setRequestHeader "Cookie", respHeader(5)
.send payload
' .Open "GET", "https://inplayguru.com/strategies/167305/history/export/xlsx/", False
' .send
Close
Open "D:\InPlayGuru_Strategy_167305_Picks.html" For Output As #1
Print #1, .responseText
Close
Else
MsgBox "Der notwendige Token konnte nicht extrahiert werden."
End If
Else
MsgBox "Ladefehler" & Chr(13) & "HTTP-Status: " & Chr(13) & .Status & " " & .statustext
End If
End With
End Sub
Viele Grüße,
Zwenn