WebBrowser.Navigate非同步運行,如何使其與其他進程同步?

來源:互聯網
上載者:User

問題描述:有一個網頁列表,準備用webBrowser的navigate一個個去訪問,然後擷取頁面內容。但navigate是非同步,一調用之後,不等待頁面載入完畢,因為訪問網頁需要時間,取決於網速,程式就返回迴圈繼續用下一個網址調用navigate,導致navigate不停的被調用,結果是一個網頁都打不開,只有列表中最後一個列表,因為沒有後續的幹擾能被完整載入。

 

網上發現有人跟我情況一樣,提供的方法能解決問題,引述如下:

-----------------------------------------------------------------------------------------------------------------

net(c#)在迴圈語句中執行WebBrowser.Navigate();方法,每次迴圈等待網頁載入完後繼續執行的解決方案.http://www.cnblogs.com/yangxiaohu1/archive/2009/01/06/1370693.html

 

最近在寫一個小程式的時候,遇到這樣的需求:

          已知一組網頁url地址,想擷取每一個網頁的html,實際上就是想利用迴圈語句裡面使用WebBrowser來載入每一個網頁,然後擷取他們的html,

          要實現這個功能,想想應該是件很簡單的事情,但是在實際操作中卻遇到了問題,因為迴圈語句和WebBrowser的載入不同步的原因,導致前一個

          前一個網頁還沒載入完,下一次迴圈又開始了....最終的結果是WebBrowser只擷取到了最後一個頁面的html.要解決這個問題,我們要做的就是

          讓迴圈執行完前一次後等待網頁載入完,然後執行下一次迴圈去載入下面的網頁.....,按照這個思路,寫了以下程式,經測試果然有效.

bool loading = true;   //該變數表示網頁是否正在載入.
        string html = string.Empty;
        WebBrowser browser = new WebBrowser();

        public void GetHtml(string[] urls)
        {            
            browser.Navigated += new WebBrowserNavigatedEventHandler(browser_Navigated);
            foreach (string url in urls)
            {
                loading = true;  //表示正在載入
                  browser.Navigate(url);

                while (loading)
                {
                    Application.DoEvents();//等待本次載入完畢才執行下次迴圈.
                }
            }
        }

        void browser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
        {
            html = browser.DocumentText;  //擷取到的html.

            loading = false;//在載入完成後,將該變數置為false,下一次迴圈隨即開始執行.
        }
 上面的問題解決了,下面隨之而來的問題是:  有時候載入一張頁面的時候,browser_Navigated會執行多次.

查了下網上的資料,原因是頁面中含有<iframe></iframe>,每一個<iframe>都會觸發一次browser_Navigated,

所以,以上程式可以完善如下:

bool loading = true;   //該變數表示網頁是否正在載入.
        string html = string.Empty;
        WebBrowser browser = new WebBrowser();

        public void GetHtml(string[] urls)
        {            
            browser.Navigated += new WebBrowserNavigatedEventHandler(browser_Navigated);
            foreach (string url in urls)
            {
                loading = true;  //表示正在載入
                browser.Navigate(url);

                while (loading)
                {
                    Application.DoEvents();//等待本次載入完畢才執行下次迴圈.
                }
            }
        }

        int i = 0;
        void browser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
        {
            i++;
            if (i % 3 == 0) // 假設每張頁面要執行3次browser_Navigated方法,那麼這表示網頁全部內容載入完成.(至於這個3要怎麼樣得到,那是仁者見仁的事情了,呵呵)
            {
                html = browser.DocumentText;  //擷取到的html.

                loading = false;//在載入完成後,將該變數置為false,下一次迴圈隨即開始執行.
            }
        }
-----------------------------------------------------------------------------------------------------------------

 

這位網友在Navigated事件中將loading設為false,而我則改在documentcompleted事件中來判斷頁面是否載入完畢。當然頁面載入完畢的判斷要考慮多種情況,如多個frame,對我所處理的情況,用documentcompleted事件比較合適。

下面是My Code概況:

 

bool loading = true;

 

while (articleUrl.Count > 0)//列表不為空白
{
       loading = true;
       string url = articleUrl.Dequeue();
       webBrowser1.Navigate(url);
        while (loading)
       {
           Application.DoEvents();
       }
}

 

 

 private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            if (webBrowser1.ReadyState == WebBrowserReadyState.Complete)//判斷載入完畢
            {
                ............

                loading = false;
            }
        }

 

 

