代码之家  ›  专栏  ›  技术社区  ›  Maurice Meyer

Pyqt5 QtWebEngine拦截带有自定义URL架构的请求,以提供来自Python的答复

  •  0
  • Maurice Meyer  · 技术社区  · 5 年前

    我正在尝试使用自定义URL方案重写几个HTTP请求:
    所有请求到 http://static.foo.bar 应该重新连接到 static://...

    问题:
    QWebEngineUrlSchemeHandler

    预期结果:
    示例图像 /tmp/iphone.jpg 服务对象 SchemeHandler 嵌入在HTML页面中,因此HTML显示 <h1>

    版本:
    Python 3.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_())
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   eyllanesc Yonghwan Shin    5 年前

    如果分析所获得页面请求的URL:

    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%2Fstatic.foo.bar%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://static.foo.bar/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')

    Where条件 if 'static.foo.bar' in str(info.requestUrl()):

    解决方法是改进过滤器:

    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)