Data uit HTML template halen met VBA (Strava)

Status
Niet open voor verdere reacties.

Joete

Gebruiker
Lid geworden
19 sep 2008
Berichten
87
Hey redders in (luxe) nood!

Ik probeer d.m.v. een VBA script data uit mijn Strava account te halen.
Het lukt mij om via VBA:
  • In te loggen
  • Mijn accountnaam er uit te halen
  • Mijn profielfoto er af te plukken
  • Te wisselen tussen de verschillende Strava-pagina's
  • Aan het eind weer uit te loggen
  • Tussendoor de pagina met activiteiten te bereiken
  • En daar de juiste tabel te vinden
Waar ik uiteindelijk vastloop is binnen de tabel de juiste regel te vinden...

Het probleem heb ik (denk ik) al geconstateerd en dat is dat wanneer ik de paginabron bekijk, ik dit krijg:
HTML:
<table class='table table-striped table-padded activities table-sortable' id='search-results'>
<thead>...</thead>
<tbody></tbody>
</table>
</div>
<nav class='text-center'>
<div class='simple pagination'></div>
</nav>
</div>
<script id='loading-template' type='text/template'>
<tr>
<td class='updating-col' colspan='8'>
<div class='mt-lg mb-lg'><div class='spinner sm vcentered' style=''>
      <div class='graphic'></div>
      <span class='status'>Bezig met laden...</span>
    </div></div>
</td>
</tr>
</script>
<script id='activity-template' type='text/template'>
<td class='view-col col-type' data-field-name='type'>{{ display_type }}</td>
<td class='view-col col-date'>{{ start_date }}</td>
<td class='view-col col-title'>
<a data-field-name='name' href='{{ activity_url }}'>{{ name }}</a>
<div class='icon-private app-icon icon-xs icon-dark' data-field-name='private' style='display: none;' title='Privé'></div>
<% if (flagged) { %>
<span class='icon flag' title='Gemeld'>
⚑
</span>
<% } %>
</td>
Of te wel: de juiste tabel wordt in de basiscode wel aangemaakt, maar niet gevuld, dit gebeurd vanuit de template, en gebeurd dus na het laden van de pagina zelf.

Wanneer ik echter op inspecteren klik in de browser, krijg ik dit:
HTML:
<table class="table table-striped table-padded activities table-sortable" id="search-results">
<thead>...</thead>
<tbody>

<tr class="training-activity-row">
<td class="view-col col-type" data-field-name="type" style="display: table-cell;">Rit</td>
<td class="view-col col-date" style="display: table-cell;">zo 19-8-2018</td>
<td class="view-col col-title" style="display: table-cell;">
<a data-field-name="name" href="https://www.strava.com/activities/1782743091">Rondje Ark van Noach</a>
<div class="icon-private app-icon icon-xs icon-dark" data-field-name="private" style="display: none;" title="Privé"></div>

</td>
<td class="view-col col-time" data-field-name="time" style="display: table-cell;">1:23:46</td>
<td class="view-col col-dist" style="display: table-cell;">
42,82
<abbr class="unit" title="kilometer">
km
</abbr>
</td>
<td class="view-col col-elev" style="display: table-cell;">
51
<abbr class="unit" title="meter">m</abbr>
</td>
Dus wel de gehele website zoals deze uiteindelijk weergegeven wordt.

Nu wil ik dus vanuit VBA de activiteitendata ophalen. Iemand een idee hoe ik dat kan doen? Als ik maar eenmaal in de goede regels kan komen, kom ik daarna wel weer verder.

