我想为阿列克谢的回答贡献一些代码。几点:严格地说,可能无法始终确定页面何时以100%的概率完成渲染。有些页面非常复杂,并且使用连续的AJAX更新。但是我们可以通过轮询页面的当前HTML快照以获取更改并检查WebBrowser.IsBusy属性来完全接近。这就是 LoadDynamicPage下面的内容。
如果页面呈现永无止境(注意CancellationTokenSource),则必须在上面提供一些超时逻辑。
Async/await 是一个很好的编码工具,因为它为我们的异步轮询逻辑提供了线性代码流,大大简化了它。
使用浏览器功能控件启用HTML5渲染非常重要,因为WebBrowser默认情况下在IE7仿真模式下运行。这就是SetFeatureBrowserEmulation下面的内容。
这是一个WinForms应用程序,但该概念可以很容易地转换为控制台应用程序。
using Microsoft.Win32;using System;using System.ComponentModel;using System.Diagnostics;using System.Threading;using System.Threading.Tasks;using System.Windows.Forms;namespace WbFetchPage{
public partial class MainForm : Form
{
public MainForm()
{
SetFeatureBrowserEmulation();
InitializeComponent();
this.Load += MainForm_Load;
}
// start the task
async void MainForm_Load(object sender, EventArgs e)
{
try
{
var cts = new CancellationTokenSource(10000); // cancel in 10s
var html = await LoadDynamicPage("https://www.google.com/#q=where+am+i", cts.Token);
MessageBox.Show(html.Substring(0, 1024) + "..." ); // it's too long!
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
// navigate and download
async Task LoadDynamicPage(string url, CancellationToken token)
{
// navigate and await DocumentCompleted
var tcs = new TaskCompletionSource();
WebBrowserDocumentCompletedEventHandler handler = (s, arg) =>
tcs.TrySetResult(true);
using (token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: true))
{
this.webBrowser.DocumentCompleted += handler;
try
{
this.webBrowser.Navigate(url);
await tcs.Task; // wait for DocumentCompleted
}
finally
{
this.webBrowser.DocumentCompleted -= handler;
}
}
// get the root element
var documentElement = this.webBrowser.Document.GetElementsByTagName("html")[0];
// poll the current HTML for changes asynchronosly
var html = documentElement.OuterHtml;
while (true)
{
// wait asynchronously, this will throw if cancellation requested
await Task.Delay(500, token);
// continue polling if the WebBrowser is still busy
if (this.webBrowser.IsBusy)
continue;
var htmlNow = documentElement.OuterHtml;
if (html == htmlNow)
break; // no changes detected, end the poll loop
html = htmlNow;
}
// consider the page fully rendered
token.ThrowIfCancellationRequested();
return html;
}
// enable HTML5 (assuming we're running IE10+)
// more info: https://stackoverflow.com/a/18333982/1768303
static void SetFeatureBrowserEmulation()
{
if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
return;
var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION",
appName, 10000, RegistryValueKind.DWord);
}
}}