Веб-сайт не распознает мои входы [как запустить IE dom event вручную из VBA]

Я хотел бы купить на gdax автоматически. Но мои данные в окне "Сумма" не распознаются. Я вижу, что на маленьком поле, которое говорит: Total (LTC) ≈ 0.00000000

Мой код:

Sub test()

    Dim ObjIE As New InternetExplorer
    Dim Ohtml As HTMLDocument
    Dim HTMLtags As IHTMLElementCollection
    Dim HTMLtag As IHTMLElement
    Dim HTMLobjekt As IHTMLElement
    Dim item_limit As Object
    Dim y As Integer

    With ObjIE
        .Visible = True
        .navigate "https://www.gdax.com/trade/LTC-EUR"
        Do Until .readyState = READYSTATE_COMPLETE: Loop
        Set Ohtml = .document
    End With

    'Amount
    Do
        Set HTMLtags = Ohtml.getElementsByClassName("OrderForm_input-box_XkGmi")
        DoEvents
    Loop While HTMLtags.Length = 0
    For Each HTMLtag In HTMLtags
        If HTMLtag.Children(1).innerText = "EUR" Then
            Set HTMLobjekt = HTMLtag.Children(0)
            HTMLobjekt.Value = 100      ' this is the part that i excanged
        End If
    Next HTMLtag

    'get the Total(LTC) to cross check
    Do
        Set HTMLtags = Ohtml.getElementsByClassName("OrderForm_total_6EL8d")
        DoEvents
    Loop While HTMLtags.Length = 0
    For Each HTMLtag In HTMLtags
        Debug.Print HTMLtag.innerText & "Total(LTC)"
    Next HTMLtag

End Sub

Это то, что веб-сайт говорит, когда код сделан:

Picture.this is how it is

и так оно и должно выглядеть, и выглядит, когда я набираю номер вручную:

Picture.this is how it should be

Я также обмениваю отмеченную часть такими вещами, как:

HTMLobjekt.innerText = 100

или же

HTMLobjekt.innerText = "100"

или же

HTMLobjekt.Value = "100"

но ничего не получилось.

Ответы

Ответ 1

Вы должны убедиться, что страница полностью загружена, прежде чем предпринимать какие-либо инициативы. Я проверил наличие одного такого класса, созданного динамически OrderBookPanel_text_33dbp. Затем я сделал все, что вы пытались сделать. Наконец, вам нужно Focus цель, прежде чем поместить эту сумму в местозаполнитель. Применяя все вышесказанное, ваш сценарий должен выглядеть следующим образом:

Sub Get_Value()
    Dim HTML As HTMLDocument, tags As Object
    Dim tag As Object, ival As Object

    With CreateObject("InternetExplorer.Application")
        .Visible = True
        .navigate "https://www.gdax.com/trade/LTC-EUR"
        While .Busy = True Or .readyState < 4: DoEvents: Wend
        Set HTML = .document

        Do: Set tags = HTML.querySelector("[class^='OrderBookPanel_text_']"): DoEvents: Loop While tags Is Nothing
        Do: Set tag = HTML.querySelector("input[name='amount']"): DoEvents: Loop While tag Is Nothing
        tag.Focus
        tag.innerText = 100

        Set ival = HTML.querySelector("[class^='OrderForm_total_']")
        [A1] = ival.innerText
        .Quit
    End With
End Sub

Выход на данный момент:

0.79139546

Ответ 2

Не полный ответ, просто направление. Откройте веб-страницу https://www.gdax.com/trade/LTC-EUR в браузере (например, Chrome). Нажмите, чтобы открыть контекстное меню целевого элемента (шаг 1 на скриншоте ниже), нажмите "Осмотреть" (2), от открытых инструментов разработчика справа вы увидите целевой элемент (3) и что есть куча прослушивателей событий для целевого объекта (4). Один из обработчиков input на уровне узла document. Фактически сумма обновляется этим обработчиком событий, когда событие помечено от узла <input>. Вы можете легко проверить это, удалив все остальные обработчики событий (нажмите на их кнопки Remove). Но если вы удалите, в частности, этот обработчик input (5), то обновления не будет (пока вы не перезагрузите веб-страницу).

F12

Таким образом, чтобы имитировать активность пользователя, вам необходимо создать такой объект события input и отправить его на целевой узел <input>. Примечание. <input> узел и управление input - это совершенно разные вещи, имеющие одно и то же имя.

