【问题标题】:How can I tell that the page has finished loading?如何判断页面已完成加载?
【发布时间】:2017-09-18 19:51:07
【问题描述】:

我正在使用 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,但我收到了多个 requestId 的消息。如何修改我的脚本,使其在页面完全加载时终止并且我可以逃脱循环?

【问题讨论】:

    标签: python google-chrome google-chrome-devtools headless headless-browser


    【解决方案1】:

    原来我也应该通过 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,我们可以假设页面已经完成加载,此时我们可以退出循环并执行任何依赖于此条件的操作。

    【讨论】:

    • 你能解释一下这里发生了什么吗?网络套接字?为什么?
    • @Fandango68 这是 - 或者至少是因为我没有检查当前状态 - 在无头模式下与 Chrome 通信的标准方式。
    • 谢谢,但我仍然不明白这是如何等待页面加载的。您提到的与 page.enable 相关的代码在哪里?我再次要求您解释代码。
    • @Fandango68 我刚刚添加了一些解释。这是否解决了您的问题?如果不是,请尝试尽可能详细地解释您的困惑来自何处。
    【解决方案2】:

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

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

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

    另外,如果页面正在轮询服务器(通常使用套接字),“完成加载”是什么意思?

    onload 更新

    如果您正在寻找onload 事件,您无需做任何特别的事情。 driver.get(<url>) 阻止到那时。

    WebDriver 将等待页面完全加载(即onload 事件已触发),然后再将控制权返回给您的测试或脚本。值得注意的是,如果您的页面在加载时使用了大量 AJAX,那么 WebDriver 可能不知道它何时完全加载。如果您需要确保此类页面已完全加载,则可以使用等待。

    【讨论】:

    • 谢谢 pbuck,我也是这么想的。我基本上想抓住普通浏览器隐藏旋转“加载”符号的那一刻,表示页面加载结束。 Chrome 会以某种方式暴露它吗?
    • 不是标准的onload 事件? Spec 说:“在文档加载完成时在窗口触发”,Mozilla 说,“在文档加载过程结束时触发。此时,文档中的所有对象都在 DOM 中,所有图像,脚本、链接和子框架已完成加载。”
    • 是的,问题是如何从 headless chrome 获取这个事件。
    • onload 本质上是内置于 driver.get() 中(我已经用此信息更新了答案。)但它可能还不足以满足您的要求。
    • 请记住,我的意思是 webtools API,而不是更高级别的 selenium 的 WebDriver。无论如何,我找到了答案:stackoverflow.com/a/43554418/1091116
    【解决方案3】:

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

    【讨论】:

    • 很抱歉,但事实并非如此。我正在与一个无头浏览器的 API 交谈,它向我提供了关于它如何发出大量请求的信号。我的问题是我应该如何确定所有请求都已收到。
    • 那么我不确定你想要什么。如果可以的话,我的第一个想法是设置超时。你能指出你的代码中的哪一行吗?
    • 问题在于 DevTools 界面。我告诉远程浏览器启用网络调试并导航到某个 URL。我还需要告诉它让我知道它什么时候会触发“加载”事件,这样我就可以抓住它并逃离循环。
    • 对不起,我不能帮你,因为我缺乏知识:/
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多