且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

VBA Web抓取-更改日历的日期

更新时间:2023-10-19 23:47:34

请尝试以下操作.实际上,它单击年份和月份的下拉菜单,然后通过itemindex属性值选择适当的项目.

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用于等待某些元素变为可单击状态,以及用于出现下拉菜单的定时循环.这会根据事件可采取行动的时间来计时,以产生预期的结果.

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