I would like to write a vba Programm which downloads automaticaly historical stock data from a web-page. Selecting the data and click on the download button is already worki
Try the following. It essentially clicks the dropdowns for year and month and selects the appropriate item by itemindex
attribute value.
In the case of year, depending on what is currently displayed, the desired year may not be in the drop down. The code uses a fixed number of clicks of the <> buttons to determine if the desired year can be found. This number could be set as a constant, at the top of the code, and altered there if required.
To select day, the collection of days is looped, and if the desired day value found then it is selected.
Javascript is used to wait for certain elements to become clickable, as well as a timed loop for a drop down to appear. This times events according to when they are actionable in order to yield desired results.
Option Explicit
Public Sub MakeChanges()
'VBE > Tools > References > Selenium Type Library
'Download: https://github.com/florentbr/SeleniumBasic/releases/tag/v2.0.9.0
Const url = "https://www.dukascopy.com/swiss/english/marketwatch/historical/"
Const MAX_WAIT_SEC As Long = 10
Const JS_WAIT_CLICKABLE = _
"var target = this, endtime = Date.now() + arguments[0];" & _
"(function check_clickable() {" & _
" var r = target.getBoundingClientRect(), x = r.left+r.width/2, y = r.top+r.height/2;" & _
" for (var e = document.elementFromPoint(x , y); e; e = e.parentElement)" & _
" if (e === target){ callback(target); return; }" & _
" if (Date.now() > endtime) { callback(target); return; }" & _
" setTimeout(check_clickable, 60);" & _
"})();" 'by @florentbr
Dim d As WebDriver, t As Date
Dim myYear As String, myMonth As String, myDay As String
Set d = New ChromeDriver
myYear = "2017"
myMonth = "January"
myDay = "1"
With d
.start "Chrome"
.get url
.SwitchToFrame .FindElementByCss("script + iframe") '<==switch to frame
'You should add tests for acceptable values e.g. January-December for MonthName, day as appropriate for month
Dim monthIndex As Long, yearIndex As Long, item As Object, dropDown As Object
monthIndex = Month(DateValue("01 " & myMonth & " 2019")) - 1 '<== get month number from name and -1 to get value to use in attribute selector
t = Timer
Do '<== timed loop for month dropdown to be present
On Error Resume Next
Set dropDown = .FindElementByCss(".d-wh-vg-xh span span")
On Error GoTo 0
If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop While dropDown Is Nothing
If dropDown Is Nothing Then Exit Sub
With dropDown '<== wait for drop down to be clickable
.ExecuteAsyncScript(JS_WAIT_CLICKABLE, 3000) _
.Click
End With
With .FindElementByCss(".d-Ch-fi-mi")
.ExecuteAsyncScript(JS_WAIT_CLICKABLE, 3000) _
.Click '<== display month dropdown
End With
.FindElementByCss(".d-Ch-fi-u [itemindex='" & monthIndex & "']").Click '<select month by index
Dim yearIndices As Object, i As Long, j As Long, currentYear As String, z As Long, dayFound As Boolean
currentYear = .FindElementByCss(".d-Ch-fi-ni").Text '<= find currently displayed year
Set yearIndices = CreateObject("Scripting.Dictionary")
For i = CLng(currentYear) - 5 To CLng(currentYear) + 5 '<== gather range of year options in dropdown into
'dictionary where key is year and value is the value required to select in attribute selector
yearIndices(CStr(i)) = CStr(j)
j = j + 1
Next
If yearIndices.Exists(myYear) Then '<check dictionary to see if year desired present
yearIndex = yearIndices(myYear)
.FindElementByCss(".d-Ch-fi-ni").Click '<== display year dropdown
.FindElementByCss("div:nth-child(11) [itemindex='" & yearIndex & "']").Click '<==select year
Else '<== year not present so loop clicking either year add or year subtract to see if desired year does become present
Dim adjustButton As Object
Set adjustButton = IIf(CLng(currentYear) > CLng(myYear),.FindElementByCss("d-Ch-fi-previousYear"), .FindElementByCss("d-Ch-fi-nextYear"))
Do
adjustButton.Click
If z > 15 Then Exit Sub
z = z + 1
Loop Until .FindElementByCss(".d-Ch-fi-ni").Text = myYear
End If
Dim daysList As Object
Set daysList = .FindElementsByCss("div:nth-child(11) td") '<==gather all the days
For Each item In daysList '<==loop days in month until required one found
If item.Text = myDay Then
item.Click
Exit For
End If
Next
Stop
.Quit
End With
End Sub