C# 如何调用WebBrowser Navigate浏览多个URL?

C# 如何调用WebBrowser Navigate浏览多个URL?,c#,winforms,webbrowser-control,webclient,async-await,C#,Winforms,Webbrowser Control,Webclient,Async Await,要收集网页上的信息,我可以使用WebBrowser.Navigated事件 首先,导航到url: WebBrowser wbCourseOverview = new WebBrowser(); wbCourseOverview.ScriptErrorsSuppressed = true; wbCourseOverview.Navigate(url); wbCourseOverview.Navigated += wbCourseOverview_Navigated; 然后在调用“导航”时处理网页

要收集网页上的信息,我可以使用WebBrowser.Navigated事件

首先,导航到url:

WebBrowser wbCourseOverview = new WebBrowser();
wbCourseOverview.ScriptErrorsSuppressed = true;
wbCourseOverview.Navigate(url);
wbCourseOverview.Navigated += wbCourseOverview_Navigated;
然后在调用“导航”时处理网页:

void wbCourseOverview_Navigated(object sender, WebBrowserNavigatedEventArgs e)
    {
        //Find the control and invoke "Click" event...
    }
当我尝试遍历URL的字符串数组时,最困难的部分出现了

foreach (var u in courseUrls)
        {
            WebBrowser wbCourseOverview = new WebBrowser();
            wbCourseOverview.ScriptErrorsSuppressed = true;
            wbCourseOverview.Navigate(u);

            wbCourseOverview.Navigated += wbCourseOverview_Navigated;
        }
在这里,由于页面加载需要时间,因此永远不会到达wbCourseOverview\u Navigated

我尝试在C5中使用异步等待。任务和基于事件的异步模式EAP可在中找到。另一个例子可以在中找到

问题是WebClient具有类似DownloadDataAsync和DownloadStringAsync的异步方法。但WebBrowser中没有NavigateAsync

有专家能给我一些建议吗?多谢各位

StackOverflow中有一个帖子。但是,有人知道如何在答案中实现这一点吗

再次更新

在,

在我的web浏览器访问第二个url之前,它看起来不错

试图在任务已完成时将其转换为最终状态

我知道我一定是在foreach循环中出错了。因为DocumentCompleted事件在循环到第二轮时未引发。在foreach循环中写入此wait的正确方法是什么?

不要使用wbCourseOverview\u导航使用webBrowser1\u DocumentCompleted在第一次URL加载完成时完成您的工作并转到下一个URL

List<string> urls = new List<string>();
    int count = 0;
    public Form1()
    {
        InitializeComponent();
        webBrowser1.DocumentCompleted+=new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        webBrowser1.Navigate(urls[count++]);
    }

    private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        //Do something
        webBrowser1.Navigate(urls[count++]);
    }
不要使用wbCourseOverview\u导航,而是在第一次URL加载完成时使用webBrowser1\u DocumentCompleted并转到下一个URL

List<string> urls = new List<string>();
    int count = 0;
    public Form1()
    {
        InitializeComponent();
        webBrowser1.DocumentCompleted+=new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        webBrowser1.Navigate(urls[count++]);
    }

    private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        //Do something
        webBrowser1.Navigate(urls[count++]);
    }
这里有一个邮局。但是,有人知道如何在答案中实现这一点吗

