Jump to content

Recommended Posts

Is it possible to read an XPath with AutoIt instead of using "Inspect" and ChroPath? I would like to write a script that takes the XPath of a mouse-clicked field or button into a variable, like copy and paste. That would save a lot of time. 

Link to post
Share on other sites
Posted (edited)
12 hours ago, JackER4565 said:

yes you can. If you want you can read the entire HTML and go through the elements in it.

That's not the problem. I want to get that XPath, that is under the mouse. If I read the entire HTML, I don't know where I clicked.
It is like "Inspect" and "Copy XPath" what I am searching for - but automatic.



Edited by HJL
Link to post
Share on other sites

...maybe not exactly what you are looking for, but you can take a look at the following link as an example to modify for your goals


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Link to post
Share on other sites

I actually did this. You'll need two things, this script and a JavaScript function that will get the XPath of an element passed to it.


 This is my XPathFromMouse.au3 file. Edit it (to set your webdriver location and other properties), run it, and press Ctrl+6 to get another XPath

#include <wd_core.au3>
#include <wd_helper.au3>
;#include <WD_Ex.au3> Not needed, personal use
;#include <CustomDebugging.au3> Not needed, added Debug + ErrMsg

Global $sSession, $__g_iXPathIndex = 0, $sConfig = @ScriptDir & "\config.ini"


Func Main()

    Local $sDriverPath = @AppDataDir & "\WebDrivers\msedgedriver.exe"

    _WD_Option('Driver', $sDriverPath)
    _WD_Option('DriverClose', True)
    _WD_Option('DriverDetect', False)
    _WD_Option('Port', 9515)

    If ErrMsg(_WD_Startup) Then Exit

    Local $sDesiredCapabilities = '{"capabilities":{"alwaysMatch": {"ms:edgeOptions": {"w3c": true, "prefs": {"plugins.always_open_pdf_externally": true}}}}}'

    $sSession = _WD_CreateSession($sDesiredCapabilities)
    If ErrMsg(_WD_CreateSession) Then Exit


    _WD_Navigate($sSession, "https://www.google.com")

    HotKeySet("^6", GetXPath)

    While True




Func GetXPath()

    #ToDo: Determine if window is active
    #ToDo: Determine which tab is active and attach

    Local $sElement = _WD_GetMouseElement($sSession)
    If ErrMsg(_WD_GetMouseElement) Then Exit

    Local $vRet = _WDEx_ElementXPathGet($sSession, $sElement)
    If ErrMsg(_WDEx_ElementXPathGet) Then Exit

    Debug("Path: " & $vRet)

    Local $sFoundElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, $vRet)
    If ErrMsg(_WD_FindElement) Then Exit

    If Not ($sElement = $sFoundElement) Then
        Debug("Mouse Element: " & $sElement)
        Debug("Found Element: " & $sFoundElement)
        Return SetError(1, 0, False)
        IniWrite($sConfig, "XPaths", $__g_iXPathIndex, $vRet)
        $__g_iXPathIndex += 1


Func _WDEx_ElementXPathGet($sSession, $sElementId)

    Local $WD_Debug_Saved = $_WD_Debug
    $_WD_Debug = $_WD_DEBUG_None
    ; Pulled from: https://stackoverflow.com/questions/2661818/javascript-get-xpath-of-a-node
    Local $sResponse = _WD_ExecuteScript($sSession, FileRead(@UserProfileDir & "\Documents\AutoIt\javascript_test.txt"), '{"' & $_WD_ELEMENT_ID & '":"' & $sElementId & '"}')
    If ErrMsg(_WD_ExecuteScript) Then
        $_WD_Debug = $WD_Debug_Saved
        Return SetError(1, @error, False)
    Local $oJson = Json_Decode($sResponse)
    Local $vRet  = Json_Get($oJson, "[value]")

    $_WD_Debug = $WD_Debug_Saved

    Return $vRet


Func CloseChrome()



