当使用QtWebKit时,如何知道网页何时加载完成?

16

无论是 QWebFrame 还是 QWebPage 都有一个名为 void loadFinished(bool ok) 的信号,可以用于检测网页何时完全加载完成。但问题在于,当网页存在异步加载内容(ajax)时,如何知道网页何时完全加载完成呢?


你如何定义完全加载?当没有任何ajax代码正在运行时,页面是否已经完全加载?(即使ajax代码可能在未来运行)当没有任何ajax代码将来会运行时,页面是否已经完全加载?有了这些信息,你会做些什么不同的事情?(为什么这很重要?) - Bill
在我的情况下,ajax调用被调用在onload事件上。当它完成后,我认为页面已经完全加载。 - Piotr Dobrogost
4个回答

13

我实际上还没有做过这个,但我认为您可以使用QNetworkAccessManager来实现您的解决方案。

您可以使用networkAccessManager()函数从您的QWebPage中获取QNetworkAccessManager。 QNetworkAccessManager有一个finished ( QNetworkReply * reply )信号,每当QWebPage实例请求文件时都会触发该信号。

finished信号会给您一个QNetworkReply实例,您可以从中获取原始请求的副本,以识别该请求。

因此,创建一个插槽以附加到finished信号,使用传入的QNetworkReply的方法来找出刚刚完成下载的文件是哪个,如果它是您的Ajax请求,则执行所需的任何处理。

我的唯一警告是我以前从未做过这件事,所以我不能保证它会起作用。

另一种选择可能是使用QWebFrame的方法将对象插入到页面的对象模型中,并插入一些JavaScript,然后在Ajax请求完成时通知您的对象。这是一种稍微有点hack的方法,但肯定可以工作。

编辑:

第二个选项对我来说似乎更好。工作流程如下:

