VB程序员博客

VB程序开发

我想用vb2008编写一个程序,将从Google calendar导出的.ics文件自动导入到"记忆日" http://www.jiyiri.com/index.aspx 中,后者支持日程按农历循环(Google calendar 中没有),而且支持免费短信提醒,可惜的就是不支持导入导出日程.但我不会用vb写自动提交表单的程序,在网上找到了一些教程,总是遇到各种各样的问题,不知道是vb2008与以前版本的区别造成的还是其他原因,所以想请教各位,如何用vb2008编写自动提交表单的程序?希望代码越简单越好

*******************************************************************************************************************************
=================================================================================================================================

附:参考过的几个例子以及遇到的问题

1.来自 http://topic.csdn.net/t/20020621/16/821289.html,感谢TechnoFantasy((VB MVP)www.applevb.com)
*******************************************************************************
首先在程序中加入Webbrowser控件并加入引用  Microsoft  HTML  Object  Library。 
  假设你的HTML页面表单代码如下: 
  <form  method="POST"  action="http://chen/dll/chat/chatmain.exe/RegUser"> 
      <p>请填写下面表单注册(*项为必添项) </p> 
      <p>*姓名 <input  type="text"  name="Name"  size="20"> </p> 
      <p>*昵称 <input  type="text"  name="NickName"  size="20"> </p> 
      <p>电子邮件 <input  type="text"  name="EMail"  size="20"> </p> 
      <p>*密码 <input  type="text"  name="Password"  size="20"> </p> 
      <p> <input  type="submit"  value="提交"  name="B1"> <input  type="reset"  value="全部重写"  name="B2"> </p> 
  </form> 
  注意其中元素的type、Name、value属性。然后VB中的代码如下: 
  Private  Sub  Command1_Click() 
          WebBrowser1.Navigate  "http://chen/chat/newuser.htm" 
  End  Sub 
 
  Private  Sub  WebBrowser1_DocumentComplete(ByVal  pDisp  As  Object,  URL  As  Variant) 
          Dim  vDoc,  vTag 
          Dim  i  As  Integer 
             
          Set  vDoc  =  WebBrowser1.Document 
          List1.Clear 
          For  i  =  0  To  vDoc.All.length  -  1 
                  If  UCase(vDoc.All(i).tagName)  =  "INPUT"  Then 
                          Set  vTag  =  vDoc.All(i) 
                          If  vTag.Type  =  "text"  Or  vTag.Type  =  "password"  Then 
                                  List1.AddItem  vTag.Name 
                                  Select  Case  vTag.Name 
                                          Case  "Name" 
                                                  vTag.Value  =  "IMGod" 
                                          Case  "NickName" 
                                                  vTag.Value  =  "IMGod" 
                                          Case  "Password" 
                                                  vTag.Value  =  "IMGodpass" 
                                          Case  "EMail" 
                                                  vTag.Value  =  "IMGod@paradise.com" 
                                  End  Select 
                          ElseIf  vTag.Type  =  "submit"  Then 
                                  vTag.Click 
                          End  If 
                  End  If 
          Next  i 
  End  Sub 
  点击Command1就可以自动填表并提交了。   
*************************************************************
遇到的问题:我在调试时,提示"未声明List1"

