有啥方法可以让浏览器页面避免 Pyqt5 中的重定向?

Posted

技术标签:

【中文标题】有啥方法可以让浏览器页面避免 Pyqt5 中的重定向?【英文标题】:Is there any way to make browser page avoiding redirects in Pyqt5?有什么方法可以让浏览器页面避免 Pyqt5 中的重定向? 【发布时间】:2020-03-04 01:39:41 【问题描述】:

我正在使用 pyqt5。我希望用户单击嵌入在我的 pyqt5 应用程序中的浏览器以获取他/她单击它的元素的 XPATH。

我得到了解决方案如何由某人实现上述想法,但浏览器上的页面被重定向到网站的另一个页面。

如果我希望用户单击元素,但不允许他通过单击按钮走得更远并离开原始页面,该怎么办。简而言之,我只想坚持在同一页面上而不重定向到另一个页面,以便仅获取同一页面的 XPATH。

这是@eyllanesc 建议的工作代码如下:

我的文件

├── 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;
    
;

原答案Is there any way of getting XPATH of the page in Pyqt5 in browser? 任何帮助将不胜感激...

【问题讨论】:

【参考方案1】:

一种可能的解决方案是事件不传输到使用preventDefault()方法点击的元素:

js += """
document.addEventListener('click', function(e) 
    e = e || window.event;
    e.preventDefault()
    var target = e.target || e.srcElement;
    var xpath = Elements.DOMPath.xPath(target, false); 
    name.receive_xpath(xpath);
, false);"""

【讨论】:

以上是关于有啥方法可以让浏览器页面避免 Pyqt5 中的重定向?的主要内容,如果未能解决你的问题,请参考以下文章

selenium重定项

selenium重定项

有啥方法可以避免 C++ 中的“调试断言失败”窗口?

有啥方法可以避免一个查询中的多个联接

有啥方法可以避免 axios 中的 linkpreview.net 出现 CORS 错误,例如 Angular 中的 trustAsResourceUrl()?

我需要撤消重定向插件完成的重定向 URL