好的,那么您需要一些带有waiter的代码。我已经编写了两段代码。 第一个使用TPL的内置等待器:

 public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ProcessUrlsAsync(new[] { "http://google.com", "http://microsoft.com", "http://yahoo.com" })
                .Start();
        }

        private Task ProcessUrlsAsync(string[] urls)
        {
            return new Task(() =>
            {
                foreach (string url in urls)
                {
                    TaskAwaiter<string> awaiter = ProcessUrlAsync(url);
                    // or the next line, in case we use method *
                    // TaskAwaiter<string> awaiter = ProcessUrlAsync(url).GetAwaiter();                     
                    string result = awaiter.GetResult();

                    MessageBox.Show(result);
                }
            });
        }        

        // Awaiter inside
        private TaskAwaiter<string> ProcessUrlAsync(string url)
        {
            TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
            var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
            {
                // TODO: put custom processing of document right here
                taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
            });
            webBrowser1.DocumentCompleted += handler;
            taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });

            webBrowser1.Navigate(url);
            return taskCompletionSource.Task.GetAwaiter();
        }

        // (*) Task<string> instead of Awaiter
        //private Task<string> ProcessUrlAsync(string url)
        //{
        //    TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
        //    var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
        //    {
        //        taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
        //    });
        //    webBrowser1.DocumentCompleted += handler;
        //    taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });

        //    webBrowser1.Navigate(url);
        //    return taskCompletionSource.Task;
        //}
下一个示例包含Eric Lippert提到的waiter结构的示例实现

希望这有帮助

这里有一个邮局。但是,有人知道如何在答案中实现这一点吗

好的,那么您需要一些带有waiter的代码。我已经编写了两段代码。 第一个使用TPL的内置等待器:

 public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ProcessUrlsAsync(new[] { "http://google.com", "http://microsoft.com", "http://yahoo.com" })
                .Start();
        }

        private Task ProcessUrlsAsync(string[] urls)
        {
            return new Task(() =>
            {
                foreach (string url in urls)
                {
                    TaskAwaiter<string> awaiter = ProcessUrlAsync(url);
                    // or the next line, in case we use method *
                    // TaskAwaiter<string> awaiter = ProcessUrlAsync(url).GetAwaiter();                     
                    string result = awaiter.GetResult();

                    MessageBox.Show(result);
                }
            });
        }        

        // Awaiter inside
        private TaskAwaiter<string> ProcessUrlAsync(string url)
        {
            TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
            var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
            {
                // TODO: put custom processing of document right here
                taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
            });
            webBrowser1.DocumentCompleted += handler;
            taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });

            webBrowser1.Navigate(url);
            return taskCompletionSource.Task.GetAwaiter();
        }

        // (*) Task<string> instead of Awaiter
        //private Task<string> ProcessUrlAsync(string url)
        //{
        //    TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
        //    var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
        //    {
        //        taskCompletionSource.SetResult(e.Url + ": " + webBrowser1.Document.Title);
        //    });
        //    webBrowser1.DocumentCompleted += handler;
        //    taskCompletionSource.Task.ContinueWith(s => { webBrowser1.DocumentCompleted -= handler; });

        //    webBrowser1.Navigate(url);
        //    return taskCompletionSource.Task;
        //}
下一个示例包含Eric Lippert提到的waiter结构的示例实现



希望这有帮助。

