Microsoft Excel

Herbers Excel/VBA-Archiv

Informationen und Beispiele zum Thema MsgBox
BildScreenshot zu MsgBox MsgBox-Seite mit Beispielarbeitsmappe aufrufen
Informationen und Beispiele zum Thema Frame
BildScreenshot zu Frame Frame-Seite mit Beispielarbeitsmappe aufrufen

Unterobj. von html getElements..() weiter parsen


Betrifft: Unterobj. von html getElements..() weiter parsen von: ingo
Geschrieben am: 23.09.2019 12:00:38

Ich versuche derzeit mit vba webseiten auszulesen bzw. bestimmte werte auszulesen und in excel weiter zu verarbeiten.
Das funktioniert so weit auch. Ich habe nur das Problem das ich mir nur vom MSHTML.HTMLDocument-Objekt mir einzelne oder mehrere Objekte zurückliefern lassen kann. Haben dies aber weitere Objekte Objekte weiß ich nicht, wie ich darauf zugreifen kann.
Ich kann die getElements...-Funktionen nicht auf das zurückgelieferte Objekt anwenden. Per Index kann ich nur direkt auf bei Rückgabe auf das Zurückgelieferte Objekt zugreifen wie z.B. hier

            Set htmlAnswers = oHtml.getElementsByClassName("klappbar offen")(0)

.

Alle anderen Versuche das htmlAnswerObjekt weiter zu filtern, oder auch in anders Objektedefinierte Objekt (z.B HTMLDocument) führen auch zu keinem Erfolg .

Weiß hier vlt. jmd eine Lösung.
Wenn ich da etwas komplett falsch mache bitte auch antworten.
Aber in mehreren Schritten durchiterieren brauche ich also das innerhtml mit regex zu parsen war auch schon meine idee, der (post dazu kommt auch von mir).

---------- beginn globaler bereich ---------------
Dim objXML As MSXML2.DOMDocument60

Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" _
   Alias "URLDownloadToFileA" ( _
   ByVal pCaller As Long, _
   ByVal szURL$, _
   ByVal szFileName$, _
   ByVal dwReserved As Long, _
   ByVal lpfnCB As Long) As Long
---------- ende globaler bereich ---------------

-----------anfang funktions bzw. prozedur-bereich -------------
Dim fso As Object
    ' FileSystem Objekt anlegen
    Set fso = CreateObject("Scripting.FileSystemObject")

    'Variablen und objekte deklarieren
    Dim oHtml As MSHTML.HTMLDocument
    Dim htmlAnswers As Object 'MSHTML.DispHTMLElementCollection
    Dim sLocalFilename As String

    ' variablen initialisieren
    sLocalFilename = "D:\16092017\webseite_vba_donload.html"
    Set oHtml = Nothing

            ' oHtml kann auch mit ExplorerObjek.Navigate  zugewiesen werden
            Set oHtml = oHtml4.createDocumentFromUrl(sLocalFilename, "")

            ' Beispiel html-teile mit class =cf anfordern
            Set htmlAnswers = oHtml.getElementsByClassName("cf")
            ' Beispiel html-teile mit class =klappbar offen anfordern und zwar nur das 1,.  _
element
            Set htmlAnswers = oHtml.getElementsByClassName("klappbar offen")(0)


----------- Ende funktions bzw. prozedur-bereich -------------

  

Betrifft: AW: Unterobj. von html getElements..() weiter parsen von: Zwenn
Geschrieben am: 23.09.2019 16:39:49

Hallo Ingo,

da ich annehme, es geht noch immer um die Jobseiten, gebe ich Dir mal ein Makro, in dem Du siehst, wie Du die aufzuklappenden Seiteninhalte anklickst. Ich habe das exemplarisch für die Kontaktdaten gemacht, weil diese Seiteninhalte nachladen und ich Dir da gleich mit zeigen kann, das an solchen Stellen eine manuelle Pause eingebaut werden muss.

Anschließend direkt die Demonstration, wie man an Inhalte kommen kann, wenn man nicht direkt auf das richtige Tag zugreifen kann. Zusätzlich der Trick, wie man mit getElementByID() in VBA umgehen muss.

Alles in allem ist das für Fortgeschrittene, aber da Du selber schon etwas ausprobiert hast, kannst Du es vielleicht nachvollziehen.

