我怎么知道页面已经完成加载?

How can I tell that the page has finished loading?

我正在玩 Chromium 的无头网络浏览器 API。基于 chrome_remote_shell 源代码,我想出了以下代码:

#!/usr/bin/env python

import json
import requests
import pprint
import websocket

tablist = json.loads(requests.get("http://%s:%s/json" % ("localhost", 9222)).text)
print(tablist)
wsurl = tablist[0]['webSocketDebuggerUrl']
conn = websocket.create_connection(wsurl)
navcom = json.dumps({"id":0, "method":"Network.enable"})
conn.send(navcom)
navcom = json.dumps({"id":1, "method":"Page.navigate", "params":{"url":"https://news.ycombinator.com/"}})
conn.send(navcom)

while True:
    packet = json.loads(conn.recv())
    if 'method' in packet:
        print(packet['method'])
    else:
        print(packet)

这是示例输出:

[{u'description': u'', u'title': u'Hacker News', u'url': u'https://news.ycombinator.com/', u'webSocketDebuggerUrl': u'ws://localhost:9222/devtools/page/7d03a57d-77a9-4ceb-b645-3b85461de5be', u'type': u'page', u'id': u'7d03a57d-77a9-4ceb-b645-3b85461de5be', u'devtoolsFrontendUrl': u'/devtools/inspector.html?ws=localhost:9222/devtools/page/7d03a57d-77a9-4ceb-b645-3b85461de5be'}]
{u'id': 0, u'result': {}}
Network.requestWillBeSent
{u'id': 1, u'result': {u'frameId': u'21045.1'}}
Network.responseReceived
Network.dataReceived
Network.dataReceived
Network.loadingFinished
Network.requestWillBeSent
Network.requestWillBeSent
Network.requestServedFromCache
Network.responseReceived
Network.dataReceived
Network.loadingFinished
Network.requestWillBeSent
Network.requestServedFromCache
Network.responseReceived
Network.dataReceived
Network.loadingFinished
Network.requestWillBeSent
Network.requestServedFromCache
Network.responseReceived
Network.dataReceived
Network.loadingFinished
Network.responseReceived
Network.dataReceived
Network.loadingFinished
Network.requestWillBeSent
Network.requestServedFromCache
Network.responseReceived
Network.dataReceived
Network.loadingFinished

我注意到我收到了一长串消息,其中最后一条是 Network.loadingFinished,但我收到了这条针对多个请求 ID 的消息。我如何修改我的脚本,以便它在页面完全加载时终止并且我可以跳出循环?

我不确定 websockets 是如何工作的,但是当你连接到接收数据块的远程服务器时,在套接字上。因此,要接收整个响应,您应该循环执行此操作,直到获得比块长度更小的块,我的意思是当您的块为 4096 字节时,最后一个块将为 0 或 x<4096,其中x 是接收到的块的长度。因此,有了这些信息,您就知道所有数据都是从远程服务器接收到的。请阅读有关套接字的内容。

在任何一般意义上,你不能...不是真的。

现在给定动态网页,您需要了解页面实际在做什么,并寻找某个特定事件/DOM 元素的存在或其他线索。

如您所见,您收到了很多 loadingFinished 事件,但您怎么知道它是 "last" 事件?您需要了解页面。例如,您能否通过观察页面将针对每个特定 DOM 元素 class 或基于 javascript 变量或 XHR 响应发出一个请求来确定将发送多少请求?如果是这样,那么您可以在收到 n 回复后停止。或者,最后一个请求(目标或负载)或最后一个响应是否有一些特殊之处(例如,零长度,包含文本 "last"、^D 或 ^Z)。

此外,如果页面正在轮询服务器(通常使用套接字),"finish loading" 甚至意味着什么?

加载更新

如果您要查找 onload 事件,则无需执行任何特殊操作。 driver.get(<url>) 块直到那时。

WebDriver will wait until the page has fully loaded (that is, the onload event has fired) before returning control to your test or script. It's worth noting that if your page uses a lot of AJAX on load then WebDriver may not know when it has completely loaded. If you need to ensure such pages are fully loaded then you can use waits.

原来我也应该通过Page.enable订阅页面事件:

#!/usr/bin/env python

import json
import requests
import pprint
import websocket
import sys

tablist = json.loads(requests.get("http://%s:%s/json" % ("localhost", 9222)).text)
print(tablist)
wsurl = tablist[0]['webSocketDebuggerUrl']
conn = websocket.create_connection(wsurl)
navcom = json.dumps({"id":0, "method":"Network.enable"})
conn.send(navcom)
navcom = json.dumps({"id":1, "method":"Page.enable"})
conn.send(navcom)
navcom = json.dumps({"id":2, "method":"Page.navigate", "params":{"url":sys.argv[1]}})
conn.send(navcom)

while True:
    s = conn.recv()
    packet = json.loads(s)
    if packet.get('method') == 'Page.loadEventFired':
        break
    print(s)

我们在这里所做的是为页面和网络项目启用通知,然后打开网站并阅读之后发生的所有消息。一旦达到 Page.loadEventFired,我们就可以假设页面已完成加载,此时我们可以退出循环并根据此条件执行任何操作。