【问题标题】:WebBrowser Control DocumentCompleted after iframe & Javascript completioniframe 和 Javascript 完成后 WebBrowser Control DocumentCompleted
【发布时间】:2013-10-22 16:57:42
【问题描述】:

我需要捕获生成的 HTML 的图像。我在这里使用 Alex Filipovici 的出色解决方案:Convert HTML string to image。它工作得很好,除非我尝试加载具有使用某些 Javascript 加载的 iframe 的页面。

静态 int 宽度 = 1024; 静态 int 高度 = 768; 公共静态无效捕获() { 变量 html = @" "; 开始浏览器(html); } 私有静态无效StartBrowser(字符串源) { var th = 新线程(() => { var webBrowser = new WebBrowser(); webBrowser.Width = 宽度; webBrowser.Height = 高度; webBrowser.ScrollBarsEnabled = false; webBrowser.DocumentCompleted += webBrowser_DocumentCompleted; webBrowser.DocumentText = 源; 应用程序.运行(); }); th.SetApartmentState(ApartmentState.STA); th.Start(); } 静态无效 webBrowser_DocumentCompleted(对象发送者,WebBrowserDocumentCompletedEventArgs e) { var webBrowser = (WebBrowser)sender; 使用(位图位图 = 新位图(宽度,高度)) { webBrowser.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, width, height)); bitmap.Save(@"image.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); } 应用程序.Exit(); }

我知道可能没有明确的方法可以知道所有 javascript 是否已结束,iframe 加载的变幻莫测以及 DocumentCompleted get 的调用次数与帧/iframe + 1 一样多。我可以处理 iframe 加载有一个计数器或其他东西,但我想要的是一个合理的延迟,所以加载了 javascript,我没有得到像这样的“加载”的图像:http://imgur.com/FiFMTmm

【问题讨论】:

    标签: c# .net winforms web-scraping webbrowser-control


    【解决方案1】:

    如果您正在处理大量使用框架和 AJAX 的动态网页,则没有完美的解决方案可以找到特定页面何时完成加载资源。你可以通过做以下两件事来接近:

    • 处理页面的window.onload事件;
    • 然后异步轮询WebBrowser Busy 属性,并带有一些预定义的合理短超时。

    例如,(查看https://stackoverflow.com/a/19283143/1768303 以获取完整示例):

    const int AJAX_DELAY = 2000; // non-deterministic wait for AJAX dynamic code
    const int AJAX_DELAY_STEP = 500;
    
    // wait until webBrowser.Busy == false or timed out
    async Task<bool> AjaxDelay(CancellationToken ct, int timeout)
    {
        using (var cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
        {
            cts.CancelAfter(timeout);
            while (true)
            {
                try
                {
                    await Task.Delay(AJAX_DELAY_STEP, cts.Token);
                    var busy = (bool)this.webBrowser.ActiveXInstance.GetType().InvokeMember("Busy", System.Reflection.BindingFlags.GetProperty, null, this.webBrowser.ActiveXInstance, new object[] { });
                    if (!busy)
                        return true;
                }
                catch (OperationCanceledException)
                {
                    if (cts.IsCancellationRequested && !ct.IsCancellationRequested)
                        return false;
                    throw;
                }
            }
        }
    }
    

    如果您不想使用async/await,您可以使用计时器实现相同的逻辑。

    【讨论】:

    • 您会在我的代码中的何处添加计时器?对我来说,一个简单的延迟就足够了。
    • @naveed,您可以将webBrowser_DocumentCompleted 事件处理程序的签名更改为async static void webBrowser_DocumentCompleted...。然后在webBrowser_DocumentCompleted 中添加await Task.Delay(1000) 作为第一行。或者,不使用async/await,在webBrowser_DocumentCompleted 中创建一个计时器,并将所有逻辑从webBrowser_DocumentCompleted 移到计时器的事件处理程序中。在任何情况下都需要注意一件事,DocumentCompleted 可以为同一个文档多次触发(因为帧)。使用静态标志变量来缓解这种情况。
    • 没问题,很高兴它有帮助。
    【解决方案2】:

    这是我在与各种其他想法混在一起之后一直在使用的东西,这些想法最终变得复杂并且有竞争条件或需要 .Net 4.5(例如这个问题的答案)。

    诀窍是在每个 DocumentCompleted 上重新启动 Stopwatch 并等待直到在某个阈值内没有完成任何文档。

    为了方便使用我加入了一个扩展方法:

    browser.NavigateAndWaitUntilComplete(uri);
    

    我应该将它命名为 NavigateUntilProbablyComplete()。这种方法的缺点是每次导航都会有 250 毫秒的延迟。我见过的许多解决方案都依赖于最终页面与我的场景中无法保证的 url 相同。

    using System;
    using System.Diagnostics;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace MyProject.Extensions
    {
        public static class WebBrowserExtensions
        {
            const int CompletionDelay = 250;
    
            private class WebBrowserCompletionHelper
            {
                public Stopwatch LastCompletion;
    
                public WebBrowserCompletionHelper()
                {
                    // create but don't start.
                    LastCompletion = new Stopwatch();
                }
    
                public void DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
                {
                    WebBrowser browser = sender as WebBrowser;
                    if (browser != null)
                    {
                        LastCompletion.Restart();
                    }
                }
            }
    
            public static void NavigateAndWaitUntilComplete(this WebBrowser browser, Uri uri)
            {
                WebBrowserCompletionHelper helper = new WebBrowserCompletionHelper();
                try
                {
                    browser.DocumentCompleted += helper.DocumentCompleted;
                    browser.Navigate(uri);
    
                    Thread.Sleep(CompletionDelay);
                    Application.DoEvents();
    
                    while (browser.ReadyState != WebBrowserReadyState.Complete && helper.LastCompletion.ElapsedMilliseconds < CompletionDelay)
                    {
                        Thread.Sleep(CompletionDelay);
                        Application.DoEvents();
                    }
                }
                finally
                {
                    browser.DocumentCompleted -= helper.DocumentCompleted;
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-02
      • 2014-01-01
      • 2012-05-30
      • 2017-06-08
      • 2011-05-29
      • 1970-01-01
      • 2012-09-05
      相关资源
      最近更新 更多