Ha!这个答案让我再次感到愚蠢。为什么我必须坚持我的foreach循环?!在标记答案之前,我会等待更多的答案,只是想看看是否有人提供了一些异步等待解决方案,因为我真的很想知道。但是谢谢你IRSOG的快速回复。@Blaise:WebBrowser类在.NET中的异步/等待实现有一个方法:检查一下,我已经在那篇文章中担任主角了。但是你知道如何实现这种支撑吗?我以前没有尝试过,但也看到了这一点:我尝试使用上一篇文章中提供的解决方案。但也有一些困难。请在原始帖子中查看我的更新。谢谢,哈!这个答案让我再次感到愚蠢。为什么我必须坚持我的foreach循环?!在标记答案之前,我会等待更多的答案,只是想看看是否有人提供了一些异步等待解决方案,因为我真的很想知道。但是谢谢你IRSOG的快速回复。@Blaise:WebBrowser类在.NET中的异步/等待实现有一个方法:检查一下,我已经在那篇文章中担任主角了。但是你知道如何实现这种支撑吗?我以前没有尝试过,但也看到了这一点:我尝试使用上一篇文章中提供的解决方案。但也有一些困难。请在原始帖子中查看我的更新。谢谢。你想从WebBrowser控件中删除HTML吗?如果是这样的话,这是一种非常低效的方法,因为加载所有图像、JavaScript和插件会带来大量开销。您可以自己处理HTTP请求,然后对响应进行处理。如果您阅读了链接到的文章,那么您应该能够使用Navigate、Navigated和TaskCompletionSource自己构建NavigateAsync。@CameronTinker,我需要的不仅仅是HTML。我想要的是在一些DOM控件上调用Click事件。所以我不会只使用DownloadStringTaskAsyc。@svick,我认为你指向了一个好的方向。这就是我想做的。你能给我更多的指示吗?为什么你建议我使用Navigated而不是DocumentCompleted来构建NavigateEasyc?@Blaise我建议使用Navigared只是因为你在原始代码中使用了Navigared。你是否试图从WebBrowser控件中删除HTML?如果是这样的话,这是一种非常低效的方法,因为加载所有图像、JavaScript和插件会带来大量开销。您可以自己处理HTTP请求,然后对响应进行处理。如果您阅读了链接到的文章,那么您应该能够使用Navigate、Navigated和TaskCompletionSource自己构建NavigateAsync。@CameronTinker,我需要的不仅仅是HTML。我想要的是调用Click事件o
n一些DOM控件。所以我不会只使用DownloadStringTaskAsyc。@svick,我认为你指向了一个好的方向。这就是我想做的。你能给我更多的指示吗?为什么你建议我使用Navigated而不是DocumentCompleted来构建NavigateEasyc?@Blaise我建议使用NavigateGared只是因为这是你在原始代码中使用的。谢谢你的帮助。在这个解决方案中,您似乎使用了wait或async关键字。你有什么理由避开他们吗?在这种情况下,它们不容易实现吗?这个答案非常详细。现在我至少有了一个有效的例子。谢谢你,谢谢你的帮助。在这个解决方案中,您似乎使用了wait或async关键字。你有什么理由避开他们吗?在这种情况下,它们不容易实现吗?这个答案非常详细。现在我至少有了一个有效的例子。非常感谢。
public partial class Form1 : Form
    {
        public struct WebBrowserAwaiter
        {
            private readonly WebBrowser _webBrowser;
            private readonly string _url;

            private readonly TaskAwaiter<string> _innerAwaiter;

            public bool IsCompleted
            {
                get
                {
                    return _innerAwaiter.IsCompleted;
                }
            }

            public WebBrowserAwaiter(WebBrowser webBrowser, string url)
            {
                _url = url;
                _webBrowser = webBrowser;
                _innerAwaiter = ProcessUrlAwaitable(_webBrowser, url);
            }

            public string GetResult()
            {
                return _innerAwaiter.GetResult();

            }

            public void OnCompleted(Action continuation)
            {
                _innerAwaiter.OnCompleted(continuation);
            }

            private TaskAwaiter<string> ProcessUrlAwaitable(WebBrowser webBrowser, string url)
            {
                TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
                var handler = new WebBrowserDocumentCompletedEventHandler((s, e) =>
                {
                    // TODO: put custom processing of document here
                    taskCompletionSource.SetResult(e.Url + ": " + webBrowser.Document.Title);
                });
                webBrowser.DocumentCompleted += handler;
                taskCompletionSource.Task.ContinueWith(s => { webBrowser.DocumentCompleted -= handler; });

                webBrowser.Navigate(url);
                return taskCompletionSource.Task.GetAwaiter();
            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ProcessUrlsAsync(new[] { "http://google.com", "http://microsoft.com", "http://yahoo.com" })
                .Start();
        }

        private Task ProcessUrlsAsync(string[] urls)
        {
            return new Task(() =>
            {
                foreach (string url in urls)
                {
                    var awaiter = new WebBrowserAwaiter(webBrowser1, url);
                    string result = awaiter.GetResult();

                    MessageBox.Show(result);
                }
            });
        }
    }   
        }