Выполнение обработчика событий трассировки в IE может выполняться в отладчике. Откройте инструменты разработчика IE (нажмите F12), перейдите на вкладку "Отладчик" (шаг 1 на снимке ниже), вкладку "Точки останова" (2), нажмите "Добавить точку останова" (3), выберите событие ввода (4). Теперь любое событие ввода приостанавливает выполнение кода и открывает окно отладчика (5).

debugger

Если вы попытаетесь ввести текстовое поле суммы на веб-странице, отладчик покажет код функции обработчика событий:

handler

Вы можете возобновить выполнение (F5) или сделать шаг в/над/из. Чтобы просмотреть объект события, переданный функции, введите arguments в консоли, после ввода вручную вы получите вывод для стандартного вызова обработчика событий:

called by typing

Я проверил приведенный ниже тестовый код, чтобы инициировать это событие из VBA:

Sub Test()

    Dim oEvt As Object

    With CreateObject("InternetExplorer.Application")
        .Visible = True
        .Navigate "https://www.gdax.com/trade/LTC-EUR"
        Do While .ReadyState < 4 Or .Busy
            DoEvents
        Loop
        With .Document
            Do While IsNull(.querySelector("div.OrderForm_input-box_XkGmi input"))
                DoEvents
            Loop
            Set oEvt = .createEvent("Event")
            oEvt.initEvent "input", True, False, .parentWindow, 1
            With .querySelector("div.OrderForm_input-box_XkGmi input")
                .Focus
                .Value = "1000"
                .dispatchEvent oEvt
                Stop
            End With
        End With
    End With

End Sub

В результате происходят абсолютно те же действия, что и при вводе суммы вручную. Он работает без ошибок. Но Total (LTC) не обновляется. Debugger также приостанавливает выполнение, а выход для объекта события выглядит следующим образом:

called by VBA

Единственное различие заключается в том, что свойство isTrusted имеет значение True для стандартного вызова и False для вызова через VBA. Наверное, поэтому обновление пропускается где-то в коде обработчика. Я попытался проследить весь процесс выполнения кода до завершения обработчика событий, но, похоже, это огромная обратная инженерная работа, на которую я не могу выделить время на данный момент.

В настоящее время я застрял в своих исследованиях в этот момент.

Ответ 3

Извините, но не смог поработать, но подтолкнул проблему.

Мне удалось выяснить правильный синтаксис для создания и повышения события в более высоких версиях Internet Explorer (благодаря этому SO-ответу). Мне удалось поднять входное событие для поля ввода, которое следовало за omegastripes отличным результатом.

Таким образом, он работает для локального файла, который я даю ниже, который имеет ту же самую существенную структуру реальной страницы.

Но когда я запускаю код на реальной странице, он просто не работает, и я не знаю, почему.

По возможности я загружаю javascript в IE для его выполнения, чтобы имитировать проблемы с мостом совместимости VBA/COM/MSHTML. Я делаю это, вызывая window.execScript передавая javascript как строку.

Я надеюсь, что кто-то сможет забрать этот мяч и запустить его по линии ворот. Для меня я дошел до конца.

Вот локальный файл html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <input id="Button1" type="button" value="Programmatically write textbox value" onclick="TestAlert()" />

    <form class="OrderForm_form_25r0u">
        <ul class="OrderForm_trade-type_2QyK4">
            <li class="OrderForm_trade-type-tab_uWGMp OrderForm_active_Di-9p">MARKET</li>
            <li class="OrderForm_trade-type-tab_uWGMp">LIMIT</li>
            <li class="OrderForm_trade-type-tab_uWGMp">STOP</li>
        </ul>
        <ul class="OrderForm_toggle_120Ka">
            <li class="OrderForm_toggle-tab_bZZnC OrderForm_buy_38n5g OrderForm_active_Di-9p">BUY</li>
            <li class="OrderForm_toggle-tab_bZZnC OrderForm_sell_3vYRQ">SELL</li>
        </ul>
        <div class="market-order">
            <div class="OrderForm_section_2Znad">
                <div class="OrderForm_section-header_fwFDB">Amount</div>
                <div class="OrderForm_input-box_XkGmi">
                    <input type="number" step="0.01" min="0" name="amount" placeholder="0.00" value="" autocomplete="off" oninput="myOnInputHandler()">
                    <span>EUR</span>
                </div>
            </div>
        </div>
        <div class="OrderForm_order-total_3Mkdz">
            <div>
                <b>Total</b>
                <span>(LTC)</span>
                <b>≈</b>
            </div>
            <div class="OrderForm_total_6EL8d" >0.00000000</div>
        </div>
    </form>

    <script language="javascript">
        function myOnInputHandler() {
            print_call_stack();
            alert('you input something');
        }

        function print_call_stack() { console.trace(); }

        function print_call_stack2() {
            var stack = new Error().stack;
            console.log("PRINTING CALL STACK");
            console.log(stack);
        }


        function TestAlert() { setInputBox(document); }

        function setInputBox() {
            try {
                var inputBox = document.querySelector('div.OrderForm_input-box_XkGmi input'); inputBox.value = 100; if (document.createEvent) { var event2 = document.createEvent("HTMLEvents"); event2.initEvent("input", true, true); event2.eventName = "input"; inputBox.dispatchEvent(event2); }

                return ({ success: true });
            }
            catch (ex) {
                return ({ exception: ex, myMsg: '#error in setInputBox!' });
            }
        }

    </script>