Dit is wat ik tot nu toe heb:
Code:
Sub ImportStravaData()
    On Error GoTo ErrorHandling
    
    ' Controle op account
    If Sheets("StravaData").Range("B1").Value <> "" Then
        ' Inloggen
        Call Inloggen
        
        ' Tellen hoeveel activiteiten al in het overzicht staan
        Dim Activiteiten As Integer
        Dim Tabel As ListObject
        Set Tabel = Sheets("StravaData").ListObjects("StravaData")
        Activiteiten = Tabel.ListRows.Count
        
        ' Variabelen maken
        Dim Link As String
        Dim Datum As String
        Dim Jaar As Integer
        Dim Maand As Integer
        Dim Dag As Integer
        Dim Sport As String
        Dim Omschrijving As String
        Dim Tijd As String
        Dim Afstand As String
        Dim GemTempo As String
        Dim MaxTempo As String
        Dim GemSnelheid As String
        Dim MaxSnelheid As String
        Dim GemHart As String
        Dim MaxHart As String
        Dim Hoogte As String
        Dim ID As String
        Dim TBL As Object
        Dim TBLs As Object
        Dim TR As Object
        Dim TD As Object
        Dim A As Object
        Dim RijTeller As Integer
        RijTeller = 4
        
        ' Data ophalen
        If Activiteiten = 0 Then
            ' Geen activiteiten in overzicht, dus alles downloaden
            With IE
                .navigate "https://www.strava.com/athlete/training"
                Do While .readyState <> READYSTATE_COMPLETE
                    DoEvents
                Loop
                Application.Wait (Now() + TimeValue("00:00:05"))

' ##### VANAF HIER WORDT HET INTERESSANT #####                
MsgBox "Site geladen"
                ' Data verzamelen
                Set TBLs = .document.getElementsByTagName("table")
                For Each TBL In TBLs
MsgBox "Tabellen gevonden"
                    ' Juiste tabel zoeken
                    If TBL.ID = "search-results" Then
MsgBox "Juiste tabel gevonden"
                        Set TR = TBL.getElementsByTagName("tr")
                        For Each TBLrij In TR
                            ' Juiste tabel rij pakken
                            If TR.className = "training-activity-row" Then
MsgBox "Juiste rij gevonden"
                                For Each TD In TBLrij.getElementsByTagName("td")
                                    ' Data uit cellen trekken
                                    If TD.className = "view-col col-date" Then
                                        Datum = TD.innerHTML
                                        Jaar = Right(TD.innerHTML, 4)
                                        Maand = Replace(Mid(TD.innerHTML, InStr(1, TD.innerHTML, "-") + 1, 2), "-", "")
                                        Dag = Replace(Mid(TD.innerHTML, InStr(1, TD.innerHTML, " ") + 1, 2), "-", "")
                                        Sheets("StravaData").Range("A" & RijTeller).Value = Datum
                                        Sheets("StravaData").Range("B" & RijTeller).Value = Jaar
                                        Sheets("StravaData").Range("C" & RijTeller).Value = Maand
                                        Sheets("StravaData").Range("D" & RijTeller).Value = Dag
                                    ElseIf TD.className = "view-col col-type" Then
                                        Sport = TD.innerHTML
                                        Sheets("StravaData").Range("E" & RijTeller).Value = Sport
                                    ElseIf TD.className = "view-col col-title" Then
                                        Set A = TD.getElementsByTagName("a")
                                        Link = A.href
                                        Omschrijving = A.innerHTML
                                        ID = Right(Link, Len(Link) - InStrRev(Link, "/"))
                                        Sheets("StravaData").Range("F" & RijTeller).Value = Omschrijving
                                        Sheets("StravaData").Range("P" & RijTeller).Value = ID
                                    ElseIf TD.className = "view-col col-time" Then
                                        Tijd = TD.innerHTML
                                        Sheets("StravaData").Range("G" & RijTeller).Value = Tijd
                                    ElseIf TD.className = "view-col col-dist" Then
                                        Afstand = TD.innerHTML
                                        Sheets("StravaData").Range("H" & RijTeller).Value = Afstand
                                    ElseIf TD.className = "view-col col-elev" Then
                                        Hoogte = TD.innerHTML
                                        Sheets("StravaData").Range("O" & RijTeller).Value = Hoogte
                                    End If
                                Next
                            End If
                            RijTeller = RijTeller + 1
                        Next
                    End If
                Next
            
            
            
            
            
            End With
        Else
            ' Wel activiteiten in overzicht, dus alleen nieuwe downloaden
            With IE
                .navigate "https://www.strava.com/athlete/training"
                Do While .readyState <> READYSTATE_COMPLETE
                    DoEvents
                Loop
                Application.Wait (Now() + TimeValue("00:00:05"))
                
                ' Data verzamelen
          
            End With
        End If
        Set Tabel = Nothing
        
        ' Uitloggen
        Call Uitloggen
    Else
        MsgBox "Geen account ingesteld. Reset eerst het account.", vbOKOnly + vbCritical, "Error"
    End If
    Exit Sub
    