将一个槽附加到QWebFrame :: javascriptWindowObjectCleared()信号。此时,调用QWebFrame :: evaluateJavascript()添加类似以下代码的代码: window.onload = function() { // page has fully loaded }

在该函数中放置所需的任何代码。您可能希望通过QWebFrame :: addToJavaScriptWindowObject()向页面添加QObject,然后调用该对象上的函数。此代码仅在页面完全加载时执行。

希望这回答了问题!


你能给我更多关于你想要实现的目标的信息吗?据我所理解,它是这样的:1)你通过QWebView->load()或其他方法加载一个页面 2)当页面内容(HTML)被接收时,QNAM会触发finished()信号 3)当整个页面 - 包括JS文件、CSS和图像 - 完成加载时,QWebPage对象会触发loadFinished()信号 4)在稍后的某个时间点,通过Ajax加载额外的数据你想知道#4何时发生?请进一步解释,我可能能够完全回答你的问题。 - Rob Knight
广告1:不完全是这样。我使用QWebFrame :: load,因为我根本不需要渲染阶段。但目前,我正在使用QWebView :: setPage查看页面的外观,但这仅用于调试目的。广告2:我对单独的HTML不感兴趣,所以我在这里不使用此信号。广告3:是的。广告4:是的。此ajax调用的时刻严格定义;它发生在用户单击数据行的某个部分时。这就是我的问题所在。我使用evaluateJavaScript调用相同的js函数,但什么也没有发生;没有发送任何网络请求(我正在监视QNAM发送的所有请求)。待续。 - Piotr Dobrogost
我已经更深入地调查了QNAM。似乎为了监视所有请求,您需要创建一个继承自QNetworkAccessManager并覆盖createRequest()函数的新类。在重写的函数中,您可以为每个请求添加一个finished()信号的槽。这使得跟踪所有请求成为可能,而不仅仅是主页面请求。但是,并不能保证页面在请求完成后立即完成处理结果。也许您可以设置一个计时器,在请求完成5秒后检查结果? - Rob Knight
1
我尝试使用定时器来检查是否能解决问题。它可以使用定时器,并且最重要的是只需要将定时器设置为10毫秒即可。这让我相信它是如此短的时间间隔,以至于仅足以离开我正在调用js的函数。这反过来又让我怀疑我的代码中存在一些与我们正在讨论的问题无直接关系的时间问题。这是可能的,因为我正在使用QStateMachine和自己的命令队列(https://dev59.com/p0jSa4cB1Zd3GeqPI9qb)。所以,在我们的讨论之后,我回到了调试... - Piotr Dobrogost
@PiotrDobrogost - 所以只是等待页面解决了这个问题? - alexizydorczyk
显示剩余5条评论

2

要检查特定元素的负载,您可以使用 QTimer。在 Python 中可以像这样实现:

@pyqtSlot()
def on_webView_loadFinished(self): 
    self.tObject = QTimer()
    self.tObject.setInterval(1000)
    self.tObject.setSingleShot(True)
    self.tObject.timeout.connect(self.on_tObject_timeout)
    self.tObject.start()

@pyqtSlot()
def on_tObject_timeout(self):
    dElement = self.webView.page().currentFrame().documentElement()
    element  = dElement.findFirst("css selector")
    if element.isNull():
        self.tObject.start()

    else:
        print "Page loaded"

1

当你的初始HTML、图片等加载完成后,就完成了。这个事实不会改变,即使你之后决定使用一些JavaScript来获取一些额外的数据、页面浏览或其他。

话虽如此,我猜想你想要在此处执行的是向视图公开一个QtScript对象/接口,以便你可以从你网页的脚本中调用它,从而提供一个“回调”到你的C++,一旦你已经决定(从页面脚本)已经“完全加载”了。

希望这能给你一个尝试的方向...


这不是我的页面,我无法更改它包含的脚本,因此我无法从中调用我的脚本/代码。 Ajax 调用是通过单击数据行触发的(我正在以编程方式模拟这些单击),并检索其他数据。 我需要在加载完成后读取此数据,因此我需要知道何时加载完成。 - Piotr Dobrogost
所以你想使用Qt来执行某种跨站脚本攻击?我认为这样行不通。唯一想到的主意是你可以观察HTTP状态本身,这些状态在加载事件后开始“监听”。 - Shaun
你为什么认为它不会起作用呢?使用QtWebKit,你可以完全访问页面的DOM和JavaScript,甚至可以调用自己的JavaScript。你还可以完全访问网络层。你还需要什么?你需要HTTP状态码有什么用呢?这是非常底层的东西。我想要并且需要的只是能够在用户正常浏览时模拟用户操作的环境。在你看来,QtWebKit缺少哪些功能可以满足我的需求呢? - Piotr Dobrogost
你说这是别人的页面。我可能误解了,但我认为你的意思是离线/跨域?如果我没记错,QtWebkit仍然是沙盒化的,而你所描述的听起来有点像跨站脚本攻击。你不想知道页面何时加载,而是想知道别人的脚本何时执行完毕。我能想到的唯一办法就是观察HTTP状态,以获取数据何时通过请求/响应周期。 - Shaun
也许你是对的,我只是看不到 :) 不过我不确定你所说的跨站脚本是什么意思。这里只有一个网站,我的应用程序以与真实用户相同的方式浏览它。如果您愿意,可以称之为网络爬虫。当您写下“我想知道别人的脚本何时执行完毕”时,您是正确的。我必须知道这一点,因为这会导致数据被下载,而我需要这些数据来处理。更准确地说,js函数仅启动数据下载(这就是ajax的整个思想),我必须知道何时下载了这些数据。 - Piotr Dobrogost

0

原帖作者认为这是由于延迟的 AJAX 请求导致的,但也可能有另一个原因可以解释为什么非常短的时间延迟可以解决问题。存在一个导致所描述行为的错误:

https://bugreports.qt-project.org/browse/QTBUG-37377

为了解决这个问题,必须使用排队连接来连接loadingFinished()信号。

如果我没记错的话,在我的情况下,问题是由于页面加载后立即进行了Ajax调用。然而,由于我们不知道在loadFinished()信号发出之前保证完成了什么,唯一可靠的解决方案是使用Rob Knight建议的window.onload。无论如何,由于QtWebKit的开发已经被QtWebEngine取代,我不会指望有任何改变。 - Piotr Dobrogost
我同意。这个问题不会再被修复了。但是解决方法仍然可能有用。我编辑了我的帖子,说明这是给定症状的另一个可能原因。 - Silicomancer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接