Regex läuft instabil, bei Quellcode auslesen.

  • VB.NET

Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Regex läuft instabil, bei Quellcode auslesen.

    Hallo liebe Forengemeinde,

    ich habe eine Programm fast fertig entwickelt, mit dem Aktienkurse ausgelesen werden und bestimmter Prüfungen unterzogen werden. Nun hat sich ein für mich unerklärliches Phänomen in eine bestimmte Funktion eingebaut:

    Die Funktion funktioniert in 70% aller Fälle.
    Ich habe hier den "problematischen" Abschnitt der Funktion einmal aufgeführt:

    VB.NET-Quellcode

    1. Dim WebReader As String
    2. WebReader= httpPost("http://de.finance.yahoo.com/q/hp?s=" + yahoo + "&b=" + tag1 + "&a=" + monat1.ToString + "&c=" + jahr1.ToString + "&e=" + tag1 + " &d=" + monat1.ToString + "&f=" + jahr1.ToString + "&g=d")
    3. Dim r As New Regex("<tr><td class=""yfnc_tabledata1"" nowrap align=""right"">.*</td><td class=""yfnc_tabledata1"" align=""right"">.*</td><td class=""yfnc_tabledata1"" align=""right"">.*?</td>")
    4. Dim matches As MatchCollection = r.Matches(WebReader)
    5. Dim ebitString As String = ""
    6. Dim ebitZahl As Double
    7. For Each itemcode As Match In matches
    8. ebitString = itemcode.ToString()
    9. Next
    10. ebitString = ebitString.Substring(260, 30)
    11. ebitZahl = Convert.ToDouble(ConvertSymbol(ebitString))
    12. Return ebitZahl


    Die problematische Stelle, wo sich das Programm ab und zu aufhängt ist, wenn ich hiermit:

    VB.NET-Quellcode

    1. ebitString = ebitString.Substring(260, 30)
    den String, den ich aus dem Quelltext der Seite ausgelesen habe, auf den gewünschten Abschnitt verkürze. Dann bleibt logischerweise ein 30 Zeichen großer String übrig, der nur eine Gleitkomma Zahl enthält. Diese filter ich mit der ConvertSymbol Funktion heraus.

    Nun ist das Phänomen, dass ich mit Regex manchmal den gewünschten Bereich auslesen kann, 10 Sekunden später aber nicht mehr. Bei der selben Aktie und von der selben Seite wohlgemerkt! Deshalb habe ich dann nur noch einen leeren String... Hat jemand eine Idee woran das liegen könnte?
    Bei erfolgreichen Auslesen sieht mein SubString so aus: ""right">32,25</td><td class="y"

    Ich würde mich über jeden Tipp und jede Vermutung sehr freuen, da ich absolut nicht weiter weiß.

    Viele Grüße
    Hab mir mal eine Yahoo Seite angesehen. Aber im Sourcecode finde ich jede Menge "yfnc_tabledata1". Aber kein TD mit ClassName "yfnc_tabledata1" wo sich davor ein <tr> befindet. Könnt sein, dass ich die falsche Seite geöffnet habe. Hab mir "http://de.finance.yahoo.com/q?s=YHOO" angeschaut, also ohne weitere Parameter. Oder sind die vom Aufbau alle gleich?

    Würde gerne versuchen zu helfen, aber ich weiß nicht, welchen Teil der Seite du wirklich herausnehmen willst.

    Agita schrieb:

    Hab mir mal eine Yahoo Seite angesehen. Aber im Sourcecode finde ich jede Menge "yfnc_tabledata1". Aber kein TD mit ClassName "yfnc_tabledata1" wo sich davor ein befindet. Könnt sein, dass ich die falsche Seite geöffnet habe. Hab mir "http://de.finance.yahoo.com/q?s=YHOO" angeschaut, also ohne weitere Parameter. Oder sind die vom Aufbau alle gleich?

    Würde gerne versuchen zu helfen, aber ich weiß nicht, welchen Teil der Seite du wirklich herausnehmen willst.
    Danke, dass du dich der Sache schonmal angenommen hast.
    Die Seite ist nicht ganz die richtige, da ich von einer bestimmten Aktie die "Historische Kurse" aufrufen möchte.

    Dazu rufe ich die Seite mit folgenden Parametern auf:
    de.finance.yahoo.com/q/hp?s=LI…2012&e=01&d=05&f=2012&g=d

    Sprich:
    de.finance.yahoo.com/q/hp?s=[Aktienname]&b=[Start-Tag]&a=[Start-Monat(-1)]&c=[Start-Jahr]&e=[End-Tag]&d=[End-Monat(-1)]&f=[End-Jahr]&g=d

    Kann es denn sein, dass die Seite mir des öfteren einen anderen Quellcode liefert, obwohl ich die Seite mit sehr genauen Parametern aufrufe ?!

    ErfinderDesRades schrieb:

    Ich vermute das bereits in post#2 - aber du wirst es sicherlich bald herausfinden ;)

    Huch, tut mir leid, dass ich deinen Post überlesen habe. Hatte wohl noch nicht genug Kaffee getrunken ... :P

    Wie könnte das denn Zustande kommen? Ich mein ich habe manuell die Seite 100 Mal manuell neu geladen und es hat sich nichts verändert, bis auf die Werbung auf der rechten Seite. Kann das etwas damit zu tun haben?
    Du willst also diese Werte haben?

    Quellcode

    1. Datum Eröffnungskurs Max. Tief Schluss Volumen Adj.Schluss*
    2. 1.Jun2012 123,95 124,35 120,45 121,30 790.800 121,30


    Wenn ja, dann schau

    VB.NET-Quellcode

    1. Imports System.Text.RegularExpressions
    2. Module Module1
    3. Public WC As Net.WebClient = New Net.WebClient()
    4. Public Sub test()
    5. Dim what As String = "LIN.DE"
    6. Dim day1 As String = "01"
    7. Dim month1 As String = "05"
    8. Dim year1 As String = "2012"
    9. Dim Url As String = "http://de.finance.yahoo.com/q/hp" & _
    10. "?s=" & what & _
    11. "&b=" & day1 & _
    12. "&a=" & month1 & _
    13. "&c=" & year1 & _
    14. "&e=" & day1 & _
    15. "&d=" & month1 & _
    16. "&f=" & year1 & _
    17. "&g=d"
    18. Dim SourceCode As String = WC.DownloadString(Url)
    19. 'Console.WriteLine(SourceCode)
    20. 'Console.ReadLine()
    21. 'Console.Clear()
    22. Dim pattern As String = _
    23. " <td\s+ " & _
    24. " class=""yfnc_tabledata1""\s+ " & _
    25. " (?:nowrap\s+)? " & _
    26. " align=""right""> " & _
    27. " (?<VALUE>.*?) " & _
    28. " <\/td> "
    29. Dim r As New Regex(pattern, RegexOptions.IgnoreCase Or RegexOptions.IgnorePatternWhitespace)
    30. Dim matches As MatchCollection = r.Matches(SourceCode)
    31. Dim Values As New List(Of String)
    32. For Each match As Match In matches
    33. Values.Add(match.Groups("VALUE").Value)
    34. Next
    35. If Values.Count = 7 Then
    36. MsgBox("Datum: " & Values(0))
    37. MsgBox("Eröffnungskurs: " & Values(1))
    38. MsgBox("Max.: " & Values(2))
    39. MsgBox("Tief: " & Values(3))
    40. MsgBox("Schluss: " & Values(4))
    41. MsgBox("Volumen: " & Values(5))
    42. MsgBox("Adj. Schluss: " & Values(6))
    43. Else
    44. MsgBox("ERROR")
    45. End If
    46. End Sub
    47. End Module

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

    Wow ich bin überwältigt Agita ...

    Das ist genau die Lösung für mein Problem.

    Wie hast du das denn so genau per Regex eingrenzen können?! Ich habe mich mehrere Stunden in die Materie eingelesen und war froh, dass ich im Quellcode ab einem bestimmten Punkt den Rest auslesen konnte und habe mir dann per Substring die gewünschten Werte "herausgeschnitten".

    Ich mein wie geht man denn am besten vor, wenn man einen bestimmten Abschnitt ausgrenzen muss?

    vielen dank schonmal!
    Mann muss verstehen wie Regex arbeitet. Es gibt auch Sachen, die ich (noch) nicht kenne. In diesem Falle muss man wissen wo und wie man zu suchen hat.

    Stichworte sind:

    Captures/Groups:
    - Gruppe ohne Rückgabe (?:HALLO)
    - Gruppe mit Rückgabe (HALLO)
    - Mit Rückgabe als Name (?<what>HALLO)

    Metacharacters
    - Ein beliebiges Zeichen .
    - Und das 1 oder mehrmals .+
    - Beliebiges Zeichen 1 oder kein mal .?
    - Beliebiges Zeichen kein oder mehrmals .*
    - Und das ohne "gierig" zu sein .*?

    RegexOptions.IgnoreCase:
    Groß und Kleinschreibung ignorieren

    RegexOptions.IgnorePatternWhitespace:
    Damit kann ich so viele Leerzeichen reinschreiben wie ich will
    Allerdings muss ich, wenn ich danach suchen will, \s benutzen

    \s
    Ein "Space" Character.
    Kann eine Leerzeile sein,
    ein Tabulator, oder auch
    ggf eine neue Zeile.

    Schau mal, alle TD's in denen die Werte stehen sehen gleich aus,
    ausser das erste, denn dort steht zusätzlich "nowrap" drinne.
    Du musst also von vornherein nach Mustern suchen, die du ausnutzen kannst.
    In diesem Falle ist es für jeden Wert je ein <td ...>1234.56</td>

    Ich suche also nur danach. Nachdem er das erste gefunden hat, sucht er das nächste.
    Somit brauchst du nicht deine 200 Zeichen suchen, sondern nur die Teil-TD's

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

    Klasse Beitrag! Hat mir schon sehr weitergeholfen :)

    Ich weiß, dass du sicher besseres zu tuen hast, aber kannst du mir sagen ob ich deine Anleitung richtig umgesetzt habe für folgende Seite:
    onvista.de/aktien/kennzahlen/fundamental.html?ID_OSI=82632

    Ich möchte die 3 Werte für Eigenkapitalquote auslesen:
    <tr class="hr"><td class="hl">Eigenkapitalquote in %</td><td>42,00</td><td>42,26</td><td>37,68</td></tr>

    Nach Regex:

    VB.NET-Quellcode

    1. Dim pattern As String
    2. pattern="<tr class=""hr""><td class=""hl"">Eigenkapitalquote in %</td><td>(?<VALUE>.*?)</td></tr>"


    Ich kann im Moment keinen "Feldtest" durchführen, da man onVista mit Cookies vortäuschen muss, dass man einen Browser ist. Aber habe ich deine Anleitung richtig interpretiert?

    Vielen vielen Dank nochmal!
    In diesem Falle müsstest du dann doch schreiben

    VB.NET-Quellcode

    1. pattern = _
    2. "<tr class=""hr"">" & _
    3. "<td class=""hl"">" & _
    4. "Eigenkapitalquote in \%</td>" & _
    5. "<td>(?<VALUE1>.*?)</td>" & _
    6. "<td>(?<VALUE2>.*?)</td>" & _
    7. "<td>(?<VALUE3>.*?)</td>" & _
    8. "</tr>"

    Wobei schöner wäre vielleicht

    VB.NET-Quellcode

    1. pattern = _
    2. "<tr class=""hr"">" & _
    3. "<td class=""hl"">" & _
    4. "Eigenkapitalquote in \%</td>" & _
    5. "<td>(?<VALUE1>\d+\,\d+|\d+)</td>" & _
    6. "<td>(?<VALUE2>\d+\,\d+|\d+)</td>" & _
    7. "<td>(?<VALUE3>\d+\,\d+|\d+)</td>" & _
    8. "</tr>"


    Gibt noch eine andere Möglichkeit, die TD's durch folgendes zu finden

    VB.NET-Quellcode

    1. (?<TD>\<td\>...\</td\>)*

    und dann per myMatch.Groups("TD").Captures(...).Value aus zu lesen.

    Aber denke, das sollte reichen =)

    VB.NET-Quellcode

    1. Dim pattern As String = _
    2. "<tr class=""hr"">" & _
    3. "<td class=""hl"">" & _
    4. "Eigenkapitalquote in \%</td>" & _
    5. "<td>(?<VALUE1>\d+\,\d+|\d+)</td>" & _
    6. "<td>(?<VALUE2>\d+\,\d+|\d+)</td>" & _
    7. "<td>(?<VALUE3>\d+\,\d+|\d+)</td>" & _
    8. "</tr>"
    9. Dim r As New Regex(pattern, RegexOptions.IgnoreCase Or RegexOptions.IgnorePatternWhitespace)
    10. Dim matches As MatchCollection = r.Matches(SourceCode)
    11. Dim Values As New List(Of String)
    12. For Each match As Match In matches
    13. Values.Add(match.Groups("TD").Captures("VALUE1").Value)
    14. Next
    15. If Values.Count = 1 Then
    16. MsgBox("Wert1: " & Values(0))
    17. Else
    18. MsgBox("ERROR")
    19. End If


    Mache ich irgendwas falsch beim Auslesen?! Values.Count ist immer = 0 :(

    Danke trotzdem nochmal! :)
    Das hier weg

    VB.NET-Quellcode

    1. For Each match As Match In matches
    2. Values.Add(match.Groups("TD").Captures("VALUE1").Value)
    3. Next

    Das hier hinzu

    VB.NET-Quellcode

    1. Values.Add(match.Groups("VALUE1").Value)
    2. Values.Add(match.Groups("VALUE2").Value)
    3. Values.Add(match.Groups("VALUE3").Value)
    Schade, dass ich von dem Regex zu wenig verstehe, um den Fehler selbst zu finden.

    Wenn du irgendwann im Laufe des Tages 5min Zeit hast, könntest du mir vielleicht helfen.

    Den Quellcode lese ich in Form1 ein:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Function httpPost(ByVal URL As String) As String
    3. Try
    4. Dim webRequest As System.Net.HttpWebRequest = CType(System.Net.WebRequest.Create(URL), System.Net.HttpWebRequest)
    5. Dim cookies As New Net.CookieContainer
    6. webRequest.CookieContainer = cookies 'Cookies
    7. webRequest.UserAgent = "Mozilla/5.0 (X11; U; Linux i586; en-US; rv:1.7.3) Gecko/20040924 Epiphany/1.4.4 (Ubuntu)"
    8. Dim webResponse As System.Net.HttpWebResponse = CType(webRequest.GetResponse(), System.Net.HttpWebResponse)
    9. Dim responseStream As System.IO.Stream = webResponse.GetResponseStream()
    10. Dim responseEncoding As System.Text.Encoding = System.Text.Encoding.UTF8
    11. Dim responseReader As New IO.StreamReader(responseStream, responseEncoding)
    12. Dim responseContent As String = responseReader.ReadToEnd()
    13. Return responseContent
    14. Catch ex As Exception
    15. Return "Error while sending post data:" & ex.Message
    16. End Try
    17. End Function
    18. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    19. test(httpPost("http://www.onvista.de/aktien/kennzahlen/fundamental.html?ID_OSI=82632"))
    20. End Sub
    21. End Class


    Und dein Modul habe ich ein wenig gekürzt zu Testzwecken:

    VB.NET-Quellcode

    1. Imports System.Text.RegularExpressions
    2. Module Module1
    3. Public Sub test(ByVal strrring As String)
    4. Dim SourceCode As String = strrring
    5. Dim pattern As String = _
    6. "<tr class=""hr"">" & _
    7. "<td class=""hl"">" & _
    8. "Eigenkapitalquote in \%</td>" & _
    9. "<td>(?<VALUE1>\d+\,\d+|\d+)</td>" & _
    10. "<td>(?<VALUE2>\d+\,\d+|\d+)</td>" & _
    11. "<td>(?<VALUE3>\d+\,\d+|\d+)</td>" & _
    12. "</tr>"
    13. Dim r As New Regex(pattern, RegexOptions.IgnoreCase Or RegexOptions.IgnorePatternWhitespace)
    14. Dim matches As MatchCollection = r.Matches(SourceCode)
    15. Dim Values As New List(Of String)
    16. For Each match As Match In matches
    17. Values.Add(match.Groups("VALUE1").Value)
    18. Values.Add(match.Groups("VALUE2").Value)
    19. Values.Add(match.Groups("VALUE3").Value)
    20. Next
    21. If Values.Count = 3 Then
    22. MsgBox("Datum: " & Values(0))
    23. MsgBox("Eröffnungskurs: " & Values(1))
    24. MsgBox("Max.: " & Values(2))
    25. Else
    26. MsgBox("ERROR")
    27. End If
    28. End Sub
    29. End Module


    Ich kriege immer values.count = 0. Ich konnte keinen Tippfehler bei dem Regex sehen :S
    Ließt er denn den SourceCode wenigstens richtig aus von der Seite? Wäre cool, wenn du den Teil des SourceCodes posten könntest. am besten immer ein wenig mehr, also nicht nur den Teil den du haben willst, sondern davor noch ein bisschen mehr und dannach auch ein bisschen. Wenn es zu viel sein sollte, dann kannst du paste2.org/ benutzen und den Link posten =)

    Vielleicht sollte ich auch mal nen kleines Tut schreiben =) Gibt viele Sachen auf die ich gerade bei meinem Projekt stoße, die anderen bestimmt helfen könnten =)
    Ja er ließt den SourceCode komplett aus.

    PHP-Quellcode

    1. </tr><tr class="hr"><td class="hl">Eigenkapitalquote in %</td><td>42,00</td><td>42,26</td><td>37,68</td></tr><tr class="hgrau2 hr"><td class="hl">Verschuldungsgrad in %</td><td>138,10%</td><td>136,65%</td><td>165,39%</td></tr><tr class="hr"><td class="hl">dynamischer Verschuldungsgrad</td><td>691,30</td><td>641,04</td><td>709,34</td></tr><tr class="hgrau2 hr">


    Ein Tutorial wäre sowas von super! Ich bewunder Leute, die mit Regex sogut umgehen können wie du. :)
    HA :D Bin ich doof, dass ich das übersehen konnte =)

    Du benutzt noch "RegexOptions.IgnorePatternWhitespace". Bedeutet, dass er Leerzeichen im Pattern nicht als Leerzeichen erkennt und ignoriert. Das benutze ich persönlich um den String übersichtlicher zu gestalten. Du kannst entweder die Option löschen, oder folgendes benutzen:

    VB.NET-Quellcode

    1. Dim pattern As String = _
    2. "<tr\s+class=""hr"">" & _
    3. "<td\s+class=""hl"">" & _
    4. "Eigenkapitalquote\s+in\s+\%</td>" & _
    5. "<td>(?<VALUE1>\d+\,\d+|\d+)</td>" & _
    6. "<td>(?<VALUE2>\d+\,\d+|\d+)</td>" & _
    7. "<td>(?<VALUE3>\d+\,\d+|\d+)</td>" & _
    8. "</tr>"
    Hier mal eine andere Lösung.
    Der SourceCode der Webseite im Anhang als 'web.txt'. (Stand: 8.8.12)

    VB.NET-Quellcode

    1. Imports System.Text.RegularExpressions
    2. Module Module1
    3. Sub Main()
    4. Dim SourceCode As String = System.IO.File.ReadAllText("d:\##\web.txt")
    5. Dim pattern As String = "Eigenkapitalquote in %</td><td>([0-9,]+)</td><td>([0-9,]+)</td><td>([0-9,]+)</td>"
    6. Dim R As New Regex(pattern, RegexOptions.IgnoreCase Or RegexOptions.Multiline)
    7. Dim Matches As Match = R.Match(SourceCode)
    8. While Matches.Success
    9. Dim i As Integer
    10. For i = 1 To Matches.Groups.Count
    11. Dim Values As Group = Matches.Groups(i)
    12. If Values.Success Then
    13. MsgBox(Values.Value)
    14. End If
    15. Next
    16. Matches = Matches.NextMatch()
    17. End While
    18. End Sub
    19. End Module
    Dateien
    • web.txt

      (28,01 kB, 328 mal heruntergeladen, zuletzt: )