QtWebEngine 使用自定义 URL 模式服务回复拦截请求
QtWebEngine intercepting request with custom URL schema serving reply
我正在尝试使用自定义 URL 方案重写多个 HTTP 请求:
对 http://static.foo.bar
的所有请求都应重新写入 static://...
并提供一些回复。
问题:
拦截和重定向似乎有效,但无论我的 QWebEngineUrlSchemeHandler
实现返回什么(图像或 html)总是替换完整的 HTML 页面。
预期结果:
SchemeHandler
提供的示例图片 /tmp/iphone.jpg
嵌入在 HTML 页面中,因此 HTML 显示 <h1>
标题和 2 张图片。
版本:
Python3.7.4
PyQt:5.14.1
示例代码:
import sys
import signal
from PyQt5 import QtCore
from PyQt5.QtCore import QUrl, QObject
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings, QWebEnginePage, QWebEngineProfile
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor, QWebEngineUrlSchemeHandler, QWebEngineUrlScheme
from PyQt5.QtWidgets import QApplication, QMainWindow
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
# Everything requested from static.foo.bar goes to static://
# //static.foo.bar/1/2/4.jpeg >> static://1/2/4.jpeg
def interceptRequest(self, info):
print("interceptRequest")
print(info.requestUrl())
if 'static.foo.bar' in str(info.requestUrl()):
url = QUrl()
url.setScheme(MyWebEngineUrlScheme.scheme.decode())
url.setHost('baz.jpg')
print('Intercepting and redirecting to: %s' % url)
info.redirect(url)
class MyWebEnginePage(QWebEnginePage):
# debugging
def acceptNavigationRequest(self, url, _type, isMainFrame):
print("acceptNavigationRequest: %s" % url)
return QWebEnginePage.acceptNavigationRequest(self, url, _type, isMainFrame)
class SchemeHandler(QWebEngineUrlSchemeHandler):
def __init__(self, app):
super().__init__(app)
def requestStarted(self, request):
url = request.requestUrl()
print('SchemeHandler requestStarted: %s' % url)
# Returns a sample image
raw_html = open('/tmp/iphone.jpg', 'rb').read()
buf = QtCore.QBuffer(parent=self)
request.destroyed.connect(buf.deleteLater)
buf.open(QtCore.QIODevice.WriteOnly)
buf.write(raw_html)
buf.seek(0)
buf.close()
request.reply(b"image/jpeg", buf)
return
class MyWebEngineUrlScheme(QObject):
# Register scheme
scheme = b"static"
def __init__(self, parent=None):
super().__init__(parent)
scheme = QWebEngineUrlScheme(MyWebEngineUrlScheme.scheme)
QWebEngineUrlScheme.registerScheme(scheme)
self.m_functions = dict()
def init_handler(self, profile=None):
if profile is None:
profile = QWebEngineProfile.defaultProfile()
handler = profile.urlSchemeHandler(MyWebEngineUrlScheme.scheme)
if handler is not None:
profile.removeUrlSchemeHandler(handler)
self.handler = SchemeHandler(self)
print("registering %s to %s" % (MyWebEngineUrlScheme.scheme, self.handler))
profile.installUrlSchemeHandler(MyWebEngineUrlScheme.scheme, self.handler)
schemeApp = MyWebEngineUrlScheme()
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QApplication(sys.argv)
win = QMainWindow()
win.resize(800, 600)
html = """
<html>
<body>
<h1>test</h1>
<hr>
<p>First image</p>
<img src="http://static.foo.bar/baz.jpg" />
<hr>
<p>Second image</p>
<img src="https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/iphone-xr-red-select-201809?wid=1200&hei=630&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1551226038669" />
</body>
</html>
"""
browser = QWebEngineView()
interceptor = WebEngineUrlRequestInterceptor()
profile = QWebEngineProfile()
profile.setUrlRequestInterceptor(interceptor)
page = MyWebEnginePage(profile, browser)
schemeApp.init_handler(profile)
browser.settings().setAttribute(QWebEngineSettings.PluginsEnabled, True)
browser.settings().setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, False)
browser.settings().setAttribute(QWebEngineSettings.LinksIncludedInFocusChain, False)
browser.settings().setAttribute(QWebEngineSettings.LocalStorageEnabled, True)
browser.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
page.setHtml(html)
browser.setPage(page)
browser.show()
win.setCentralWidget(browser)
win.show()
sys.exit(app.exec_())
如果您分析页面请求的 urls,您会得到:
PyQt5.QtCore.QUrl('data:text/html;charset=UTF-8,%0A%3Chtml%3E%0A%3Cbody%3E%0A%3Ch1%3Etest%3C%2Fh1%3E%0A%3Chr%3E%0A%3Cp%3EFirst image%3C%2Fp%3E%0A%3Cimg src%3D%22http%3A%2F%2F<b>static.foo.bar</b>%2Fbaz.jpg%22 %2F%3E%0A%3Chr%3E%0A%3Cp%3ESecond image%3C%2Fp%3E%0A%3Cimg src%3D%22https%3A%2F%2Fstore.storeimages.cdn-apple.com%2F4668%2Fas-images.apple.com%2Fis%2Fiphone-xr-red-select-201809%3Fwid%3D1200%26hei%3D630%26fmt%3Djpeg%26qlt%3D95%26op_usm%3D0.5%2C0.5%26.v%3D1551226038669%22 %2F%3E%0A%3C%2Fbody%3E%0A%3C%2Fhtml%3E%0A')
PyQt5.QtCore.QUrl('http://<b>static.foo.bar</b>/baz.jpg')
PyQt5.QtCore.QUrl('https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/iphone-xr-red-select-201809?wid=1200&hei=630&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1551226038669')
第一个和第二个满足条件 if 'static.foo.bar' in str(info.requestUrl()):
url((为了更好的可视化,我将其标记为粗体)。
解决方法是改进过滤器:
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
# Everything requested from static.foo.bar goes to static://
# //static.foo.bar/1/2/4.jpeg >> static://1/2/4.jpeg
def interceptRequest(self, info):
print("interceptRequest")
print(info.requestUrl())
if info.requestUrl().host().startswith("static.foo.bar"): # or if info.requestUrl().host() == "static.foo.bar":
url = QUrl()
url.setScheme(MyWebEngineUrlScheme.scheme.decode())
url.setHost(info.requestUrl().path()[1:]) # remove "/"
print("Intercepting and redirecting to: %s" % url)
info.redirect(url)
我正在尝试使用自定义 URL 方案重写多个 HTTP 请求:
对 http://static.foo.bar
的所有请求都应重新写入 static://...
并提供一些回复。
问题:
拦截和重定向似乎有效,但无论我的 QWebEngineUrlSchemeHandler
实现返回什么(图像或 html)总是替换完整的 HTML 页面。
预期结果:
SchemeHandler
提供的示例图片 /tmp/iphone.jpg
嵌入在 HTML 页面中,因此 HTML 显示 <h1>
标题和 2 张图片。
版本:
Python3.7.4
PyQt:5.14.1
示例代码:
import sys
import signal
from PyQt5 import QtCore
from PyQt5.QtCore import QUrl, QObject
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings, QWebEnginePage, QWebEngineProfile
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor, QWebEngineUrlSchemeHandler, QWebEngineUrlScheme
from PyQt5.QtWidgets import QApplication, QMainWindow
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
# Everything requested from static.foo.bar goes to static://
# //static.foo.bar/1/2/4.jpeg >> static://1/2/4.jpeg
def interceptRequest(self, info):
print("interceptRequest")
print(info.requestUrl())
if 'static.foo.bar' in str(info.requestUrl()):
url = QUrl()
url.setScheme(MyWebEngineUrlScheme.scheme.decode())
url.setHost('baz.jpg')
print('Intercepting and redirecting to: %s' % url)
info.redirect(url)
class MyWebEnginePage(QWebEnginePage):
# debugging
def acceptNavigationRequest(self, url, _type, isMainFrame):
print("acceptNavigationRequest: %s" % url)
return QWebEnginePage.acceptNavigationRequest(self, url, _type, isMainFrame)
class SchemeHandler(QWebEngineUrlSchemeHandler):
def __init__(self, app):
super().__init__(app)
def requestStarted(self, request):
url = request.requestUrl()
print('SchemeHandler requestStarted: %s' % url)
# Returns a sample image
raw_html = open('/tmp/iphone.jpg', 'rb').read()
buf = QtCore.QBuffer(parent=self)
request.destroyed.connect(buf.deleteLater)
buf.open(QtCore.QIODevice.WriteOnly)
buf.write(raw_html)
buf.seek(0)
buf.close()
request.reply(b"image/jpeg", buf)
return
class MyWebEngineUrlScheme(QObject):
# Register scheme
scheme = b"static"
def __init__(self, parent=None):
super().__init__(parent)
scheme = QWebEngineUrlScheme(MyWebEngineUrlScheme.scheme)
QWebEngineUrlScheme.registerScheme(scheme)
self.m_functions = dict()
def init_handler(self, profile=None):
if profile is None:
profile = QWebEngineProfile.defaultProfile()
handler = profile.urlSchemeHandler(MyWebEngineUrlScheme.scheme)
if handler is not None:
profile.removeUrlSchemeHandler(handler)
self.handler = SchemeHandler(self)
print("registering %s to %s" % (MyWebEngineUrlScheme.scheme, self.handler))
profile.installUrlSchemeHandler(MyWebEngineUrlScheme.scheme, self.handler)
schemeApp = MyWebEngineUrlScheme()
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QApplication(sys.argv)
win = QMainWindow()
win.resize(800, 600)
html = """
<html>
<body>
<h1>test</h1>
<hr>
<p>First image</p>
<img src="http://static.foo.bar/baz.jpg" />
<hr>
<p>Second image</p>
<img src="https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/iphone-xr-red-select-201809?wid=1200&hei=630&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1551226038669" />
</body>
</html>
"""
browser = QWebEngineView()
interceptor = WebEngineUrlRequestInterceptor()
profile = QWebEngineProfile()
profile.setUrlRequestInterceptor(interceptor)
page = MyWebEnginePage(profile, browser)
schemeApp.init_handler(profile)
browser.settings().setAttribute(QWebEngineSettings.PluginsEnabled, True)
browser.settings().setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, False)
browser.settings().setAttribute(QWebEngineSettings.LinksIncludedInFocusChain, False)
browser.settings().setAttribute(QWebEngineSettings.LocalStorageEnabled, True)
browser.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
page.setHtml(html)
browser.setPage(page)
browser.show()
win.setCentralWidget(browser)
win.show()
sys.exit(app.exec_())
如果您分析页面请求的 urls,您会得到:
PyQt5.QtCore.QUrl('data:text/html;charset=UTF-8,%0A%3Chtml%3E%0A%3Cbody%3E%0A%3Ch1%3Etest%3C%2Fh1%3E%0A%3Chr%3E%0A%3Cp%3EFirst image%3C%2Fp%3E%0A%3Cimg src%3D%22http%3A%2F%2F<b>static.foo.bar</b>%2Fbaz.jpg%22 %2F%3E%0A%3Chr%3E%0A%3Cp%3ESecond image%3C%2Fp%3E%0A%3Cimg src%3D%22https%3A%2F%2Fstore.storeimages.cdn-apple.com%2F4668%2Fas-images.apple.com%2Fis%2Fiphone-xr-red-select-201809%3Fwid%3D1200%26hei%3D630%26fmt%3Djpeg%26qlt%3D95%26op_usm%3D0.5%2C0.5%26.v%3D1551226038669%22 %2F%3E%0A%3C%2Fbody%3E%0A%3C%2Fhtml%3E%0A')
PyQt5.QtCore.QUrl('http://<b>static.foo.bar</b>/baz.jpg')
PyQt5.QtCore.QUrl('https://store.storeimages.cdn-apple.com/4668/as-images.apple.com/is/iphone-xr-red-select-201809?wid=1200&hei=630&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1551226038669')
第一个和第二个满足条件 if 'static.foo.bar' in str(info.requestUrl()):
url((为了更好的可视化,我将其标记为粗体)。
解决方法是改进过滤器:
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
# Everything requested from static.foo.bar goes to static://
# //static.foo.bar/1/2/4.jpeg >> static://1/2/4.jpeg
def interceptRequest(self, info):
print("interceptRequest")
print(info.requestUrl())
if info.requestUrl().host().startswith("static.foo.bar"): # or if info.requestUrl().host() == "static.foo.bar":
url = QUrl()
url.setScheme(MyWebEngineUrlScheme.scheme.decode())
url.setHost(info.requestUrl().path()[1:]) # remove "/"
print("Intercepting and redirecting to: %s" % url)
info.redirect(url)