ErrorHandling:
    ' Uitloggen
    Call Uitloggen

End Sub
De MSGBOXes die vooraan staan gebruik ik puur om te controleren hoever ik kom met mijn code. De tabel wordt dus wel gevonden, maar daarna niet meer...

Hoop dat iemand kan helpen/meedenken.

Alvast bedankt!
 
De code ziet er goed uit
Ik gebruik deze wachtlus met een extra busy
Code:
Do
            DoEvents
        Loop While .Busy Or .readyState <> 4
        Application.Wait DateAdd("s", 5, Now)
Je gebruikt internet explorer voor het scrapen van een site met javascript, dat is goed.
Het lijkt dat je een nog langere lus moet inbouwen zodat de javascript code je tabel kan genereren.

Laad de pagina nog eens en kijk in het inspectievenster van je browser als er niet meer parameters worden meegezonden bij de aanvraag.

p.s.
Een stap verder en beter is het werken met een API, zie https://developers.strava.com/
Een API stuurt je alleen de data en geen html-plaatjes-reclame-lettertypes etc.
 
Laatst bewerkt:
Een API kan ik zeker aanraden: Stel je voor dat de website haar lay-out wordt aangepast, dan zal jouw script niet meer (netjes) kunnen 'scrapen'.
Een API ontsluit de content in een specifiek formaat, zoals XML en JSON en soms zelf in plain-text.
 
Ik heb even gekeken naar de api, deze gebruikt oauth2 authentificatie.
Dat is volgens mij best wel moeilijk met VBA.
 
Moeilijk misschien wel, maar niet onmogelijk.
Zet eerst de stappen op een rijtje om de data op te kunnen halen.
 
Extra wachttijd is het proberen waard, ga ik eens proberen.

De tip van de API is ook zeker een goede, nog niet bij nagedacht dat de website ooit wel eens aangepast kan worden.
Ik heb alleen nog nooit met VBA en API's gewerkt. HTML was voor mij ook nieuw trouwens en daar ben ik best wel een beetje uit gekomen denk ik zelf...
Iemand een voorbeeldje van hoe dit gaat in VBA?
 
Je hoeft de OAUTH niet telkens door met VBA, dat is het hele punt :)

Je moet eenmalig een client / token en secret aanmaken. Daarmee creëer je op de website een access token. Dat is gewoon een lange unieke reeks tekens. Het is deze reeks die je vanaf dan nodig hebt in je VBA om je informatie op te halen. Helaas kan ik op de dev website geen manier vinden om individuele tokens aan te maken voor puur persoonlijk gebruik. Iets wat google en veel andere bedrijven wel toelaten om makkelijk zelf de API te gebruiken. Maar dat is iets dat je misschien kunt opnemen met de makers.
 
Extra wachttijd is het proberen waard, ga ik eens proberen.
De wachttijd is elk moment van de dag anders, ook per lokatie anders en het is zelfs per pc anders. Dit komt door de beinvloeding van de snelheid door deze 3 factoren. Als je het gaat proberen met timing zal het soms wel en soms niet werken, het resultaat is niet betrouwbaar.

De bron geeft de code zoals deze is geschreven in de html.
Met inspecteren zie je hoe de code is geworden nadat de DOM klaar is met renderen en alle Javascript is verwerkt.

Dus eigenlijk is de API de enige juiste manier.
 
Laatst bewerkt:
Met extra wachttijd, bedoel ik dan ook 5 tot 10 of zelfs 20 seconden (langer dat mensen geduld hebben)
Aangezien je maar eens per dag de gegevens binnenhaalt is dat het wachten waard
 
Deze 3 voorbeelden vond ik her en der. Ze kijken naar de pagina als die compleet is gerendeerd.
Code:
While Ie.**document**.readystate="complete"
  DoEvents
Wend

Do Until IE.document.getelementbyid("search-results").Lenght>0
  DoEvents
Loop

Set objIE = CreateObject("InternetExplorer.Application")
objIE.Visible = True
objIE.Navigate "https://www.yahoo.com/"
Do While objIE.ReadyState <> 4
  WScript.Sleep 10
Loop
Ter info, ik ben geen VBA'er maar een website bouwer.
 