</body>
</html>

Вот VBA

Option Explicit

'* Tools - References
'*      MSHTML      Microsoft HTML Object Library                   C:\Windows\SysWOW64\mshtml.tlb
'*      SHDocVw     Microsoft Internet Controls                     C:\Windows\SysWOW64\ieframe.dll
'*      Shell32     Microsoft Shell Controls And Automation         C:\Windows\SysWOW64\shell32.dll


Private Function ReacquireInternetExplorer(ByVal sMatch As String) As Object
    Dim oShell As Shell32.Shell: Set oShell = New Shell32.Shell
    Dim wins As Object: Set wins = oShell.Windows
    Dim winLoop As Variant
    For Each winLoop In oShell.Windows
        If "C:\Program Files (x86)\Internet Explorer\IEXPLORE.EXE" = winLoop.FullName Then

            Dim sFile2 As String
            sFile2 = "file:///" & VBA.Replace(sMatch, "\", "/")
            If StrComp(sFile2, winLoop.LocationURL, vbTextCompare) = 0 Then
                Set ReacquireInternetExplorer = winLoop.Application
                GoTo SingleExit
            End If
        End If
    Next
SingleExit:
End Function

Sub test()

    Dim objIE As InternetExplorer
    Set objIE = New InternetExplorer
    Dim oHtml As HTMLDocument
    Dim HTMLtags As IHTMLElementCollection


    Dim sUrl As String
    sUrl = "C:\Users\Simon\source\repos\WebApplication2\WebApplication2\HtmlPage1.html"
    'sUrl = "http://localhost:50367/HtmlPage1.html"
    sUrl = "https://www.gdax.com/trade/LTC-EUR"

    objIE.Visible = True
    objIE.Navigate sUrl

    If StrComp(Left(sUrl, 3), "C:\") = 0 Then
        Stop '* give chance to clear the activex warning box for the local file
        Set objIE = ReacquireInternetExplorer(sUrl)
    End If
    Do Until objIE.readyState = READYSTATE_COMPLETE: DoEvents: Loop
    Set oHtml = objIE.Document

    Do
        '* wait for the input box to be ready
        Set HTMLtags = oHtml.getElementsByClassName("OrderForm_input-box_XkGmi")
        DoEvents
    Loop While HTMLtags.Length = 0

    Dim objWindow As MSHTML.HTMLWindow2
    Set objWindow = objIE.Document.parentWindow


    '* next line would be really useful if it worked because it prints the stack trace in the console window
    '* and we would be able to see the needle in the haystack where te order total gets updated
    '* works on local file but not on https://www.gdax.com/trade/LTC-EUR  **sigh**
    objWindow.execScript "var divTotal = document.querySelector('div.OrderForm_total_6EL8d'); divTotal.onchange = function() { console.trace(); }"


    '* next line sets the input box and raises an event, works on local file but not on GDAX
    objWindow.execScript "var inputBox = document.querySelector('div.OrderForm_input-box_XkGmi input'); inputBox.value = 100; if (document.createEvent) { var event2 = document.createEvent('HTMLEvents'); event2.initEvent('input', false, false); event2.eventName = 'input'; inputBox.dispatchEvent(event2);  }"


    'get the Total(LTC) to cross check
    Do
        '* wait for the order total div to be ready
        Set HTMLtags = oHtml.getElementsByClassName("OrderForm_total_6EL8d")
        DoEvents
    Loop While HTMLtags.Length = 0

    Dim divTotal As HTMLDivElement
    Set divTotal = oHtml.querySelector("div.OrderForm_total_6EL8d")
    Debug.Print divTotal.innerText & " Total(LTC)"

    Stop

End Sub