为了账号安全,请及时绑定邮箱和手机立即绑定

有没有办法在浏览器中获取Pyqt5中页面的XPATH?

有没有办法在浏览器中获取Pyqt5中页面的XPATH?

繁星coding 2022-08-16 16:13:55
我正在使用pyqt5。我希望用户单击嵌入在我的pyqt5应用程序上的浏览器,以获取他/她正在单击的元素的XPATH。任何想法是如何完成的,或者它是否可行?
查看完整描述

1 回答

?
慕田峪4524236

TA贡献1875条经验 获得超5个赞

为了回答这个问题,我使用了以下答案:

连接所有这些部分,您将获得以下解决方案:

├── main.py

└── xpath_from_element.js

main.py


import os


from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets, QtWebChannel


from jinja2 import Template


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))



class Element(QtCore.QObject):

    def __init__(self, name, parent=None):

        super(Element, self).__init__(parent)

        self._name = name


    @property

    def name(self):

        return self._name


    def script(self):

        return ""



class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):

    def __init__(self, parent=None):

        super(WebEnginePage, self).__init__(parent)

        self.loadFinished.connect(self.onLoadFinished)

        self._objects = []

        self._scripts = []


    def add_object(self, obj):

        self._objects.append(obj)


    @QtCore.pyqtSlot(bool)

    def onLoadFinished(self, ok):

        print("Finished loading: ", ok)

        if ok:

            self.load_qwebchannel()

            self.add_objects()


    def load_qwebchannel(self):

        file = QtCore.QFile(":/qtwebchannel/qwebchannel.js")

        if file.open(QtCore.QIODevice.ReadOnly):

            content = file.readAll()

            file.close()

            self.runJavaScript(content.data().decode())

        if self.webChannel() is None:

            channel = QtWebChannel.QWebChannel(self)

            self.setWebChannel(channel)


    def add_objects(self):

        if self.webChannel() is not None:

            objects = {obj.name: obj for obj in self._objects}

            self.webChannel().registerObjects(objects)

            _script = """

            {% for obj in objects %}

            var {{obj}};

            {% endfor %}

            new QWebChannel(qt.webChannelTransport, function (channel) {

            {% for obj in objects %}

                {{obj}} = channel.objects.{{obj}};

            {% endfor %}

            }); 

            """

            self.runJavaScript(Template(_script).render(objects=objects.keys()))

            for obj in self._objects:

                if isinstance(obj, Element):

                    self.runJavaScript(obj.script())



class Helper(Element):

    xpathClicked = QtCore.pyqtSignal(str)


    def script(self):

        js = ""

        file = QtCore.QFile(os.path.join(CURRENT_DIR, "xpath_from_element.js"))

        if file.open(QtCore.QIODevice.ReadOnly):

            content = file.readAll()

            file.close()

            js = content.data().decode()


        js += """

        document.addEventListener('click', function(e) {

            e = e || window.event;

            var target = e.target || e.srcElement;

            var xpath = Elements.DOMPath.xPath(target, false); 

            {{name}}.receive_xpath(xpath);

        }, false);"""

        return Template(js).render(name=self.name)


    @QtCore.pyqtSlot(str)

    def receive_xpath(self, xpath):

        self.xpathClicked.emit(xpath)



if __name__ == "__main__":

    import sys


    app = QtWidgets.QApplication(sys.argv)


    xpath_helper = Helper("xpath_helper")

    xpath_helper.xpathClicked.connect(lambda xpath: print("clicked", xpath))

    view = QtWebEngineWidgets.QWebEngineView()

    page = WebEnginePage()

    page.add_object(xpath_helper)

    view.setPage(page)

    view.load(QtCore.QUrl("https://www.qt.io"))

    view.show()

    sys.exit(app.exec_())

xpath_from_element.js


// Copyright 2018 The Chromium Authors. All rights reserved.

// Use of this source code is governed by a BSD-style license that can be

// found in the LICENSE file.


Elements = {};

Elements.DOMPath = {};


/**

 * @param {!Node} node

 * @param {boolean=} optimized

 * @return {string}

 */