另外,資料量大時,Application.DoEvents()的效率,所以這個問題應該用線程同步來解決,或者所謂委託回調。這個還沒試過。下面是另一網友對Application.DoEvents()的效能測試

 

-----------------------------------------------------------------------------------------------------------------

關於Application.DoEvents()

記得第一次使用Application.DoEvents()是為了在載入大量資料時能夠有一個資料載入的提示,不至於系統出現假死的現象,當時也沒有深入的去研究他的原理是怎樣的,結果在很多地方都用上了Application.DoEvents(),今天看到了關於這方面的一些文章,知道我以前有些用法是不當的,有些地方需要慎用Application.DoEvents()。
首先我們先看看在迴圈比較大的程式中,它的作用還是不錯的,起到了一個即時響應的效果,例如:

如果沒有加上 DoEvents的話,由於迴圈時間會比較久就會出現假死的狀態,而且程式不能處理其他的事件。而如果加上DoEvents的話就會對文字框的值即時響應,給使用者帶來較好的使用者體驗,可是DoEvents也帶來了效率上的問題,處理同樣的一個事件調用了DoEvents後效率降低了好幾倍,這也是為什麼要慎用的原因了。下面是我做的一個測試:

for (int q = 0; q < 1000000; q++)
            {
                textBox1.Text = q.ToString();
                Application.DoEvents();//即時響應文字框中的值
           }

 

private void button1_Click(object sender, EventArgs e)
        {
            expendTime.start();
            for (int q = 0; q < 100000; q++)
            {
                textBox1.Text = q.ToString();
                Application.DoEvents();
            }
            label2.Text = expendTime.ComputerTime();//計算耗時
        }

        private void button2_Click(object sender, EventArgs e)
        {
            expendTime.start();
            for (int q = 0; q < 100000; q++)
            {
                textBox2.Text = q.ToString();
            }
            label3.Text = expendTime.ComputerTime();//計算耗時
        }

執行耗時對比:
從較大資料的迴圈中可以看出效率是很低的,所以如果能不調用DoEvents就盡量不用。也可以通過別的方法來處理的,例如多線程非同步呼叫等。
MSDN中的定義:
當運行   Windows   表單時,它將建立新表單,然後該表單等待處理事件。該表單在每次處理事件時,均將處理與該事件關聯的所有代碼。所有其他事件在隊列中等待。在代碼處理事件時,應用程式並不響應。例如,當將另一視窗拖到該視窗前面時,該視窗不重新繪製。如果在代碼中調用   DoEvents,則您的應用程式可以處理其他事件。例如,如果您有向   ListBox   添加資料的表單,並將   DoEvents   添加到代碼中,那麼當將另一視窗拖到您的表單上時,該表單將重新繪製。如果從代碼中移除   DoEvents,那麼在按鈕的單擊事件處理常式執行結束以前,您的表單不會重新繪製。   
 通常,您在迴圈中使用該方法來處理訊息。
具體可參考這裡。

 

-----------------------------------------------------------------------------------------------------------------

解決這個問題,我走了很多彎路,去看了WebBrowser的Activex 控制項,用axwebBrowser代替webBrowser重寫了一遍代碼,但兩者的navigate都是非同步。後來想用線程同步來解決誒,因為時間緊,不熟悉,所以沒弄成。現在湊巧用DoEvents解決了。但最終我想還是 要用線程同步來解決的。比如用訊號量,比如ManualResetEvent.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.