Fehler HTML-Auslesung

  • VB.NET

Es gibt 32 Antworten in diesem Thema. Der letzte Beitrag () ist von AliveDevil.

    Fehler HTML-Auslesung

    Hallo,

    versuche die Lottozahlen auf einer Webseite auszulesen. Leider funktioniert dies nicht mit dem folgenden Code:

    VB.NET-Quellcode

    1. With WebBrowser_Lotto
    2. .Navigate("http://www.lotto.de")
    3. .Visible = True
    4. End With
    5. Dim suchstring As String = "div class=" & Chr(34) & "lottonumbers" & Chr(34)
    6. MessageBox.Show("Ladevorgang abwarten" & vbNewLine & suchstring)
    7. For Each elem As HtmlElement In _
    8. WebBrowser_Lotto.Document.GetElementsByTagName(suchstring)
    9. MessageBox.Show("Text: " & elem.InnerText)
    10. Next
    11. MessageBox.Show("Ende")
    12.  


    Hier ein Teil der Website auf die ich zugreife

    <div id="hp649gwzLeft">

    <h3>Ziehung Samstag 19.05.2012</h3>

    <div class="lottonumbers">

    <span>20</span>

    <span>34</span>

    <span>36</span>

    <span>42</span>

    <span>43</span>

    <span>47</span>

    </div>

    <div class="hp649clearer"></div>

    <div class="lottonumbers">

    ZZ:
    <span>17</span>

    SZ:
    <span>5</span>

    </div>




    Gefunden wird nichts.
    Gruß Markus
    Hallo und danke für die Antworten / Anregungen.

    Bleibe schon bei dem Versuch das Ziehungsdatum zu ermitteln, stecken. Mein Suchstring sieht nun wie folgt aus:

    suchstring =
    "<div id=" & Chr(34) & "hp649gwzLeft" & Chr(34) & ">." + "<h3>Ziehung.[a-zA-Z]" + ".([0-9]+.[0-9]+.[0-9]+)</h3>"



    Gruß Markus

    VB.NET-Quellcode

    1. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    2. If (WebBrowser_Lotto.Document IsNot Nothing) Then
    3. Dim Elems As HtmlElementCollectionDim WebOC As WebBrowser = WebBrowser_Lotto
    4. Elems = WebOC.Document.GetElementsByTagName("div")
    5. For Each elem As HtmlElement In Elems
    6. Dim IdStr As String = elem.GetAttribute("id")
    7. If ((IdStr IsNot Nothing) And (IdStr.Length <> 0)) Then
    8. If IdStr.Equals("hp649gwzLeft") Then
    9. 'Dim ContentStr As String = elem.GetAttribute("content")Dim ContentStr As String = elem.InnerHtml
    10. MessageBox.Show("Document: " & WebOC.Url.ToString() & vbCrLf & "Description: " & ContentStr)
    11. End If
    12. End If
    13. Next
    14. End If
    15. MsgBox("Ende 1")
    16. End Sub

    Mein Lösungsansatz
    Regex ist zwar schön und gut, aber es geht besser, denn das Framework kann schon von sich aus mit XML umgehen. Dazu nehme ich zunächst mal an, dass das XML-Dokument eher so aussieht:

    XML-Quellcode

    1. <bla>
    2. <div id="hp649gwzLeft">
    3. <h3>Ziehung Samstag 19.05.2012</h3>
    4. <div class="lottonumbers">
    5. <span>20</span>
    6. <span>34</span>
    7. <span>36</span>
    8. <span>42</span>
    9. <span>43</span>
    10. <span>47</span>
    11. </div>
    12. </div>
    13. <div class="hp649clearer">
    14. <div class="lottonumbers">
    15. ZZ:
    16. <span>17</span>
    17. SZ:
    18. <span>5</span>
    19. </div>
    20. </div>
    21. </bla>

    Das oben gepostete Dokument macht nämlich keinen Sinn (es fehlt ein schließendes div).

    Mit dem korrekten Dokument geht das mit LINQs :)

    VB.NET-Quellcode

    1. Dim doc As XDocument = XDocument.Load(New IO.FileStream("bla.xml", IO.FileMode.Open))
    2. Dim numbers = From divs In doc...<div> Where divs.@id = "hp649gwzLeft" AndAlso divs.<div>.@class = "lottonumbers"
    3. From span In divs.<div>.<span> Select Int32.Parse(span.Value)

    Erklärung:
    divs enthält eine Liste generell aller div-Tags, die doc zu finden sind.
    Einschränkend sind aber nur die div-Tags erlaubt, die das id-Attribut mit dem Wert "hp649gwzLeft" haben.
    Zusätzlich müssen die divs ein Sub-Tag vom Typ div haben, dessen class-Attribut den Wert "lottonumbers" hat.
    Es bleibt genau ein div übrig.
    Dieses hat das besagte Sub-div "lottonumbers" und darunter eine Liste von span-Tags, die in span aufgelistet werden.
    Von allen Span-Tags wählen wir den Wert aus, also die Lottozahl.
    Wir erhalten eine typrichtige Liste der Lottozahlen.
    Gruß
    hal2000
    Hallo und Danke,

    Bekomme nun beim download den Fehler angzeigt, dass URL-Formate nicht unterstützt werden.

    VB.NET-Quellcode

    1. Dim doc As XDocument = XDocument.Load(New IO.FileStream("http://www.lotto.de", IO.FileMode.Open))
    2. MessageBox.Show("Ladevorgang abwarten")
    3. Dim numbers = From divs In doc...<div> Where divs.@id = "hp649gwzLeft" AndAlso divs.<div>.@class = "lottonumbers"
    4. From span In divs.<div>.<span> Select Int32.Parse(span.Value)

    Denke das Dokument muss erst anders runter geladen werden.

    Edit: Mit dieser Lösung bekomme ich nun den Fehler Illegales Zeichen im Pfad

    VB.NET-Quellcode

    1. Dim wc As New System.Net.WebClient()
    2. Dim xml As String = wc.DownloadString("http://lotto.de")
    3. Dim doc As XDocument = XDocument.Load(New IO.FileStream(xml, IO.FileMode.Open))
    4. MessageBox.Show("Ladevorgang abwarten")
    5. Dim numbers = From divs In doc...<div> Where divs.@id = "hp649gwzLeft" AndAlso divs.<div>.@class = "lottonumbers"From span In divs.<div>.<span> Select Int32.Parse(span.Value)


    Edit by hal2000:
    - Bitte statt Doppelposts die Editieren-Funktion benutzen.
    Gruß Markus

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

    Und jetzt mal bitte scharf nachdenken, was in der Variable xml steht.

    Edit: Die Lotto-Seite, die du auslesen willst, liefert kein gültiges Dokument aus. Der XML-Parser verzeiht diese Fehler aber nicht, anders als z.B. Firefox. Wäre das Dokument gültig, könntest du LINQ direkt benutzen. Da dem aber nicht so ist, musst du doch wieder mit Regex vorfiltern. Das ist ein bisschen tricky ist und erfordert zudem einen anderen LINQ-Ausdruck. Daher hier mal die Lösung:

    VB.NET-Quellcode

    1. Dim xml As String
    2. Dim rx As New Regex("\<div\sid\=\x22hp649gwzLeft\x22\>.*(?=\n\s*\<div\sid\=\x22hp649gwzRight\x22\>)", RegexOptions.Singleline)
    3. Using wc As New WebClient() With {.Proxy = Nothing}
    4. xml = wc.DownloadString("http://lotto.de")
    5. xml = rx.Match(xml).Value
    6. End Using
    7. Dim doc As XDocument = XDocument.Parse(xml)
    8. Dim numbers = From spans In (From divs In doc...<div> Where divs.@class = "lottonumbers" Select divs).First().<span> Select Int32.Parse(spans.Value())
    9. Dim zdate = doc.<div>.<h3>.Value 'Datum der Ziehung
    Gruß
    hal2000

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „hal2000“ ()

    Danke,

    der Code funktioniert und ich kann das Ziehungsdatum auslesen. Hier ist der Bezugspunkt <h3>. Will ehrlich sein, finde die weiteren Bezugspunkte nicht. Im Grunde möchte ich die Internet-Programmierung auch nicht weiter verfolgen, sondern hier eigentlich nur die Lottozahlen mit meiner Datenbank abgleichen. Ist halt jede Woche die gleiche Prozedur. Lottozahlen (incl. Spiel 77, Super6 und die Zahlen der Glückspirale) abzuschreiben und manuell in eine VB-Form eingeben.
    Gruß Markus
    Der Bezugspunkt für alles (beide Ziehungen, 6 Zahlen, Spiel77 und Super6) ist das übergeordnete div, das alle Informationen enthält:

    XML-Quellcode

    1. <div class="hp649gwz">

    Du musst nun noch die Länge des übergeordneten DIVs finden, damit du den Substring für den Zugriff auf das XML-DOM rausfiltern kannst. Ein ganz anderer Ansatz ist das manuelle Parsen des Dokuments mit den XMLReader-Klassen des Frameworks. Das ist performanter, weil die Arbeit nicht doppelt (Substring ermitteln + Werte auslesen) anfällt, aber auch viel aufwändiger.
    Gruß
    hal2000
    Versuche erneut mich mit dem Problem zu beschäftigen. Irgendwie steige ich nicht dahinter. Habe nun festgestellt, dass ich die Zeile

    VB.NET-Quellcode

    1. Dim numbers = From spans In (From divs In doc...<div> Where divs.@class = "lottonumbers" Select divs).First().<span> Select Int32.Parse(spans.Value())

    zum Auslesen des Ziehungsdatum gar nicht benötige.
    Mein Ansatz

    VB.NET-Quellcode

    1. Dim Zahlen = doc.<div>.<numbers>.Value

    bringt mich nicht weiter.
    Gruß Markus
    Habe nun die Zahlen auslesen können. Sicherlich kein schöner Code aber es funktioniert.

    Spoiler anzeigen


    VB.NET-Quellcode

    1. Dim xml As String
    2. Dim rx As New Regex("\<div\sid\=\x22hp649gwzLeft\x22\>.*(?=\n\s*\<div\sid\=\x22hp649gwzRight\x22\>)", RegexOptions.Singleline)
    3. Using wc As New WebClient() With {.Proxy = Nothing}
    4. xml = wc.DownloadString("http://lotto.de")
    5. xml = rx.Match(xml).Value
    6. End Using
    7. Dim doc As XDocument = XDocument.Parse(xml)
    8. Dim numbers = From spans In (From divs In doc...<div> Where divs.@class = "lottonumbers" Select divs).First().<span> Select Int32.Parse(spans.Value())
    9. Dim zdate = doc.<div>.<h3>.Value 'Datum der Ziehung
    10. Dim Lottodokument As String = xml.ToString
    11. Dim Lottogekuerzt As String = Nothing
    12. Dim Suchstring As String = "<span>"
    13. Dim Hilfsstring As String = Nothing
    14. Dim Suchpostiton As Integer = 0
    15. Dim GesLaenge As Integer = Len(Lottodokument)
    16. Dim DifLaenge As Integer = 0
    17. Dim AbsLaenge As Integer = 0
    18. Dim LottoZahlen(6) As String
    19. Dim ZZahl As String = Nothing
    20. Dim SZahl As String = Nothing
    21. Dim Spiel77 As String = Nothing
    22. Dim Super6 As String = Nothing
    23. ' 1. Lottozahl
    24. Suchpostiton = Lottodokument.IndexOf(Suchstring)
    25. DifLaenge = (GesLaenge - Suchpostiton) - 6
    26. Lottogekuerzt = Strings.Right(Lottodokument, DifLaenge)
    27. Suchstring = "</span>"
    28. Suchpostiton = Lottogekuerzt.IndexOf(Suchstring)
    29. Hilfsstring = Strings.Left(Lottogekuerzt, 2)
    30. If Strings.Right(Hilfsstring, 1) = "<" Then
    31. AbsLaenge = 1
    32. Else
    33. AbsLaenge = 2
    34. End IfLottoZahlen(1) = Strings.Left(Lottogekuerzt, AbsLaenge)
    35. DifLaenge = Lottogekuerzt.Length - AbsLaenge - 13
    36. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    37. Suchpostiton = Lottogekuerzt.IndexOf(Suchstring)
    38. DifLaenge = (Lottogekuerzt.Length - Suchpostiton) + 2
    39. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    40. ' Lottozahl 2 bis 6
    41. For LZ As Integer = 2 To 6
    42. Hilfsstring = Strings.Left(Lottogekuerzt, 2)
    43. If Strings.Right(Hilfsstring, 1) = "<" Then
    44. AbsLaenge = 1
    45. Else
    46. AbsLaenge = 2
    47. End If
    48. LottoZahlen(LZ) = Strings.Left(Lottogekuerzt, AbsLaenge)
    49. DifLaenge = Lottogekuerzt.Length - AbsLaenge - 13
    50. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    51. Suchpostiton = Lottogekuerzt.IndexOf(Suchstring)
    52. DifLaenge = (Lottogekuerzt.Length - Suchpostiton) + 2
    53. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    54. Next
    55. ' Zusatzzahl
    56. Lottogekuerzt = Strings.Right(Lottogekuerzt, (Lottogekuerzt.Length - 1))
    57. Hilfsstring = Strings.Left(Lottogekuerzt, 2)
    58. If Strings.Right(Hilfsstring, 1) = "<" Then
    59. AbsLaenge = 1
    60. Else
    61. AbsLaenge = 2
    62. End If
    63. ZZahl = Strings.Left(Lottogekuerzt, AbsLaenge)
    64. DifLaenge = Lottogekuerzt.Length - AbsLaenge - 13
    65. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    66. Suchpostiton = Lottogekuerzt.IndexOf(Suchstring)
    67. DifLaenge = (Lottogekuerzt.Length - Suchpostiton) + 2
    68. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    69. ' Superzahl
    70. Lottogekuerzt = Strings.Right(Lottogekuerzt, (Lottogekuerzt.Length - 1))
    71. Hilfsstring = Strings.Left(Lottogekuerzt, 2)
    72. If Strings.Right(Hilfsstring, 1) = "<" Then
    73. AbsLaenge = 1
    74. Else
    75. AbsLaenge = 2
    76. End If
    77. SZahl = Strings.Left(Lottogekuerzt, AbsLaenge)
    78. DifLaenge = (Lottogekuerzt.Length - 13)
    79. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    80. Suchpostiton = Lottogekuerzt.IndexOf(Suchstring)
    81. DifLaenge = (Lottogekuerzt.Length - Suchpostiton) + 7
    82. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    83. ' Spiel77
    84. Spiel77 = Strings.Left(Lottogekuerzt, 7)
    85. DifLaenge = (Lottogekuerzt.Length - 13)
    86. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    87. Suchpostiton = Lottogekuerzt.IndexOf(Suchstring)
    88. DifLaenge = (Lottogekuerzt.Length - Suchpostiton) + 6
    89. Lottogekuerzt = Strings.Right(Lottogekuerzt, DifLaenge)
    90. ' Super 6
    91. Super6 = Strings.Left(Lottogekuerzt, 6)
    92.  




    Die Zahlen der Glücksspirale stehen in einer Tabelle. Wie spreche ich die denn nun an?
    Gruß Markus

    VB.NET-Quellcode

    1. Dim Srx As New Regex("\<div\sid\=\x22hpGStable\x22\>.*(?=\n\s*\<div\sid\=\x22hpGStable\x22\>)", RegexOptions.Singleline)
    2. xml = Srx.Match(xml).Value

    Diesen Ansatzpunkt hatte ich wie o.a. schon im Auge. Meine Frage hier, sid steht doch sicher für ID. Die Tabelle hat keine ID. Und wofür steht x22?
    Gruß Markus
    Hi,

    nicht sid, nur id. \s ist ein Whitespace (Leerzeichen). \x22 steht für ein doppeltes Anführungszeichen - siehe auch: codeproject.com/Articles/9099/The-30-Minute-Regex-Tutorial.
    Ich habe mich nicht durch den ganzen Code oben gewühlt - der ist ein wenig anstrengend. Wie ich festgestellt habe, ist es gar nicht so einfach, mit regulären Ausdrücken genau die Anzahl div-Tags rauszufiltern, sodass die Tags auch wieder geschlossen werden (was nötig ist, um LINQ to XML zu verwenden). Ehrlich gesagt ist das mit regulären Ausdrücken gar nicht machbar, denn das verbietet schon die Theorie. Kurz angerissen:

    Reguläre Ausdrücke beschreiben (evtl. nur eine Teilmenge der) reguläre(n) Sprachen. Nun ist die Struktur <div>[irgendwas]<div>[nochwas]<div>[bla]</div></div></div> aber nicht regulär, sondern nur kontextsensitiv, denn sie hat (vereinfacht gesehen) die Form (a^n,b^n). Der reguläre Ausdruck müsste also zählen können (und zwar bis n), was er aber nicht kann. Diese Form ist also nicht mit einem regulären Ausdruck beschreibbar, solange die Anzahl von <div>-Tags unbekannt ist.

    Es wäre einfacher, einen XML-Parser zu verwenden, der fehlertolerant ist - ich habe es aber auf die Schnelle nicht geschafft, den Parser des .NET Frameworks entsprechend zu konfigurieren. Daher müsstest du die Struktur innerhalb der Tabelle bis zum schließenden </div> komplett in einem Ausdruck beschreiben, aber das ist recht aufwändig. Ansatz dafür: beschreibe z.B.

    XML-Quellcode

    1. <div x=abc>
    2. <div>
    3. bla
    4. <div>
    5. blubb
    6. </div>
    7. </div>
    8. </div>
    mit

    Quellcode

    1. <div\sx\=\abc>.*<div.*<div.*<\/div><\/div><\/div>
    . Dann solltest du mit LINQ filtern können.
    Gruß
    hal2000