2.来自http://topic.csdn.net/t/20040818/15/3286897.html,感谢qiyanchao(qiyanchao)
**************************************************************************************************************************
使用VB实现邮箱自动注册(一):表单自动提交 
前些天在网上看到有人需要邮箱自动注册的程序,于是自己也写了一个,在查资料的过程中我看到一些网友写的类似文章,有一些是基于网络协议,需要了解session,cookie这些东西,对于一个新手来说很繁琐,等到把这些东西搞懂,热情也消耗殆尽。VB的优点就在于可以让那些对于计算机底层不是很了解的人迅速实现自己的程序设计,我找到了一种比较简单的办法,要求对HTML标记语言有所了解即可,现在把自己的经验写出来于各位网友共享,该方法也许不值一提,也许对您有所帮助。 
  我的办法就是利用控件WebBrowser,我想很多人对它都很熟悉,在新建一个工程之后您要做的是添加两个控件,一个是WebBrowser,另一个是 Scriptlet,这两个控件配合使用,就可以完成这个任务了,Scriptlet中的IHTMLDocument2对象可以用来获得 WebBrowser控件中的HTML文档。这两个控件的名字在组件对话框里分别叫Microsoft  HTML  Object  Library和Microsoft  Internet  Controls。 
  这两个控件添加完成之后,你还需要知道WebBrowser的一个事件DocumentComplete,这个事件在整个网页下载完成之后触发,也就是说在页面完成之后这个事件即被执行,有了这些就可以顺利的实现注册了。 
  下面以eyou网站的免费邮箱注册为例来实现我的程序设计,新建标准工程后向Form中添加一个WebBrowser控件。所有代码如下: 
  Dim  ii  As  Integer 
 
  Private  Sub  Form_Load() 
        WebBrowser1.Navigate  "http://freemail.eyou.com/signup.html?bgp=%CE%D2%BD%D3%CA%DC&bgp_enable=on" 
  End  Sub 
 
  Private  Sub  WebBrowser1_DocumentComplete(ByVal  pDisp  As  Object,  URL  As  Variant) 
      Dim  doc  As  IHTMLDocument2 
      Set  doc  =  WebBrowser1.Document 
      Dim  tmp  As  String 
 
      If  InStr(doc.body.innerText,  "寻找一个新的用户名")  >  0  Then 
          tmp  =  "sdfsdf23"  +  Trim(Str$(ii)) 
          doc.All.Item("uid").focus 
          SendKeys  tmp 
          SendKeys  "{enter}" 
      End  If 
     
      If  InStr(doc.body.innerText,  "输入您的个人资料")  >  0  Then 
         
          doc.All.Item("Password").Value  =  "123456" 
          doc.All.Item("Confirm").Value  =  "123456" 
          doc.All.Item("FirstName").Value  =  "tomcant" 
          doc.All.Item("question").Value  =  "what  is  your  name" 
          doc.All.Item("answer").Value  =  "my  name  is  ddd" 
          doc.All.Item("year").Value  =  "80" 
          doc.All.Item("month").Value  =  "3" 
          doc.All.Item("day").Value  =  "18" 
          doc.All.Item("day").focus 
          SendKeys  "{enter}" 
    End  If 
 
 
    If  InStr(doc.body.innerText,  "申请邮箱成功")  >  0  Then 
          ii  =  ii  +  1 
          WebBrowser1.Navigate  "http://freemail.eyou.com/signup.html?bgp=%CE%D2%BD%D3%CA%DC&bgp_enable=on" 
    End  If 
 
  End  Sub 
  Eyou的免费邮箱注册入口地址为http://freemail.eyou.com/signup.html?bgp=%CE%D2%BD%D3%CA %DC&bgp_enable=on,所以要在Form_load事件中使用WebBrowser控件的Navigate方法。 
  在eyou的注册入口页面完成之后WebBrowser1_DocumentComplete即被执行,在这个函数中主要有三个条件语句,在三个条件语句之前有如下三句: 
  Dim  doc  As  IHTMLDocument2 
  Set  doc  =  WebBrowser1.Document 
  Dim  tmp  As  String 
  doc定义为IHTMLDocument2对象用以取得WebBrowser1的文档,tmp字符串是循环注册中的用户名,在程序刚开始定义了ii这个integer变量,在每次注册完成之后ii加1附于tmp之后用以改变用户名。 
  第一个条件语句如下: 
  If  InStr(doc.body.innerText,  "寻找一个新的用户名")  >  0  Then 
          tmp  =  "sdfsdf23"  +  Trim(Str$(ii)) 
          doc.All.Item("uid").focus 
          SendKeys  tmp 
          SendKeys  "{enter}" 
      End  If 
  首先判断是否是注册入口页面,通过InStr(doc.body.innerText,  "寻找一个新的用户名")  >  0语句实现,doc.body.innerText即取得了该页面中的所有文字。如果是则构造一个用户名。 doc.All.Item("uid").focus使用户名文本框取得焦点,其中uid是该文本框的ID,这个ID的取得是通过查看HTML源代码得到的,所以要求对HTML标记语言有所了解。使用户名文本框取得焦点之后通过SendKeys函数模拟键盘向文本框发送tmp字符串,这样用户名即填写完成,之后发送回车键提交表单。关于SendKeys函数的使用大家可查阅MSDN帮助。 
  第二个条件语句首先判断改页面是否是个人资料填写页面,然后填写个人资料,其方法同填写用户名一致,取得个文本框的ID后填写其值,最后提交表单。 
  最后一个条件语句判断邮箱注册成功后将ii加一,然后将页面重新定位到注册入口页面,至此一个循环完成。 
  以上代码即实现了自动注册提交。 