Die urlmon-Geschichte brauchst Du für Dein Vorhaben nicht. Die wird benötigt, wenn Du Dateien runterladen möchtest, die Du auf der Festplatte speichern willst. Z.B. Bilder von einer Seite. Das Speichern der HTML-Dateien brauchst Du für Deinen Zweck aber nicht.

Da Du auf Seiten zugreifst, die AJAX-Inhalte haben, an die Du ran musst, wirst Du den Internet Explorer nutzen müssen. Der kann mit JavaScript umgehen, was Du unbedingt brauchst. AJAX-Inhalte sind Teile einer Seite, die nachgeladen werden, wenn man etwas bestimmtes anklickt oder z.B. bei Facebook runterscrollt.

Ich hoffe Du kannst damit was anfangen:
(Die url musst Du wieder auf eine Zeile ziehen. Die ist so lang, dass die Forensoftware sie mehrfach umbricht. Die ParentNode-Zeile wird auch einmal umgebrochen.)

Sub JobAngebotKontaktdatenAuslesen()

'Für den Internetzugriff
Dim browser As Object
Dim url As String
Dim knotenWurzel As Object
Dim knotenStamm As Object
Dim knotenAst As Object
Dim adresse As String

url = "https://jobboerse.arbeitsagentur.de/vamJB/stellenangeboteFinden.html?execution=e2s1& _
_eventId_detailView&bencs=QFBrF0U0oHouAVZy%2BcgzlA%2FZD1%2FHs32Jn0ofKD5zPNsALGnhYCZhDoj8Iza0WZmT&bencs=PhdLA4E%2BZti4%2BR82eMYyAcr2f0es%2BqheKgN81DAVb8Zwu34FvGGarYrjx5XFt%2FOrdhxxOFAlOiPWqZazQUCgEg%3D%3D&bencs=Y7sCHM0u4zE4Urarmr5%2BLi9RnFDRlglOQtQilhdvkuimLwetFkU65Jm%2BMYEFCkBNC6MrBGXWadJCjLTenJ%2BnyZ5oSz5vrZLFUSO96PY2j2HZajvIMiyAbiAKXaPDg10c"
  
  'Internet Explorer initialisieren, Sichtbarkeit festlegen,
  'URL aufrufen und warten bis Seite vollständig geladen wurde
  Set browser = CreateObject("internetexplorer.application")
  browser.Visible = False 'Zum sichtbar machen auf True setzen
  browser.navigate url
  Do Until browser.ReadyState = 4: DoEvents: Loop
  
  'Um die Kontaktdaten auszulesen, müssen sie auf der Seite ausgeklappt werden
  'Es handelt sich um einen Link, der angeklickt werden muss
  'Also sammeln wir zunächst alle Links in einer NodeCollection ein
  Set knotenWurzel = browser.document.getElementsByTagName("a")
  
  'Prüfen ob die NodeCollection gebildet werden konnte
  If Not knotenWurzel Is Nothing Then
    'Wenn ja, alle elemente der Collection durchgehen
    
    For Each knotenStamm In knotenWurzel
      'Wir suchen das HTML-Element für die Kontaktdaten
      'Dieses Wort steht im Linktext, der auf der Seite angezeigt wird
      If InStr(1, knotenStamm.innertext, "Kontaktdaten") > 0 Then
        'Wenn das richtige Element in der NodeCollection gefunden wurde
        'Aufklappen durch Anklicken auslösen
        knotenStamm.Click
        'Warten bis Seite neu geladen
        'Hier durch manuelle Pause, da die Abfrage auf ReadyState
        'schon auf 4 (was für "complete" steht) gesetzt ist
        'Ich habe 3 Sekunden gewählt
        Application.Wait (Now + TimeSerial(0, 0, 3))
        'Die Schleife verlassen, da wir haben was wir wollen
        Exit For
      End If
    Next knotenStamm
    
    'Wir gehen davon aus, dass das Aufklappen funktioniert hat
    'Da wir auf eine ID zugreifen, müssen wir die Fehlerbehandlung abschalten
    'weil für getElementByID() keine NodeCollection gebildet wird, sondern
    'direkt ein Laufzeitfehler angezeigt wird, wenn es die ID im HTML-Code
    'nicht gibt
    On Error Resume Next
    'Die etwas kryptische ID sollte sich nach dem Aufklappen im Quelltext befinden
    '(Ich habe die ID nur bei zwei verschiedenen Jobangeboten getestet)
    'Das HTML-Element dazu enthält allerdings nur den Firmennamen. Um an die ganze
    'Adresse zu kommen, müssen wir deshalb auf den Elternknoten zugreifen. Das ist
    'ein p-Tag, welches die gesamte Adresse umschließt
    Set knotenAst = browser.document.getElementByID("ruckfragenundbewerbungenan_-2147483648"). _
