上个帖子简要讨论了浏览器自动化的几种方法。现在讨论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控件还有许多要谈的问题,以后再写。
原文地址:https://www.cnblogs.com/badnumber/p/8221801.html