Interceptar carga de recursos en Qt WebEngine (PyQt 5)

Interceptar carga de recursos en Qt WebEngine (PyQt 5)



Descarga: qt-webengine-interceptor.zip.

Ya vimos en el ejemplo de un navegador web simple con PyQt 5 cómo utilizar Qt WebEngine ─en particular, la clase QWebEngineView─ para cargar una página web dentro de un control de nuestra aplicación.

Otra de las funcionalidades interesantes que nos provee este widget es la de poder interceptar la carga de recursos que realiza el navegador de Qt (navegador que, por cierto, utiliza Chromium). Por recursos me refiero a toda dirección de URL que se involucra en el renderizado de una página web: archivos de imágenes, hojas de estilo (CSS), código de fuente de JavaScript, lo que fuere. Así, por ejemplo, vía este método podríamos cancelar todas las peticiones que se hagan a direcciones que no utilicen el protocolo HTTPS, o bien aquellas que accedan a un dominio en particular que consideramos malicioso.

El primer paso es crear la clase que actuará como interceptor.

from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor


class RequestInterceptor(QWebEngineUrlRequestInterceptor):
    
    def interceptRequest(self, info):
        pass

El método interceptRequest() será invocado por Qt cada vez que se intente cargar algún recurso. La información acerca de éste estará contenida en el argumento info, que será una instancia de QWebEngineUrlRequestInfo. De modo que aquí definiremos toda la lógica de filtrado que queramos aplicar sobre nuestro navegador web incorporado.

Pero para que Qt pueda despachar los eventos primeros debemos conectar nuestro interceptor al control correspondiente. Así, suponiendo que nuestro widget es self.webview (como lo hicimos en este ejemplo), le asignaremos la clase que acabamos de crear del siguiente modo.

# Por lo general ubicaremos esto dentro del método __init__()
# de nuestra ventana.
self.webview.page().profile().setRequestInterceptor(
    RequestInterceptor(self))

Ahora bien, algunos de los métodos del argumento info son requestMethod() (cuyo valor de retorno es una instancia de QByteArray) y requestUrl() (QUrl), que nos indican el método HTTP que se ha empleado para acceder a un recurso (GET, POST, PUT, etc.) y la dirección de URL del mismo. En consencuencia, el siguiente código imprime en pantalla cada uno de los recursos cargados y su respectivo método.

    def interceptRequest(self, info):
        print("[{}] {}".format(
            info.requestMethod().data().decode("utf-8"),
            info.requestUrl().toString()))

Cargando la web oficial de Python en nuestro navegador vía self.webview.load(QUrl("https://python.org/")) obtenemos los siguientes mensajes:

[GET] https://python.org/
[GET] https://www.python.org/
[GET] https://www.python.org/static/js/libs/modernizr.js
[GET] https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js
[GET] https://www.python.org/static/stylesheets/style.css
[GET] https://www.python.org/static/stylesheets/mq.css
[GET] https://www.python.org/static/img/python-logo.png
[GET] https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js
[GET] https://www.python.org/static/js/libs/masonry.pkgd.min.js
[GET] https://www.python.org/static/js/main-min.js
[GET] https://ssl.google-analytics.com/ga.js

Pero lo más interesante es que vía el método info.block() podemos bloquear la carga de cualquiera de estos recursos. Por ejemplo, del siguiente modo evitamos la carga de las imágenes de una web.

    def interceptRequest(self, info):
        if info.resourceType() == info.ResourceTypeImage:
            info.block(True)

Y de este otro, cualquier archivo de estilos:

    def interceptRequest(self, info):
        if info.resourceType() == info.ResourceTypeStylesheet:
            info.block(True)

El resultado es evidente:

Estilos bloqueados en un navegador de Qt

Como decíamos al comienzo, podemos también bloquear recursos que provengan de algún dominio que consideremos malicioso:

    def interceptRequest(self, info):
        host = info.requestUrl().host()
        if "sitiomalicioso.com" in host:
            info.block(True)

Si evitar la carga de un recurso no es nuestro cometido, tal vez nos sea más útil redireccionarlo a una nueva dirección vía info.redirect(QUrl(...)).

En conclusión, a partir de los pocos atributos y métodos que hemos escudriñado es como debemos proceder para aplicar distintas políticas de filtro a los recursos que constituyen una página web.



Deja un comentario