Jump to content

Read XPath


 Share

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 comment
Share on other sites

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

 

 

image.jpeg.9f1a974c98e9f77d824b358729b089b0.jpeg Chimp

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

Link to comment
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.

Spoiler

 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"

Main()

Func Main()

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

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

    _WD_Startup()
    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

    OnAutoItExitRegister(CloseChrome)

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

    HotKeySet("^6", GetXPath)

    While True

        Sleep(200)

    WEnd

EndFunc

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)
    Else
        IniWrite($sConfig, "XPaths", $__g_iXPathIndex, $vRet)
        $__g_iXPathIndex += 1
    EndIf

EndFunc

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)
    EndIf
    Local $oJson = Json_Decode($sResponse)
    Local $vRet  = Json_Get($oJson, "[value]")

    $_WD_Debug = $WD_Debug_Saved

    Return $vRet

EndFunc

Func CloseChrome()

    _WD_DeleteSession($sSession)
    _WD_Shutdown()

EndFunc

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)
    EndIf

EndFunc

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, "")
    EndIf
    ; Preserve the error
    Return SetError($iError, $iExtended, $iError)

EndFunc

#cs

============= 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
_WD_Navigate-LoadWait=True

[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"]
_WD_ElementAction-Command=value
_WD_ElementAction-Option=test

[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"]
_WD_ElementAction-Command=click

#ce

 

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.

Spoiler

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.

Spoiler

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
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...