Problem mit Net.Webclient

  • VB.NET

Es gibt 25 Antworten in diesem Thema. Der letzte Beitrag () ist von Asana.

    Problem mit Net.Webclient

    Huhu, bin hier gerade ein wenig am Verzweifeln.
    Und zwar möchte ich den Sourcecode einer Seite verwerten, die ich per Webclient als String herunterlade.
    Im Prinzip funktioniert das ganze auch, jedoch werden glaube ich dadurch nicht alle Scripts oder Plugins auf der entsprechenden Seite ausgeführt und erscheinen deshalb auch nicht im heruntergeladenen Quellcode, wenn ich die Webseite im Browser aufrufe und mir den Quellcode anzeigen lasse, sind die entsprechenden Codezeilen vorhanden.
    Den heruntergeladenen String lass ich mir erstmal in die TextBox ausgeben, das sollte auch nicht weiter das Problem sein.

    Im Prinzip läuft das ganze so:

    VB.NET-Quellcode

    1. Dim wc As New Net.WebClient
    2. Dim WebAddress as String = "https://www.tradeskillmaster.com/black-market?realm=EU-"
    3. Dim Servername as String = "Aegwynn"
    4. TextBox1.Text = (wc.DownloadString(WebAddress & Servername))



    Angezeigt wird mir dann:
    Spoiler anzeigen

    HTML-Quellcode

    1. <div class="col-md-7">
    2. <form class="navbar-form" action="user/realm-select">
    3. <div class="form-group">
    4. <div class="kv-plugin-loading loading-realmSelect">&nbsp;</div><div class="kv-hide group-realmSelect input-group input-group-md select2-bootstrap-prepend"><span class="input-group-addon">Realm</span><select id="realmSelect" class="form-control kv-hide " name="globalRealmSelect" style="width: 300px;" data-krajee-select2="select2_81a1c529">
    5. <option value="">-- Select a Realm --</option>
    6. <option value="1">Aegwynn (US)</option>
    7. <option value="6">Aerie Peak (US)</option>
    8. <option value="7">Agamaggan (US)</option>
    9. <option value="12">Aggramar (US)</option>
    10. <option value="14">Akama (US)</option>
    11. <option value="17">Alexstrasza (US)</option>
    12. <option value="19">Alleria (US)</option>
    13. <option value="21">Altar of Storms (US)</option>
    14. <option value="25">Alterac Mountains (US)</option>
    15. <option value="30">Aman'Thul (US)</option>
    16. <option value="31">Andorhal (US)</option>
    17. <option value="23">Anetheron (US)</option>
    18. <option value="35">Antonidas (US)</option>
    19. <option value="37">Anub'arak (US)</option>
    20. <!-- Verschiedene Auswahlmöglichkeiten die ich jetzt mal rausgekürzt habe -->
    21. </select></div> </div>
    22. </form>
    23. </div>


    Jedoch, im Browser bekomme ich folgenden Quellcode:

    Spoiler anzeigen

    HTML-Quellcode

    1. <div class="col-md-7">
    2. <form class="navbar-form" action="user/realm-select">
    3. <div class="form-group">
    4. <div class="kv-plugin-loading loading-realmSelect">&nbsp;</div><div class="kv-hide group-realmSelect input-group input-group-md select2-bootstrap-prepend"><span class="input-group-addon">Realm</span><select id="realmSelect" class="form-control kv-hide " name="globalRealmSelect" style="width: 300px;" data-krajee-select2="select2_81a1c529">
    5. <option value="">-- Select a Realm --</option>
    6. <option value="247">Aegwynn (EU)</option>
    7. <option value="248">Aerie Peak (EU)</option>
    8. <option value="250">Agamaggan (EU)</option>
    9. <option value="256">Aggra (Português) (EU)</option>
    10. <option value="258">Aggramar (EU)</option>
    11. <option value="260">Ahn'Qiraj (EU)</option>
    12. <option value="270">Al'Akir (EU)</option>
    13. <option value="273">Alexstrasza (EU)</option>
    14. <option value="275">Alleria (EU)</option>
    15. <option value="277">Alonsus (EU)</option>
    16. <option value="280">Aman'Thul (EU)</option>
    17. <option value="281">Ambossar (EU)</option>
    18. <option value="279">Anachronos (EU)</option>
    19. <option value="283">Anetheron (EU)</option>
    20. <!-- Mal wieder eine riesige Auswahl, gekürzt zur besseren Lesbarkeit -->
    21. </select></div> <span style="font-size:10px; padding-left:5px;">Last Updated 6 minutes ago</span> </div>
    22. </form>
    23. </div>
    24. </html>


    Hierbei wird schonmal deutlich, dass die Server nicht stimmen, da ich gerne EU-Server möchte und nicht die US-Server, welches aber eher nebensächlich für mich ist.

    Wichtig ist das

    HTML-Quellcode

    1. <span style="font-size:10px; padding-left:5px;">Last Updated 6 minutes ago</span>


    an welches ich gerne rankommen würde.

    Ih hoffe ihr habt ne Lösung oder Idee für mich parat :(
    Mit welchem Browser hast du es getestet?

    //Edit

    Nvm. Habe mir die Seite mal angeschaut. Es scheint als würde die Server und Region Auswahl nicht über GET Parameter geregelt werden.
    Also wird es wohl ein POST sein. Dann erhältst du vermutlich Cookies und die musst du mitsenden mitm GET.
    Also zB:

    POST tradeskillmaster.com/user/realm-select
    Als repsone erhältst ein realm cookie und das musst du bei GET tradeskillmaster.com/black-market mitsenden

    Am besten mal mit nem HTTP Viewer genau anschauen wie das abläuft.

    LG
    Das ist meine Signatur und sie wird wunderbar sein!

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Mono“ ()

    Dürfte keine Rolle spielen, Internet Explorer sowie Google Chrome.
    Wichtig ist, dass das "Last Updated x ago" erst erscheint, wenn man oben die Server per Hand auswählt, folgt man nur den Link wird das nicht angezeigt, ich glaube das ist der Knackpunkt.

    Asana schrieb:

    VB.NET-Quellcode

    1. WebAddress & Servername
    Wieso setzt Du diesen String zusammen?
    Arbeite mal mit genau einem String, den Du per C&P sowohl im Browser als auch in Deinem Programm verwendest.
    Bedenke, dass im Browser der Befehl .Navigate() ausgeführt wird, bei .DownloadString() ggf. nicht.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Eigentlich wähle ich vorher in einer Combobox den Servernamen aus, deswegen setzte ich den String zusammen.
    Öffne ich in bspw. Chrome die Seite inkl. Link zum Server, ist oben die Toolbar nicht ausgefüllt, obwohl eigentlich EU, Aegwynn stehen müsste. Wähle ich das per Hand aus aktualisiert kurz die Seite und zeigt mir "Last Updated" an. Wenn ich jetzt im selben Fenster einen Link zum anderen Server eingebe, pflegt er sowohl die Toolbar oben mit und zeigt mir auch "Last Updated" an, aber erst nachdem ich in der aktuellen Sitzung einmal die Toolbar per Hand gefüllt habe.
    Ich habe im Post drüber einen Edit eingefügt der die grundlegende Erklärung und Heransgehensweise beinhaltet. Wenn du dabei Probleme hast dann stell einfach weitere Fragen.

    LG
    Das ist meine Signatur und sie wird wunderbar sein!

    Mono schrieb:

    Ich habe im Post drüber einen Edit eingefügt der die grundlegende Erklärung und Heransgehensweise beinhaltet. Wenn du dabei Probleme hast dann stell einfach weitere Fragen.

    LG

    Erstmal eine großes Dankeschön an dich!
    Leider kann ich mit deiner erläuterten Herangehensweise nicht viel anfangen, da ich mich in diesem Kontext überhaupt nicht auskenne.
    Ich denke ich werde mir das Cookie in ein Cookiecontainer speichern müssen und das Cookie bei jeder Anfrage mitsenden, oder?

    Ich wäre sehr verbunden wenn du mir hierbei noch auf die Sprünge helfen könntest.

    Danke und Liebe Grüße!
    Also ich habe leider nicht so viel Zeit mir dein Problem ganz genau anzuschauen.

    Grundsätzlich würde ich wie folgt vorgehen:

    - Schau dir die Seite an mit einem Programm oder Browser Addon ala HTTP Watch. Sie dir alle Requests an (welche Posts mit welchen Daten. Auf welche Seite, welche Cookies usw.)
    - Bau das Ganze mit dem WebClient nach (die POST und GET Requests)
    - Schau dir den Cookiecontainer an mit Hilfe von Goolge und Stackoverflow -> stackoverflow.com/questions/17…iner-with-webclient-class
    - Bau die Cookies korrekt in die Requests ein und dann erhältst auch das korrekte Ergebnis.


    LG
    Das ist meine Signatur und sie wird wunderbar sein!
    Hm, ist das hier der richtige Ansatz? Bekomme immer Fehlermeldung 400 "Ungültige Anforderung"


    VB.NET-Quellcode

    1. Dim Request As Net.HttpWebRequest =
    2. DirectCast(
    3. Net.HttpWebRequest.Create("https://www.tradeskillmaster.com/user/realm-select"),
    4. Net.HttpWebRequest
    5. )
    6. Dim Cookies As New Net.CookieContainer()
    7. With Request
    8. .CookieContainer = Cookies
    9. .Method = "POST"
    10. .ContentType = "application/x-www-form-urlencoded"
    11. End With
    12. Dim Writer As New IO.StreamWriter(Request.GetRequestStream())
    13. Writer.Write("regionID=<1de8a1f36d12d6d93b180176e811127d35fb174f75e311c5025f1fb101c6194aa%3A2%3A%7Bi%3A0%3Bs%3A8%3A%22regionId%22%3Bi%3A1%3Bs%3A2%3A%22EU%22%3B%7D>")
    14. Writer.Close()
    15. Request.GetResponse().Close()
    16. Writer.Write("realmID=<cd03175de6d9f8d0f022e2468b46bd3fc9ce8b0527f4c7c9f576b3e25c707fc0a%3A2%3A%7Bi%3A0%3Bs%3A7%3A%22realmId%22%3Bi%3A1%3Bi%3A247%3B%7D")
    17. Writer.Close()
    18. Request.GetResponse().Close()
    19. Request =
    20. DirectCast(
    21. Net.WebRequest.Create("https://www.tradeskillmaster.com/black-market"),
    22. Net.HttpWebRequest
    23. )
    24. Request.CookieContainer = Cookies
    25. Dim Reader As New IO.StreamReader(Request.GetResponse().GetResponseStream())
    26. MsgBox(Reader.ReadToEnd())
    27. Reader.Close()
    Also ich habe es mir heute mal angeschaut.
    Es ist wirklich etwas komplizierter, aber ich habe es hinbekommen.
    Allerdings bin ich kein Fan davon, dir hier ein komplett fertiges Codebeispiel zu posten. Darum gebe ich dir die Basis. Die letzliche exakte Implementierung überlasse ich dir (betrifft vor allem das parsen der Responses)


    Ich erläutere auch kurz die Herangehensweise die ich dir eh schon beschrieben habe.

    Webseite mit Google Chrome öffnen und Str+Shift+I drücken. Dort auf Network gehen. Alle Cookies im Browser löschen und die Seite laden. Hier sieht man einen GET Request und das man 2 cookies benötigt. Dann den Realm ändern. Da sieht man im Network kurz einen POST auf tradeskillmaster.com/user/realm-select. Schnell stop drücken und den post anschauen (oder preserve log aktivieren ;))
    Dort drauf klicken und man sieht:
    X-CSRF-Token:Ll9sZ0g2X1B5NAIJEHIqBUEIOB5xQGYmYjkCIC1GMD1iBxoJLHMGMw==
    im Request header.
    Außerdem gibt es Formdata:
    realmId=1

    Im Responseheader sieht man ein neuen Cookie namens realmId= und ieien lange GUID. Ok. Danach folgt ein erneuter GET Request auf die erste Seite tradeskillmaster.com/black-market mit dem Cookie realmId.

    Das ist der Ablauf. Diesen muss man nun nachbauen.

    Cookies (PHPSessionID und _csrf) erhalten. Ohne die geht gar nichts.
    Ich verwende gleich HTTPRequest und nicht den HTTPClient oder WebClient
    Der HTTPClient ist ab 4.5 mit Asynchronen Methode eigentlich das Mittel der Wahl. Habe hier aber nur 4.0.

    VB.NET-Quellcode

    1. Private cc As CookieContainer
    2. Private baseUrl As New Uri("https://www.tradeskillmaster.com/black-market")
    3. Private realmUrl As New Uri("https://www.tradeskillmaster.com/user/realm-select")
    4. Private Sub button1_Click(sender As Object, e As EventArgs)
    5. Dim request As HttpWebRequest = DirectCast(WebRequest.Create(baseUrl), HttpWebRequest) 'webrequest mit get auf https://www.tradeskillmaster.com/black-marke
    6. cc = New CookieContainer() 'neuer cookiecontainer mit anhängen
    7. request.CookieContainer = cc 'enthalt nach dem request die cookies
    8. Dim res As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
    9. Dim streamResponse As Stream = res.GetResponseStream()
    10. Using sr As New StreamReader(streamResponse)
    11. richTextBox1.Text = sr.ReadToEnd() 'zur ausgabe der seite und weiter verarbeiten, das ist eine provisorische lösung. Du solltest das dann natürlich nicht mehr so machen
    12. End Using
    13. End Sub


    So bleiben die Fragen nach den Daten die wir brauchen.
    X-CSRF-Token
    realmId=

    Das X-CSRF-Token scheint ein Schutzmechanismus gege CSRF zu sein, in jedem Fall erhält man bei jedem Request im Meta Header der HTML Seite ein neuen Token. Diesen muss man im Post auf die Realm-Select mitsenden, sonst kommt keine Antwort. Das Token habe ich aktuell einfach händisch kopiert. Es befindet sich in der Richtextbox:

    HTML-Quellcode

    1. <!DOCTYPE html>
    2. <html lang="en-US">
    3. <head>
    4. <meta charset="UTF-8"/>
    5. <meta name="viewport" content="width=1120" />
    6. <meta name="csrf-param" content="_csrf">
    7. <meta name="csrf-token" content="bXhXUVJLNGxUGxQUFjhdXik7NQkoJGQhMhpifBYecUE/DiEOFw9.IQ==">

    Und ist wie gesagt immer anders.
    Ich habe mir eine Textbox hinzugefügt in die ich das händisch reinkopiere, da ich keine Lust hatte es zu parsen. Das ist dann deine Aufgabe ;)
    (Achtung, die 2 == gehören auch dazu).
    So un dann fehlt noch die ID. In deiner HTML Ansicht sieht man ja auch die Options:

    HTML-Quellcode

    1. <option value="">-- Select a Realm --</option>
    2. <option value="1" selected>Aegwynn (US)</option>
    3. <option value="6">Aerie Peak (US)</option>
    4. <option value="7">Agamaggan (US)</option>
    5. <option value="12">Aggramar (US)</option>
    6. <option value="14">Akama (US)</option>


    Auch die parse ich nicht sondern trage sie in eine 2. Textbox rein(für Aegwynn also 1). Danach klicke ich den 2. Button für den Post und den finalen GET:

    VB.NET-Quellcode

    1. Private Sub button2_Click(sender As Object, e As EventArgs)
    2. Dim request As HttpWebRequest = DirectCast(WebRequest.Create(realmUrl), HttpWebRequest) 'Request auf die REALMURL!!!
    3. request.Method = "POST" 'Diesmal kein GET sondern POST (GET ist default)
    4. request.Accept = "*/*" 'alle Header einfach aus chrome übernommen, vermutlich sind nicht alle erforderlich
    5. request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8"
    6. request.CookieContainer = cc 'Cookiecontainer mit PHPSessionID und dem anderen Cookie mitgeben und dort befindet sich danach das realmid cookie
    7. request.Headers.Add("X-CSRF-Token: " + textBox1.Text) 'zwingend erforderlich, das kopierte Token aus dem ursprünglichen Request
    8. request.Headers.Add("X-Requested-With: XMLHttpRequest") ' steht auch mit im request header. kenn den nicht wirklich, vermutlich auch unnötig. kannst ja ein bissl experimentieren welche header wirklich erforderlich sind
    9. Dim postData As [String] = "realmId=" + textBox2.Text 'postdata für den Postrequest, damit der Webserver weiss, welche Realm-id benötigt wird.
    10. Dim data As [Byte]() = Encoding.ASCII.GetBytes(postData) 'als byte array
    11. request.ContentLength = data.Length 'mit seiner länge
    12. Using s As Stream = request.GetRequestStream() 'in den requeststream
    13. s.Write(data, 0, data.Length) 'reinschreiben
    14. End Using
    15. richTextBox1.Text = ""
    16. Dim res As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse) 'request senden und response holen
    17. request = DirectCast(WebRequest.Create(baseUrl), HttpWebRequest) 'jetzt haben wir alles für den finalen request
    18. request.CookieContainer = cc 'cookie container enthält nun alle 3 cookies
    19. res = DirectCast(request.GetResponse(), HttpWebResponse)
    20. Dim streamResponse As Stream = res.GetResponseStream()
    21. Using sr As New StreamReader(streamResponse)
    22. richTextBox1.Text = sr.ReadToEnd() 'response in rtb ausgeben. Dort findest du dann auch
    23. End Using
    24. End Sub


    HTML-Quellcode

    1. </select></div> <span style="font-size:10px; padding-left:5px;">Last Updated 2 hours ago</span> </div>


    Ich hoffe es ist nachvollziehbar. Es ist keine Anfängeraufgabe. Aber ich denke, mit den Informationen solltest du es allein schaffen. Bin auch kein regex experte für das X-CSRF-Token. Aber das schaffst du schon.
    Btw ist das mit der Region genau das Gleiche, nur ein etwas anderer POST (das solltest du mit den mitteln nun auch schaffen)
    LG
    Das ist meine Signatur und sie wird wunderbar sein!

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Mono“ ()

    Erstmal ein ganz ganz großes Dankschön an dich Mono!
    Genau sowas wollte ich und den Rest bekomme ich selber auch noch hin, vielen Dank!
    Leider dauert die letzte Abfrage ca. eine Minute, was bei 267~ Servern dann leider den Zweck verfehlt oder dauert das nur bei mir so lange?

    VB.NET-Quellcode

    1. res = DirectCast(request.GetResponse(), HttpWebResponse)
    2. Dim streamResponse As Stream = res.GetResponseStream()
    3. Using sr As New StreamReader(streamResponse)
    4. RichTextBox1.Text = sr.ReadToEnd()
    5. End Using


    Lässt sich das noch irgendwie beschleunigen?
    Das mit dem Parsen sollte kein Problem darstellen.

    Liebe Grüße

    markus.obi schrieb:

    Asana schrieb:

    Lässt sich das noch irgendwie beschleunigen?


    Mit

    VB.NET-Quellcode

    1. webclient.Proxy = Nothing
    sollte es schneller gehen.


    Es wird kein Webclient verwendet...
    Das ist meine Signatur und sie wird wunderbar sein!
    Bei mir Zuhause ging es dann auch deutlich schneller.
    Das Parsen funktioniert ohne Probleme, per ComboBox kann ich mir die verschiedenen Server auswählen, klappt alles.
    Soweit so gut, nun möchte ich zwischen EU und US wählen und habe den POST für den Realm ein wenig frisiert.
    Den Anfang habe ich um die regionUrl und um einen String fürs Token erweitert.

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private cc As CookieContainer
    3. Private baseUrl As New Uri("https://www.tradeskillmaster.com/black-market")
    4. Private regionUrl As New Uri("https://www.tradeskillmaster.com/user/region-select") 'Das müsste die Url für die Regionsauswahl sein
    5. Private realmUrl As New Uri("https://www.tradeskillmaster.com/user/realm-select")
    6. Private TokenStr As String 'Das ist das X-CSRF-Token


    Die RegionsUrl habe ich mir aus dem POST Header nach Auswahl der Region auf EU geholt.

    Quellcode

    1. POST /user/region-select HTTP/1.1
    2. Host: www.tradeskillmaster.com:443
    3. Accept: */*
    4. Accept-Encoding: gzip, deflate
    5. Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
    6. Content-Type: application/x-www-form-urlencoded; charset=UTF-8


    Nun alles wie gewohnt, bloß das hier das Token Parse und mir in der Textbox wiedergebe.

    VB.NET-Quellcode

    1. Private Sub GetToken()
    2. Dim request As HttpWebRequest = DirectCast(WebRequest.Create(baseUrl), HttpWebRequest)
    3. cc = New CookieContainer()
    4. request.CookieContainer = cc
    5. Dim res As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
    6. Dim streamResponse As Stream = res.GetResponseStream()
    7. Using sr As New StreamReader(streamResponse)
    8. TokenStr = ParseToken(sr.ReadToEnd())
    9. TextBox1.Text = TokenStr
    10. End Using
    11. End Sub


    Anschließend versuche ich den Request zu starten.

    VB.NET-Quellcode

    1. Private Sub GetCode()
    2. Dim request As HttpWebRequest = DirectCast(WebRequest.Create(regionUrl), HttpWebRequest) 'Hier der Request auf die REGION url
    3. request.Method = "POST"
    4. request.Accept = "*/*"
    5. request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8"
    6. request.CookieContainer = cc
    7. request.Headers.Add("X-CSRF-Token: " + TokenStr)
    8. request.Headers.Add("X-Requested-With: XMLHttpRequest")
    9. Dim postData As [String] = "region_select=EU" 'Hier auf die region_select anstatt realmid
    10. Dim data As [Byte]() = Encoding.ASCII.GetBytes(postData)
    11. request.ContentLength = data.Length
    12. Using s As Stream = request.GetRequestStream()
    13. s.Write(data, 0, data.Length)
    14. End Using
    15. Dim res As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
    16. request = DirectCast(WebRequest.Create(baseUrl), HttpWebRequest)
    17. request.CookieContainer = cc
    18. res = DirectCast(request.GetResponse(), HttpWebResponse)
    19. Dim streamResponse As Stream = res.GetResponseStream()
    20. Using sr As New StreamReader(streamResponse)
    21. TextBox3.Text = sr.ReadToEnd()
    22. End Using
    23. End Sub


    Der Request an sich ist erfolgreich, jedoch steht letzenendes Folgendes im Quelltext:

    Quellcode

    1. <select name="region" id="region_select" class="form-control">
    2. <option value="US" selected>US</option>
    3. <option value="EU">EU</option>


    Und mir werden weiterhin die US Server angezeigt.
    Wo liegt mein Fehler? Den POST hab ich ja dementsprechend angepasst, trotzdem ändert er die Region nicht.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Asana“ ()

    VB.NET-Quellcode

    1. Dim postData As [String] = "region_select=EU" 'Hier auf die region_select anstatt realmid



    Der PostString ist falsch. Hast du dir die Formdaten angeschaut ? Es ist regionID
    Das ist meine Signatur und sie wird wunderbar sein!
    Hab natürlich nur im Quelltext nachgeguckt :thumbdown:
    Hab mir den POST nach deiner Antwort nochmal angeguckt und dann direkt das Cookie "RegionId" gesehen, da hätte ich auch selber drauf kommen können^^

    Danke nochmal Mono, du hast mir sehr weiter geholfen! :)

    So klappt es:

    VB.NET-Quellcode

    1. Dim postData As [String] = "RegionId=1"