3

生成された HTML の画像をキャプチャする必要があります。ここから Alex Filipovici の優れたソリューションを使用しています: Convert HTML string to image。Javascriptを使用してロードするiframeを持つページをロードしようとしている場合を除いて、うまく機能します。

        静的 int 幅 = 1024;
        静的 int 高さ = 768;

        public static void Capture()
        {
            var html = @"
<!DOCTYPE html>
<meta http-equiv='X-UA-Compatible' content='IE=Edge'>
<html>
<iframe id='forecast_embed' type='text/html' frameborder='0' height='245' width='100%' src='http://forecast.io/embed/#lat=42.3583&lon=- 71.0603&name=ボストンのダウンタウン'> </iframe>
</html>
";
            StartBrowser(html);
        }

        private static void StartBrowser(文字列ソース)
        {
            var th = 新しいスレッド(() =>
            {
                var webBrowser = new WebBrowser();
                webBrowser.Width = 幅;
                webBrowser.Height = 高さ;
                webBrowser.ScrollBarsEnabled = false;
                webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;
                webBrowser.DocumentText = ソース;
                Application.Run();
            });
            th.SetApartmentState(ApartmentState.STA);
            th.Start();
        }

        static void webBrowser_DocumentCompleted(オブジェクト送信者, WebBrowserDocumentCompletedEventArgs e)
        {
            var webBrowser = (WebBrowser)送信者;
            using (Bitmap bitmap = new Bitmap(幅, 高さ))
            {
                webBrowser.DrawToBitmap(ビットマップ、新しい System.Drawing.Rectangle(0, 0, 幅, 高さ));
                bitmap.Save(@"image.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
            }
            Application.Exit();
        }

すべての JavaScript が終了したかどうか、iframe の読み込みの気まぐれと、DocumentCompleted get がフレーム/iframe + 1 の数だけ呼び出されるという事実を知る決定的な方法はおそらくないことを理解しています。iframe の読み込みをカウンターで処理できます。または何か、しかし私が望むのは合理的な遅延だけなので、javascriptがロードされ、次のように「ロード中」の画像が表示されません: http://imgur.com/FiFMTmm

4

2 に答える 2

3

フレームと AJAX を多用する動的な Web ページを扱っている場合、特定のページがリソースの読み込みを完了したときを見つける完全な解決策はありません。次の2つのことを行うことで、近づくことができます。

  • ページの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、タイマーを使用して同じロジックを実装できます。

于 2013-10-15T07:39:21.563 に答える
0

これは、複雑になり、競合状態が発生したり、.Net 4.5が必要になったりする他のさまざまなアイデアを何度もいじった後、私が使用しているものです(この質問への回答など)。

秘訣は、DocumentCompleted ごとにストップウォッチを再起動し、特定のしきい値内で完了したドキュメントがなくなるまで待機することです。

使いやすくするために、拡張メソッドに入れました。

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;
            }
        }
    }
}
于 2014-02-06T06:01:56.480 に答える