浏览器自动化的一些体会2 webBrowser控件之ajax

上个帖子简要讨论了浏览器自动化的几种方法。现在讨论webBrowser控件使用中的一些问题。基本的操作就不详细说了,随便网上找个帖子或找本书都有介绍的。这里只写点网上似乎少有人总结过的内容,以及自己的一些实践体会。

1.ajax

首先,DocumentCompleted事件对于ajax无能为力,因为这个事件不能处理网页加载完成后执行javascript发出ajax请求。网上可以找到的方法,主要有两种,一种是利用onpropertychange事件,随便找了个参考链接:https://social.msdn.microsoft.com/Forums/ie/en-US/5fe3e7a1-b3c7-4083-9a00-7a72bf833a9c/ajax-detection-in-webbrowser-control?forum=ieextensiondevelopment , 另外一种就是用timer。

实践中的体会是onpropertychange也不好用,timer比较好使。timer在这里的本质就是延时。我一般有两种做法,一种是在DocumentCompleted事件处理代码里启动timer

即:

private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
      timer1.Stop();
      timer1.Start();
}

这里先Stop再Start不过是个人的一个习惯,很多情况下没必要。但是如果一个timer已经启动了,则需要先Stop再Start。

另一种是在启动webbrowser.Navigate的代码里启动timer:

private void button1_Click(object sender, EventArgs e)
{
        webBrowser1.Navigate(“foo.com");
        timer1.Stop();
        timer1.Start();
}

个人更喜欢第2种方式,理由是DocumentCompleted里面尽量不用很多代码,主要原因是有些情况下DocumentCompleted事件会被多次触发,所以如果没有正确处理好这种情况,有些代码就会被重复执行,造成意想不到的结果。但是,后一种做法也有缺点,因为不是在DocumentCompleted事件触发后再启动timer,如果网页加载较慢,timer可能会不必要地启动多次。所以还是要看情况处理。

timer1_Tick里的代码大致如此:

private int _waitCount = 0;

private void timer1_Tick(object sender, EventArgs e)
{
      //先stop一般没有什么坏处,而且有些情况下是必要的,比如这里的操作所需时间超过了
      //timer1的间隔(Interval),如果没有stop可能出现意想不到的结果。
      timer1.Stop();
      if (webBrowser1.Document == null
              || webBrowser1.Document.Body == null
              || webBrowser1.Document.GetElementById("foo") == null)
      {
           if (_waitCount > 3)
           { 
                 MessageBox.Show("网络有问题,请换个时间再试");
                 return;
           }
           _waitCount++;
           timer1.Start();
           return;//继续等
      }

      _waitCount = 0;//复原
      //“foo”元素检测到,说明ajax请求已返回想要的结果,可以继续下一步处理
      ..............
}

这里用了一个_waitCount的全局变量,目的是为了避免网络有问题,永远加载不完,导致timer代码被反复触发,进入死循环。尝试4次,如果还不行,就提示失败。这里其实牵涉到浏览器自动化的一个重要问题:出错处理。因为网络情况无法预料,所以只有包含良好出错处理的程序,才比较健壮(robust)。网上很多程序,表面看起来work,但遇到出乎意外的网络状况时都会出错,就是因为出错处理做得不好的缘故。这个具体以后再讨论。

前面的if判断条件,如果用新版framework的?语法糖,或许还可写得简单些,这里就不改了。

实践中感到timer的方式主要问题是如果网页里的ajax过多,就需要很多timer,代码显得有点乱。既然timer在这里的作用是延时,那么应该也可用其他方式。

最简单的延时方式,就是Thread.Sleep()。缺点很明显,就是阻塞了主线程,造成假死机现象,用户体验不好。于是有人改良,可参考这个链接:

https://stackoverflow.com/questions/3794522/waiting-for-webbrowser-ajax-content

它用了Application.DoEvents()来避免阻塞。但是,Application.DoEvents()使用要小心,参考这个链接:

https://stackoverflow.com/questions/5181777/use-of-application-doevents

我的理解是,如果允许Application.DoEvents()的时间过长,就容易给用户有机会关掉应用等操作,有可能造成意想不到的结果。而在处理ajax请求时,有可能要等上几秒,几秒是显得有些长了。因此Application.DoEvents()也许在这里用,并不是太好的做法。

另外一种延时方式,虽然并不局限于浏览器自动化里的应用,但既然谈到了,不妨也说一下。这大概是游戏程序里用的较多的,就是类似下面的代码:

var start = Environment.TickCount;
while (Environment.TickCount - start < 100) //延时100毫秒
{
       Application.DoEvents();
}

写博客很费时间。webBrower控件还有许多要谈的问题,以后再写。

posted @ 2018-01-07 02:54  平静寄居者  阅读(1065)  评论(0编辑  收藏  举报