有啥方法可以让浏览器页面避免 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 中的重定向?的主要内容,如果未能解决你的问题,请参考以下文章
有啥方法可以避免 axios 中的 linkpreview.net 出现 CORS 错误,例如 Angular 中的 trustAsResourceUrl()?