**************************************************************************************************************************
遇到的问题:0.我不懂怎么添加scriptlet控件,难道是vb2008中没有?  1.未定义类型 ihtmldocument2  2.SendKeys是一个类型,不能用作表达式

第1个问题中,List1是一个ListBox(列表框)控件,用来存放和显示得到的元素的name,你需要在窗体上放置这个控件并命名为List1。
其他问题因为对VB2008不熟无法回答。

感谢happy_sea的帮助
添加ListBox控件之后,还需要修改一下代码,才能解决这部分的问题:

error BC30456: “clear”不是“System.Windows.Forms.ListBox”的成员
解决方法:把list1.clear 改为 ListBox1.Items.Clear()

error BC30456: “AddItem”不是“System.Windows.Forms.ListBox”的成员。 
解决方法:把List1.AddItem  vTag.Name  改为ListBox1.Items.Add(vTag.Name)

此外,调试时出现:未找到类型“HtmlDocument”的公共成员“length”。
我把vDoc.All.length改为 vDoc.All.Count之后不再出现这个错误,不过我不懂得这样改是否正确,望指教!

解决了以上问题之后,出现了新的问题:

1.调试时,在 If  vTag.Type  =  "text"  Or  vTag.Type  =  "password"  Then 这一行出错
提示:    未找到类型“HtmlElement”的公共成员“type”。

2.我把所有含 vtag.type的语句注释掉,继续调试,在vTag.Value  =  "IMGod" 出错,
提示:      未找到类型“HtmlElement”的公共成员“value”。

1,2 看起来是同一类问题.
这个问题我搞了半天都没解决.

最后,还有一点我不明白, System.Windows.Forms.HtmlElement 好象是只读的,怎么可以对 vtag.value 赋值呢?

谢谢指教!!

上面的问题:
1.调试时,在 If  vTag.Type  =  "text"  Or  vTag.Type  =  "password"  Then 这一行出错
提示:    未找到类型“HtmlElement”的公共成员“type”。

2.我把所有含 vtag.type的语句注释掉,继续调试,在vTag.Value  =  "IMGod" 出错,
提示:      未找到类型“HtmlElement”的公共成员“value”。

我已经解决了,方法是把所有的 vTag.Value 改为vTag.DomElement.Value
vTag.Type 改为 vTag.DomElement.Type
vTag.Click改为 vTag.DomElement.Click()

不过我不是很明白为什么,我是用很笨的方法,在监视里面找vTag的子对象,一个一个找,直到里面有值为"test"的项目,然后替换掉原来的vtagValue,请教一下,应该怎么解释?我想弄明白这个问题,好在写总结的时候解释清楚,方便其他像我一样的新手参考,还望高手指教!