Laatst bewerkt:
Dit is een voorbeeld bestandje van hoe je met de Strava API kan importeren (data is echt, maar geanonimiseerd).
  1. Druk op (linker)ALT+F11
  2. Vervang achter strAccess_Token, <anoniem> door je access_token
  3. Sla het bestand op
  4. Ga naar Blad1
  5. Klik op het kleine knopje linksboven
Code:
[SIZE=1][FONT=Courier New]Option Explicit

Public Sub ImporterenStravaAPIActivities()

    Dim objActivitie1 As Object
    Dim objActivitie2 As Object
    Dim objActivities As Object
    Dim lngRow As Long
    Dim lngColumn As Long
    Dim lngColumns As Long
    Dim strAccess_Token As String
    Dim strResponseText As String
    Dim strKey As String
    Dim strURL As String
    Dim vntValue As Variant

    Cells(1, 1).CurrentRegion.Offset(2, 0).Clear
    
    strAccess_Token = "<anoniem>"
    strURL = "https://www.strava.com/api/v3/athlete/activities?access_token=" & strAccess_Token & "&per_page=200" 'am_2018
    lngRow = 2
    lngColumns = Cells(1, Columns.Count).End(xlToLeft).Column
    With CreateObject("msxml2.xmlhttp")
        .Open "GET", strURL, False
        .send
        strResponseText = .ResponseText
        With CreateObject("ScriptControl")
            .Language = "JScript"
            .AddCode "function getProperty(jsonObj, propertyName) { return jsonObj[propertyName]; } "
            Set objActivities = .Eval("(" + strResponseText + ")")
            For Each objActivitie1 In objActivities
                lngRow = lngRow + 1
                Cells(lngRow, 1).Value = lngRow - 2
                For lngColumn = 2 To lngColumns
                    strKey = Cells(1, lngColumn).Value
                    vntValue = .Run("getProperty", objActivitie1, strKey)
                    If vntValue = "[object Object]" Then
                        Set objActivitie2 = .Run("getProperty", objActivitie1, strKey)
                        strKey = Cells(2, lngColumn).Value
                        vntValue = .Run("getProperty", objActivitie2, strKey)
                    End If
                    Cells(lngRow, lngColumn).Value = vntValue
                Next
            Next
        End With
    End With

    Cells(1, 1).CurrentRegion.Columns.AutoFit

End Sub[/FONT]
[/SIZE]
 

Bijlagen

  • helpmij joete strava.xlsm
    89,6 KB · Weergaven: 31
Laatst bewerkt:
Erg netjes. misschien handig om de code apart eronder te posten, dat maakt het vinden van dit topic voor anderen in de toekomst wellicht makkelijker.
 
Dit is een voorbeeld bestandje van hoe je met de Strava API kan importeren (data is echt, maar geanonimiseerd).
  1. Druk op (linker)ALT+F11
  2. Vervang achter strAccess_Token, <anoniem> door je access_token
  3. Sla het bestand op
  4. Ga naar Blad1
  5. Klik op het kleine knopje linksboven

Geniaal! Bedankt!
Maar hoe kom ik aan die access_token? Ben al wat aan het zoeken en proberen geweest, maar kom er nog niet achter...
 
Top! Is gelukt!
Wel wat omslachtig om het zo te moeten doen, wilde het automatiseren zodat anderen er ook gebruik van kunnen maken, maar moet nog eens even kijken of ik dat ga doen, of het handmatig laat doen...

Moet zeggen dat de code wel traag is, 200 activiteiten doet ie best lang over om te laden (zou met mijn idee van code ook zo zijn hoor), maar ik kan hier zeker mee verder!

Thanks!
 
Bij mij ongeveer 10seconden.
 
Bij mij nu inderdaad ook 15 seconden, zal aan mijn WiFi thuis gelegen hebben denk ik...

Zo'n API is toch wel interessant! Ben me er nu een beetje meer in aan het verdiepen, maar een leuke manier van data vergaren, al ben je wel afhankelijk van wat ze beschikbaar stellen...
 
Ik denk dat de site van strava en de api van strava de gegevens uit dezelfde database halen.
Alleen zal de site de data opleuken met grafiekjes en landkaartjes.
Je hebt nu een totaaloverzicht met activities en globale gegevens.
Met Get Activity kan je een activiteit uitlichten met gedetailleerde informatie.
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan