web-dev-qa-db-fra.com

Le site Web ne reconnaît pas mes entrées [comment tirer IE événement dom manuellement à partir de VBA]

Je voudrais acheter sur gdax automatiquement. Mais mes entrées dans la fenêtre Montant ne sont pas reconnues. Je peux voir que sur le petit champ, qui dit: Total (LTC) ≈ 0.00000000

Mon code:

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

Voici ce que dit le site lorsque le code est terminé:

 Picture.this is how it is

et voici à quoi cela devrait ressembler, et lorsque je tape le nombre manuellement:

 Picture.this is how it should be

J'échange aussi la partie marquée avec des choses comme:

HTMLobjekt.innerText = 100

ou

HTMLobjekt.innerText = "100"

ou

HTMLobjekt.Value = "100"

mais rien n'a fonctionné.

15
Nils

Vous devez vous assurer que la page est entièrement chargée avant de prendre une initiative. J'ai vérifié la disponibilité d'une telle classe générée dynamiquement OrderBookPanel_text_33dbp. Puis j'ai fait le reste ce que vous avez essayé de faire. Enfin, vous devez Focus la cible avant de mettre ce montant dans l'espace réservé. En appliquant tout ce qui précède, votre script devrait ressembler davantage à celui-ci:

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

Sortie en ce moment:

0.79139546
9
SIM

Pas une réponse complète, juste une direction. Ouvrez la page Web https://www.gdax.com/trade/LTC-EUR dans un navigateur (p. Ex. Chrome). Cliquez pour ouvrir le menu contextuel sur l'élément cible (étape 1 de la capture d'écran ci-dessous), cliquez sur Inspecter (2), à partir des outils de développement ouverts à droite peut voir l'élément cible (3), et qu'il existe un groupe d'écouteurs d'événements attachés à l'objet cible (4). Un des gestionnaires est input au niveau du noeud document. En réalité, le montant est mis à jour par ce gestionnaire d'événements, lorsque l'événement est diffusé à partir du noeud <input>. Vous pouvez facilement vérifier cela en supprimant tous les autres gestionnaires d'événements (cliquez sur leur Remove boutons). Mais si vous supprimez particulièrement ce gestionnaire input (5), il n'y aura aucune mise à jour (jusqu'à ce que vous rechargiez la page Web).

F12

Par conséquent, pour imiter l'activité de l'utilisateur, vous devez créer cet objet événement input et le répartir vers le noeud cible <input>. Remarque Le noeud <input> et l'événement de contrôle input sont des choses complètement différentes ayant simplement le même nom.

L'exécution du gestionnaire d'événements de suivi dans IE peut être effectuée dans le débogueur. Ouvrez les outils de développement IE (appuyez sur F12), allez à l'onglet du débogueur (étape 1 de la capture d'écran ci-dessous), Points d'arrêt onglet (2), cliquez sur Ajouter un point d'arrêt d'événement (3), choisissez saisissez événement (4). Désormais, tout événement d'entrée mettra en pause l'exécution du code et ouvrira la fenêtre du débogueur (5).

debugger

Si vous essayez de saisir la zone de texte montant sur la page Web, le débogueur affichera le code de la fonction de gestionnaire d'événements:

handler

Vous pouvez reprendre l'exécution (F5), ou faire un pas en avant/arrière/dehors. Pour voir l'objet d'événement transmis à la fonction, saisissez arguments dans la console. Un appel est généré pour le gestionnaire d'événement standard après la saisie manuelle:

called by typing

J'ai testé le code de test ci-dessous pour déclencher cet événement à partir de 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

En conséquence, les mêmes actions que si vous saisissez le montant manuellement sont exécutées. Cela fonctionne sans aucune erreur. Mais Total (LTC) n'est pas mis à jour. Le débogueur interrompt également l'exécution et la sortie de l'objet événement est la suivante:

called by VBA

La seule différence est que la propriété isTrusted est True pour l'appel standard et False pour l'appel via VBA. J'imagine que c'est pourquoi la mise à jour est ignorée quelque part dans le code du gestionnaire. J'ai essayé de retracer l'exécution du code complet jusqu'à l'achèvement du gestionnaire d'événements, mais il semble que ce soit un énorme travail d'ingénierie inverse, pour lequel je ne peux pas consacrer de temps pour le moment.

Actuellement, je suis coincé dans mes enquêtes à ce stade.

4
omegastripes

Désolé, je n’ai pas pu me rendre au travail, mais j’ai laissé tomber le problème. 

J'ai réussi à trouver la syntaxe correcte pour créer et déclencher un événement dans les versions ultérieures d'Internet Explorer (grâce à this SO answer ). J'ai réussi à soulever un événement d'entrée pour la zone de saisie, suivi de omegastripes excellente lead .

Donc, cela fonctionne pour un fichier local que je donne ci-dessous qui a la même structure essentielle de la page réelle. 

Mais lorsque je lance le code sur la page réelle, cela ne fonctionne tout simplement pas et je ne sais pas pourquoi.

Dans la mesure du possible, je fournis javascript à IE pour l'exécuter afin de minimiser les problèmes liés au pont d'interopérabilité VBA/COM/MSHTML. Je le fais en appelant window.execScript en transmettant le javascript sous forme de chaîne.

J'espère que quelqu'un pourra ramasser ce ballon et le faire passer par-dessus la ligne de but. Pour moi, j'ai atteint la fin.

Voici le fichier HTML local 

<!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>

Voici la 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
0
S Meaden