ParentNode
    'Die Adresse übernehmen wir in die Variable Adresse, indem wir den auf der Seite
    'lesbaren Text mit innertext in die String-Variable adresse schreiben
    'HTML-Zeilenumbrüche und Codierungen für Sonderzeichen, wie deutsche Umlaute,
    'werden automatisch richtig übernommen
    adresse = knotenAst.innertext
    'Da der kritische Part hier endet, schalten wir die Fehlerbehandlung wieder ein
    On Error GoTo 0
  End If
  
  'Ausgelesene Kontaktadresse anzeigen
  MsgBox adresse
  
  'Aufräumen
  browser.Quit
  Set browser = Nothing
  Set knotenWurzel = Nothing
  Set knotenStamm = Nothing
  Set knotenAst = Nothing
End Sub

Viele Grüße,

Zwenn


  

Betrifft: AW: Unterobj. von html getElements..() weiter parsen von: ingo
Geschrieben am: 24.09.2019 13:54:45

Hallo Zwenn,

erst mal vielen Dank für deine Antwort und die ausführliche Kommentierung (wäre aber nicht nötig gewesen ich kann gut Code lesen & verstehen).
ich hatte 3 Probleme, bei 2 Punkte hatte ich zwischenzeitlich selber schon Ansätze, aber danke für das Klick-event dass hatte ich nur gelesen wie es theoretisch funktioniert.


  • 1. wie kann ich über Variable mittels GetElementByID(..), GetElementByClassName(..),.. zugreifen nachdem ich einmal auf das HTMLDocument-Objekt gefiltert habe.
    Hier habe ich keine Lösung kann das aber mit diversen Methodiken lösen (siehe unten).



  • 2. die html-tags liegen nicht in geordneter Weise vor (überflüssige Tabs, Leerzeichen, Zeilenumbrüche), wenn man also mit innerHTMl oder innerText darauf zu greift muß man hier eine menge filtern. Wenn man das unterste Element erwischt, fällt hier vieles weg z.B.
    10000-1173725470-S
    . Sollte Punkt1 sich doch irgendwie umsetzen lassen, wäre das schon mal eine Erleichterung, dann müssten nur nur Htmlformatierungen bzw. Sonderzeichen etc. angepasst werden.

    Hier ist nur ein ein Beispiel, wo ersichtlich wird, dass dass man zwar filterroutinen angepasst schreiben kann, aber sehr umständlich werden können.
    IT, DV, Computer
    
    
    
    Erweiterte Kenntnisse
    Erweiterte Kenntnisse
    Datenbank Oracle, Datenbanksprache SQL, HTML, XML, XHTML, XAML, XSLT, JavaScript-Framework _ jQuery, Programmiersprache PHP
    Ich weiß zwar das es viele RegEx-Beipiele für Html gibt, ich sehe hier den Aufwand aber geringer, wenn ich diese Modifikationen selber implementiere.
    Ich lasse mich aber auch gerne überzeugen falls darin jmd. firm ist.



  • 3.verstecke Inhalte (komplett gelöst)
    Ein Problem war, wenn ich vba-gesteuert einen Download der html-Datei durchführe,dass z.B. Kontaktdaten.
    Hier habe ich Dank Zwenn eine geeignete Implementierung, hier hatte ich vorher nur gelesen, wie es funktioniert.


  • Ich bin wie gesagt an einem Punkt, wo ich es umsetzten kann, wenn an vielen Stellen noch sehr umständlich ist bzw. sein wird.

    Sollt jmd. wissen, ob und wie man eine Mehrfachfilterung mit den GetElementBy-Funktionen umsetzen kann um den quellcode übersichtlicher zu halten, bitte melden.

    Und der andere Punkt sollte jmd. "ad hock" wissen, wie man mit wenig Aufwand regex auf die Text loslassen kann, ebenfalls bitte melden.
    Ich würde mich hier dann darauf festlegen das ich falls kein Mehrfachfilterung mit GetElementBy möglich ist dass Filterfunktionen mit Iteration durch die Ebenen und Element nachbilde, so dass ich maximal mit überflüssigen führenden Tabs und Leerzeichen zu tun habe.
    Da ich zukünftig mehr mit dem Thema Web-Scraping vorhabe machen frage ich doch noch mal expilzit nach oobwohl ich auf umständliche Art und Weise dieses schon spezifisch umsetzen könnte.

    Bessere Lösung gewünscht für:

  • Mehrfachfilterung


  • - Nutztext besser extrahieren



  • 'Bespiel für Zugriff auf unterschiedliche Ebenen  mittels Variable
    Set objElem = oHtml.getElementByID("containerInhaltSpalte1").Children(0)
    Set objElem = oHtml.getElementByID("containerInhaltSpalte1").Children(0).Children(0)
    Set objElem = oHtml.getElementByID("containerInhaltSpalte1").Children(0).Children(0).Children(0) _
    
    Set objElem = oHtml.getElementByID("containerInhaltSpalte1").Children(0).Children(0).Children(1) _
    
    

    For Each tElem In oHtml.getElementByID("containerInhaltSpalte1").Children(0).Children(0). _
    Children(2).Children(1).Children
        'Debug.Print tElem.textContent
    Next
    

    For Each tElem In oHtml.getElementsByClassName("ausklappBox").Item(3).Children
        Debug.Print tElem.textContent
    Next
    



      

    Betrifft: AW: Unterobj. von html getElements..() weiter parsen von: Zwenn
    Geschrieben am: 24.09.2019 21:19:21

    Hallo Ingo,

    heute Abend habe ich keinen Elan mehr mir Dein Anliegen im Detail anzusehen. Für das weitere Bearbeiten eines zuvor gebildeten DOM-Objektes habe ich aber schonmal in der Vergangenheit an einem Beispiel gezeigt, wie das geht. Ich denke damit solltest Du einen Teil Deiner Fragen lösen können.

    In dem Beitrag schreibe ich noch von Arrays, die mit den Get-Methoden gebildet werden. Das ist falsch, es sind die bereits erwähnten NodeCollections. Das nur zum Wording:
    https://www.herber.de/cgi-bin/callthread.pl?index=1560197#1560268

    Für weitere Hilfe wäre eine Beispielmappe mit Deinem Projektstand sehr hilfreich. Im Moment ist das alles etwas theoretisch. Persönliche Daten musst Du ggf. anonymisieren.

    Viele Grüße,

    Zwenn


      

    Betrifft: AW: Unterobj. von html getElements..() weiter parsen von: ingo
    Geschrieben am: 25.09.2019 10:14:11

    Hallo Zwenn,

    erst mal danke für den Link.
    Bin leider die nächsten Tage im Urlaub, daher wird die nächsten Tage nichts passieren.

    Aber eine sache verstehe ich nicht.

    warum geht das ?

    Set knotenAst = oIE.document.GetElementsByClassName("fightResultsInner")(0). _
                GetElementsByTagName("p")(0).GetElementsByTagName("em")(0)
    
    und das nicht?
    dim  knotenObj1,knotenObj2 as Object
    
    Set knotenObj1 = oIE.document.GetElementsByClassName("fightResultsInner")(0)
    Set knotenObj2 = knotenObj1.GetElementsByTagName("p")(0).GetElementsByTagName("em")(0)
    
    konnte das leider nicht testen aber in meinen Testroutinen habe ich das Problem das ich dann Laufzeitfehler bekomme.

    In c++ kann ich das einfach auf das entsprechende Objekt casten, wenn dieses die memberVariablen und Einsprungadressen für die entsprechende Funktionen besitzt.

    Das ist glaube ich auch eines meiner Probleme in VBA im Vergleich zu anderen Programmiersprachen.
    zu meinem Background: die letzten Jahre habe ich mit C(µController)/C#(Windows-Tools) zu tun gehabt, davor mit c++ und Java aber Webprogramming ist für mich völliges Neuland habe mich da mit einem Javascript-kurs bei Linkedin vor kurzem etwas eingearbeitet.
    ' "" = inhalt steht hier als Platzhalter für eine fiktive Klasse, Struktur,...
    
    dim rootObj as objekt
    dim derivedObj as 
    
    set derviedObjekt=
    
    
    mit diesen Fragment kann ich doch in VBA explitizes Casten nachbilden . Ist das richtig?

    für mein letztes Problem mit der besseren Content-Filterung läde ich nach dem Urlaub mein excel-file hoch.


    Beiträge aus dem Excel-Forum zum Thema "Unterobj. von html getElements..() weiter parsen"