HTML - 我如何知道所有框架何时加载?
题
我正在使用 .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
财产是 tru
e 在文档完成处理程序期间是因为该处理程序仍在运行,因此 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);
您将获得外部网页以及每个框架的 BeforeNavigate 和 DocumentComplete 事件。当您收到外部网页的 DocumentComplete 事件时,您就知道您已经完成了。您应该能够使用托管的等效项 IWebBrowser2::TopLevelContainer() 来确定这一点。
但请注意,网站本身可以随时触发更多的框架导航,因此您永远不知道页面是否真正永远完成。您能做的最好的事情就是记录您看到的所有 BeforeNavigates 的计数,并在获得 DocumentComplete 时减少计数。
编辑:这是托管文档: 顶级容器.
我只是使用 webBrowser.StatusText 方法。当它说“完成”时,一切都已加载!或者我错过了什么?
检查 IE.readyState = READYSTATE_COMPLETE 应该可以,但如果这对您来说不可靠,并且您确实想知道“IE 在状态栏中写入“完成”的那一刻”,那么您可以执行循环,直到 IE.StatusText 包含“完毕”。
你有没有尝试过 WebBrowser.IsBusy
财产?