到这里,还剩下最后一个小问题,就是我使用 Private  Sub  WebBrowser1_DocumentComplete(ByVal  pDisp  As  Object,  URL  As  Variant) ,想要在网页加载完毕时触发过程,但是调试时没有反应,这是怎么回事?(之前就遇到这个问题,不过因为不是主要问题,我在后来的调试中将这个过程用一个Button2.Click过程代替,逐步解决了其他的问题,现在就只剩下一个了)

那个 Private  Sub  WebBrowser1_DocumentComplete(ByVal  pDisp  As  Object,  URL  As  Variant) 的问题我也解决了,代码修改为:
Private Sub WebBrowser1_DocumentCompleted(ByVal sender As System.Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted

争取今天把程序写完,然后把代码贴上来,并做个总结.

至于一楼中提到的第二份代码,我还没整明白…不过懒得去调试了,估计解决方法类似.

这个贴满好玩的 楼主一个人回了基本所有问题-_-
看来来晚了哈哈
HtmlElement是个HTML的元素,它不一定是dom模型中的元素
vTag.DomElement才返回了一个标准DOM元素,他包含了所有你需要的属性

顺便多嘴两句……
虽然不懂自动提交表单是什么意思……不过从楼主的代码来看……这部分的内容还是在js里完成最为合适……

谢谢no1gentlebreeze,请问一下,原来的示例代码在vb6或者vb2005中可以通过的吗?是vb本身的变化还是示例代码有问题?好像我看到的例子都是这样的.

如果要用js实现,应该怎么做,可否麻烦举个例子?(这样就不会是我回了基本所有问题了……)

最后一个题外话,我结贴时可以给自己加分吗呵呵?

楼主找出来的代码应该不是VB.NET的。是VB的代码吧

应该是vb的代码,但不是vb2008的.不过我修改过后的代码是vb2008上的.
我又遇到新的困难了:
1. 代码 <a href="javascript:LoginManager.GetInstance().OnOpenLoginPanelBtnClick();">登录 </a>
如何自动登录?

2.我在调试我的半成品时,连续填写两个表单,第一个将会提交失败,原因是前者提交后还没得到响应后者就已经提交了,解决这个问题,除了使用timer控件设置一定的时间间隔,还有没有更好的办法,使程序在确认第一个表单已经提交成功之后才开始填写第二个表单?

谢谢!

上帖的问题1又被我自己解决了…
可以查找标签"a",再找vDoc.All(i).DomElement.href="javascript:LoginManager.GetInstance().OnOpenLoginPanelBtnClick();"
然后 vDoc.All(i).DomElement.Click(),就可以打开登录窗口.
但事实上,不需要打开登录窗口,就可以直接填写并提交登录的表单.

问题2还是没搞定…
不过看样子,我还是得在这里继续自问自答…

奇怪了,不同请求在服务器里应该是分线程操作的呀?前者提交后还没得到响应后者就已经提交这个问题应该不会出现吧……

我又遇上新的麻烦了…
这次是有关下拉菜单的问题
对于  <select name="IndexManager1$AddCommemoration1$JiyiriCalendar1$ddlYear" id="IndexManager1_AddCommemoration1_JiyiriCalendar1_ddlYear" hold="true">
之类的下拉菜单,有什么方法可以得到下拉列表中分别有什么项目?
这个问题比较急切.上面的问题我还没搞清楚,不过这个更重要些.
我知道用  vDoc.All.Item("IndexManager1$AddCommemoration1$commemotitlebox").DomElement.Value = NameValue 之类的语句可以对下拉菜单进行选择,可是在不知道每一项各自代表什么内容的情况下,改如何取得这些项呢?
谢谢!

下拉菜单的问题我解决了…
用 vDoc.All.Item("IndexManager1$AddCommermoration1$JiyiriCalendar1$ddlYear").DomElement.Options(i).Inertext 可以获取两个 Select标签之间的文本内容

现在只剩最后一个问题了,就是连续提交表单发生冲突的问题了,好心人帮帮忙啊…

还是不理解为什么连续提交表单会发生冲突

谢谢no1gentlebreeze

我用三种方法连续提交表单,一种是用了timer控件,每隔2秒提交一个:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Timer1.Enabled = True
        Timer1.Interval = 2000
    End Sub

Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        If JustForTest < LineCount - 1 Then
            SubmitForm(AddConfig, CalendarConfig, ArrayInputValue(JustForTest, NameRow), ArrayInputValue(JustForTest, YearRow), ArrayInputValue(JustForTest, MonthRow), ArrayInputValue(JustForTest, DayRow), ArrayInputValue(JustForTest, CategoryRow), ArrayInputValue(JustForTest, DetailRow))
            JustForTest = JustForTest + 1
        Else
            Timer1.Enabled = False
        End If
    End Sub
********************************************
第二种是把submitform过程放在一个按钮时间中,每按一次button2提交一个表单
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
If JustForTest < LineCount - 1 Then
            SubmitForm(AddConfig, CalendarConfig, ArrayInputValue(JustForTest, NameRow), ArrayInputValue(JustForTest, YearRow), ArrayInputValue(JustForTest, MonthRow), ArrayInputValue(JustForTest, DayRow), ArrayInputValue(JustForTest, CategoryRow), ArrayInputValue(JustForTest, DetailRow))
            JustForTest = JustForTest + 1
        End If
end sub
**********************************************
还有一种是直接用for next 循环,连续提交
for justfortest=1 to LineCount-1
            SubmitForm(AddConfig, CalendarConfig, ArrayInputValue(JustForTest, NameRow), ArrayInputValue(JustForTest, YearRow), ArrayInputValue(JustForTest, MonthRow), ArrayInputValue(JustForTest, DayRow), ArrayInputValue(JustForTest, CategoryRow), ArrayInputValue(JustForTest, DetailRow))
next
*******************************************
*******************************
submitform的内容如下:
private sub submitform()
点击网页上的 "添加生日"按钮
填写表单
点击表单的"保存"按钮
end sub
***********************************

前两种方法提交都没有问题,可以肯定我用来提交表单的submitform过程本身是没有问题的,但是第三种提交方法就出问题了,不管连续提交多少个,都是前面的表单都提交不成功,只有最后一个表单能提交成功.从webbrowser1中显示的内容,第三种方法提交时好像是这样的:
点击"添加生日"
填写表单1
点击"添加生日"
填写表单2
.
.
.
点击"添加生日"
填写表单n
点击"保存"

也就是说,从浏览器显示的过程看,很像是前面n-1次都是只填写没有提交,只有最后一次提交了.

这到底是什么原因呢?有什么方法能在确认上一个表单提交成功之后再填写下一个表单?
有没有可能是网页本身的问题?  我提交表单的网页是 http://www.jiyiri.com/reminder.aspx  "记忆日"

另外一个问题:vb2008有没有类似doevents的事件?我想试试用
do while webbowser1.isbusy=ture
doevents
loop
的方法来解决这个问题.不过有个疑惑,调试时发现提交表单的过程webbrowser1.isbusy始终是false,请问什么情况下webbrowser1.isbusy才是ture?

1.vb2008里面应该没有doevents!但是你完全可以用多线程解决这个问题!
2.vb2008的webbrowser没有用过,不知道!
3.如果电脑资源允许,不妨每次实例化一个webbrowser控件来完成一次的工作!这样应该是肯定没有问题的!
4.我试了一下这个网页!发现它是用ajax的!(也就是网络异步传输)
  有可能它的HTMLHttpRequest对象一直保持和服务器的通信没有关闭,因此导致webbrowser1.isbusy始终是false
5.对于这种网页,最好是循环查找最后的一个成功的提示框来判断是不是完成了一次工作!


标签: , , ,