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.对于这种网页,最好是循环查找最后的一个成功的提示框来判断是不是完成了一次工作!