Func Debug($sMsg, $sPrefix = "+")

    ; Write to the console if we're not compiled
    If Not @Compiled Then
        If $sPrefix <> "" Then $sPrefix &= " "
        ConsoleWrite($sPrefix & $sMsg & @CRLF)


Func ErrMsg($sMsg = "", $iError = @error, $iExtended = @extended, $iScriptLineNum = @ScriptLineNumber)

    ; If we've used a Function as the message (it looks really nice when I do) then print the name of the function
    If IsFunc($sMsg) Then $sMsg = FuncName($sMsg)

    ; If there is an error, then write the error message
    If $iError Then
        Local $sText = '"' & @ScriptFullPath & '" (' & $iScriptLineNum & ',5) : (See below for message)' & @CRLF
        If $iExtended <> 0 Then $sMsg = "Extended: " & $iExtended & " - " & $sMsg
        $sText &= "! Error: " & $iError & " - " & $sMsg
        Debug($sText, "")
    ; Preserve the error
    Return SetError($iError, $iExtended, $iError)



============= Output File Pattern =============

[Step Sequence]
<#>=<user defined step name>
[<user defined step name>]
Function name=<user selected function>
<function name>-<property #>=<function specific property value>

============= Example Output File =============

[Step Sequence]
1=Open Google
2=Enter Search
3=Click Search

[Open Google]
Function name=_WD_Navigate

[Enter Search]
Function name=_WD_ElementAction
_WD_ElementAction-XPath=id("tsf")/div[2]/div[@class="A8SBwf"]/div[@class="RNNXgb"]/div[@class="SDkEP"]/div[@class="a4bIc"]/input[@class="gLFyf gsfi"]

[Click Search]
Function name=_WD_ElementAction
_WD_ElementAction-XPath=id("tsf")/div[2]/div[@class="A8SBwf"]/div[@class="FPdoLc tfB0Bf"]/center[1]/input[@class="gNO89b"]



I suggest trying out a few different functions from StackOverflow. You'll find a few different styles of XPath. Choose the one you like most.


The (slightly modified) javascript_test.txt, pulled the JavaScript from here: https://stackoverflow.com/questions/2661818/javascript-get-xpath-of-a-node

Note: The important modification is adding "return <functionName>(arguments[0])" as this allows you to pass an element into the function and get the function's return value

return createXPathFromElement(arguments[0]);

function createXPathFromElement(elm) { 
    var allNodes = document.getElementsByTagName('*'); 
    for (var segs = []; elm && elm.nodeType == 1; elm = elm.parentNode) 
        if (elm.hasAttribute('id')) { 
                var uniqueIdCount = 0; 
                for (var n=0;n < allNodes.length;n++) { 
                    if (allNodes[n].hasAttribute('id') && allNodes[n].id == elm.id) uniqueIdCount++; 
                    if (uniqueIdCount > 1) break; 
                if ( uniqueIdCount == 1) { 
                    segs.unshift('id("' + elm.getAttribute('id') + '")'); 
                    return segs.join('/'); 
                } else { 
                    segs.unshift(elm.localName.toLowerCase() + '[@id="' + elm.getAttribute('id') + '"]'); 
        } else if (elm.hasAttribute('class')) { 
            segs.unshift(elm.localName.toLowerCase() + '[@class="' + elm.getAttribute('class') + '"]'); 
        } else { 
            for (i = 1, sib = elm.previousSibling; sib; sib = sib.previousSibling) { 
                if (sib.localName == elm.localName)  i++; }; 
                segs.unshift(elm.localName.toLowerCase() + '[' + i + ']'); 
    return segs.length ? '/' + segs.join('/') : null; 

function lookupElementByXPath(path) { 
    var evaluator = new XPathEvaluator(); 
    var result = evaluator.evaluate(path, document.documentElement, null,XPathResult.FIRST_ORDERED_NODE_TYPE, null); 
    return  result.singleNodeValue; 


Edited by seadoggie01

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.


My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Create New...