Elements.DOMPath.xPath = function (node, optimized) {

    if (node.nodeType === Node.DOCUMENT_NODE) {

        return '/';

    }


    const steps = [];

    let contextNode = node;

    while (contextNode) {

        const step = Elements.DOMPath._xPathValue(contextNode, optimized);

        if (!step) {

            break;

        }  // Error - bail out early.

        steps.push(step);

        if (step.optimized) {

            break;

        }

        contextNode = contextNode.parentNode;

    }


    steps.reverse();

    return (steps.length && steps[0].optimized ? '' : '/') + steps.join('/');

};


/**

 * @param {!Node} node

 * @param {boolean=} optimized

 * @return {?Elements.DOMPath.Step}

 */

Elements.DOMPath._xPathValue = function (node, optimized) {

    let ownValue;

    const ownIndex = Elements.DOMPath._xPathIndex(node);

    if (ownIndex === -1) {

        return null;

    }  // Error.


    switch (node.nodeType) {

        case Node.ELEMENT_NODE:

            if (optimized && node.getAttribute('id')) {

                return new Elements.DOMPath.Step('//*[@id="' + node.getAttribute('id') + '"]', true);

            }

            ownValue = node.localName;

            break;

        case Node.ATTRIBUTE_NODE:

            ownValue = '@' + node.nodeName;

            break;

        case Node.TEXT_NODE:

        case Node.CDATA_SECTION_NODE:

            ownValue = 'text()';

            break;

        case Node.PROCESSING_INSTRUCTION_NODE:

            ownValue = 'processing-instruction()';

            break;

        case Node.COMMENT_NODE:

            ownValue = 'comment()';

            break;

        case Node.DOCUMENT_NODE:

            ownValue = '';

            break;

        default:

            ownValue = '';

            break;

    }


    if (ownIndex > 0) {

        ownValue += '[' + ownIndex + ']';

    }


    return new Elements.DOMPath.Step(ownValue, node.nodeType === Node.DOCUMENT_NODE);

};


/**

 * @param {!Node} node

 * @return {number}

 */

Elements.DOMPath._xPathIndex = function (node) {

    // Returns -1 in case of error, 0 if no siblings matching the same expression,

    // <XPath index among the same expression-matching sibling nodes> otherwise.

    function areNodesSimilar(left, right) {

        if (left === right) {

            return true;

        }


        if (left.nodeType === Node.ELEMENT_NODE && right.nodeType === Node.ELEMENT_NODE) {

            return left.localName === right.localName;

        }


        if (left.nodeType === right.nodeType) {

            return true;

        }


        // XPath treats CDATA as text nodes.

        const leftType = left.nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : left.nodeType;

        const rightType = right.nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : right.nodeType;

        return leftType === rightType;

    }


    const siblings = node.parentNode ? node.parentNode.children : null;

    if (!siblings) {

        return 0;

    }  // Root node - no siblings.

    let hasSameNamedElements;

    for (let i = 0; i < siblings.length; ++i) {

        if (areNodesSimilar(node, siblings[i]) && siblings[i] !== node) {

            hasSameNamedElements = true;

            break;

        }

    }

    if (!hasSameNamedElements) {

        return 0;

    }

    let ownIndex = 1;  // XPath indices start with 1.

    for (let i = 0; i < siblings.length; ++i) {

        if (areNodesSimilar(node, siblings[i])) {

            if (siblings[i] === node) {

                return ownIndex;

            }

            ++ownIndex;

        }

    }

    return -1;  // An error occurred: |node| not found in parent's children.

};


/**

 * @unrestricted

 */

Elements.DOMPath.Step = class {

    /**

     * @param {string} value

     * @param {boolean} optimized

     */

    constructor(value, optimized) {

        this.value = value;

        this.optimized = optimized || false;

    }


    /**

     * @override

     * @return {string}

     */

    toString() {

        return this.value;

    }

};



查看完整回答
反对 回复 2022-08-16
  • 1 回答
  • 0 关注
  • 142 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信