我正在使用 .NET WebBrowser 控件。我如何知道网页何时已完全加载?

我想知道浏览器何时不再获取更多数据。(当 IE 在状态栏中写下“完成”时......)。

笔记:

  • 对于包含多个框架的网站,DocumentComplete/NavigateComplete 事件可能会发生多次。
  • 浏览器就绪状态也不能解决问题。
  • 我尝试检查帧集合中的帧数,然后计算获得 DocumentComplete 事件的次数,但这也不起作用。
  • this.WebBrowser.IsBusy 也不起作用。在文档完成处理程序中检查时它始终为“假”。
有帮助吗?

解决方案 4

这最终对我有用:

       public bool WebPageLoaded
    {
        get
        {
            if (this.WebBrowser.ReadyState != System.Windows.Forms.WebBrowserReadyState.Complete)
                return false;

            if (this.HtmlDomDocument == null)
                return false;

            // iterate over all the Html elements. Find all frame elements and check their ready state
            foreach (IHTMLDOMNode node in this.HtmlDomDocument.all)
            {
                IHTMLFrameBase2 frame = node as IHTMLFrameBase2;
                if (frame != null)
                {
                    if (!frame.readyState.Equals("complete", StringComparison.OrdinalIgnoreCase))
                        return false;

                }
            }

            Debug.Print(this.Name + " - I think it's loaded");
            return true;
        }
    }

在每个文档完成事件中,我都会运行所有 html 元素并检查所有可用的框架(我知道它可以优化)。对于每一帧,我检查其就绪状态。它非常可靠,但就像 jeffamaphone 所说的那样,我已经看到触发一些内部刷新的网站。但上面的代码已经满足我的需求了。

编辑:每个帧都可以包含其中的帧,因此我认为应该更新此代码以递归地检查每个帧的状态。

其他提示

我做某事的方法 当页面完全加载时 (包括框架)是这样的:

using System.Windows.Forms;
    protected delegate void Procedure();
    private void executeAfterLoadingComplete(Procedure doNext) {
        WebBrowserDocumentCompletedEventHandler handler = null;
        handler = delegate(object o, WebBrowserDocumentCompletedEventArgs e)
        {
            ie.DocumentCompleted -= handler;
            Timer timer = new Timer();
            EventHandler checker = delegate(object o1, EventArgs e1)
            {
                if (WebBrowserReadyState.Complete == ie.ReadyState)
                {
                    timer.Dispose();
                    doNext();
                }
            };
            timer.Tick += checker;
            timer.Interval = 200;
            timer.Start();
        };
        ie.DocumentCompleted += handler;
    }

从我的其他方法中,我学到了一些“不要”:

  • 不要试图弯曲勺子...;-)
  • 不要尝试使用 DocumentComplete、Frames、HtmlWindow.Load 事件构建复杂的构造。如果你的解决方案真的有效的话,它将会很脆弱。
  • 不要使用 System.Timers.Timer 代替 Windows.Forms.Timer, ,如果您这样做,奇怪的错误将开始出现在奇怪的地方,因为计时器在与应用程序的其余部分不同的线程上运行。
  • 不要只使用没有 DocumentComplete 的 Timer,因为它可能会在页面开始加载之前触发,并会过早执行您的代码。

以下是我在应用程序中解决问题的方法:

private void wbPost_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    if (e.Url != wbPost.Url)
        return;
    /* Document now loaded */
}

这是我测试过的版本。只需将此作为您的 DocumentCompleted Event Handler 并放置您只想调用的代码 一次 进入方法 OnWebpageReallyLoaded(). 。实际上,这种方法可以确定页面何时稳定 200 毫秒,然后执行其操作。

// event handler for when a document (or frame) has completed its download
Timer m_pageHasntChangedTimer = null;
private void webBrowser_DocumentCompleted( object sender, WebBrowserDocumentCompletedEventArgs e ) {
    // dynamic pages will often be loaded in parts e.g. multiple frames
    // need to check the page has remained static for a while before safely saying it is 'loaded'
    // use a timer to do this

    // destroy the old timer if it exists
    if ( m_pageHasntChangedTimer != null ) {
        m_pageHasntChangedTimer.Dispose();
    }

    // create a new timer which calls the 'OnWebpageReallyLoaded' method after 200ms
    // if additional frame or content is downloads in the meantime, this timer will be destroyed
    // and the process repeated
    m_pageHasntChangedTimer = new Timer();
    EventHandler checker = delegate( object o1, EventArgs e1 ) {
        // only if the page has been stable for 200ms already
        // check the official browser state flag, (euphemistically called) 'Ready'
        // and call our 'OnWebpageReallyLoaded' method
        if ( WebBrowserReadyState.Complete == webBrowser.ReadyState ) {
            m_pageHasntChangedTimer.Dispose();
            OnWebpageReallyLoaded();
        }
    };
    m_pageHasntChangedTimer.Tick += checker;
    m_pageHasntChangedTimer.Interval = 200;
    m_pageHasntChangedTimer.Start();
}

OnWebpageReallyLoaded() {
    /* place your harvester code here */
}

当帧完成时,在每个帧中使用 javascript 设置一个标志,然后让 C# 查看这些标志怎么样?

我没有其他选择,但我想知道是否 IsBusy 财产是 true 在文档完成处理程序期间是因为该处理程序仍在运行,因此 WebBrowser 从技术上讲,控制仍处于“忙碌”状态。

最简单的解决方案是有一个循环,每 100 毫秒左右执行一次,直到 IsBusy 标志被重置(出现错误时有最大执行时间)。这当然假设 IsBusy 不会被设置为 false 在页面加载期间的任何时刻。

如果文档完成处理程序在另一个线程上执行,您可以使用锁使主线程进入睡眠状态并将其从文档完成线程中唤醒。然后检查 IsBusy flag,重新锁定主线程仍然是 true.

我不确定它是否有效,但尝试在框架集上添加 JavaScript“onload”事件,如下所示:

function everythingIsLoaded() { alert("everything is loaded"); }
var frameset = document.getElementById("idOfYourFrameset");
if (frameset.addEventListener)
    frameset.addEventListener('load',everythingIsLoaded,false); 
else
    frameset.attachEvent('onload',everythingIsLoaded); 

你能使用 jQuery 吗?然后,您可以轻松地将帧就绪事件绑定到目标帧上。看 回答指示。这 博客文章 也有相关讨论。最后有一个 插入 你可以使用的。

这个想法是使用以下方法计算网页中的框架数量:

$("iframe").size()

然后计算 iframe 就绪事件被触发的次数。

您将获得外部网页以及每个框架的 BeforeNavigate 和 DocumentComplete 事件。当您收到外部网页的 DocumentComplete 事件时,您就知道您已经完成了。您应该能够使用托管的等效项 IWebBrowser2::TopLevelContainer() 来确定这一点。

但请注意,网站本身可以随时触发更多的框架导航,因此您永远不知道页面是否真正永远完成。您能做的最好的事情就是记录您看到的所有 BeforeNavigates 的计数,并在获得 DocumentComplete 时减少计数。

编辑:这是托管文档: 顶级容器.

我只是使用 webBrowser.StatusText 方法。当它说“完成”时,一切都已加载!或者我错过了什么?

检查 IE.readyState = READYSTATE_COMPLETE 应该可以,但如果这对您来说不可靠,并且您确实想知道“IE 在状态栏中写入“完成”的那一刻”,那么您可以执行循环,直到 IE.StatusText 包含“完毕”。

你有没有尝试过 WebBrowser.IsBusy 财产?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top