VB.net自动签到机的实现——以CG资源网为例

来源:互联网 发布:电脑免流软件 编辑:程序博客网 时间:2024/05/01 22:58

其中几个重要问题的研究

 

这个论坛(http://www.cgtsj.com/)的登录系统比较简陋,没有验证码,所以只要用WebBrowser控件的SetAttribute自动填写text就可以搞定。但还有另一个方法,可能适用性更广些——使用Cookies。这样即使是要验证码的论坛(比如小木虫)登录,用户也只需登录一次,记录下网站反馈的Cookies,下次把这些数据feed给WebBrowser就能绕过验证码了。

我建了个名为wb的WebBrowser控件,只要多次调用“Debug.Print(wb.Document.Cookie)”就能在调试窗口看到这个网页在不同阶段都往Cookies里写了什么。我们不妨来研究一下: 一开始的内容是“Hm_lvt_925…de6=146…137,146…175,…; Hm_lpvt_925…de6=146…632; PHPSESSID=b0b…qn6”,这里前两个主要记录访问信息,最后一个是当前会话ID。

然后在网页内输用户名密码登录,记得要勾上“记住”选项,不然Cookies不会有变化。可以发现多了两项:“ck_n01=s…o; ck_m01=f…2;”第一个是用户名;第二个记录的是密码,但论坛也不会傻到用明文记录,所以做了些许的encoding。可这不是我们关注的重点,只要你不换密码,这个ck_m01的值是固定的。为此,我还做了个小实验:把我另一个小号的密码调成和这个号一样,最后发现ck_m01的值相同。这进一步说明了这个假设的正确性。

最后应用这个Cookies并模拟Click对应的签到按钮即可。

01. wb.Navigate("http://www.cgtsj.com/user/index.php")

02. While wb.ReadyState <> 4 Or wb.IsBusy ’ 页面打开时等待

03.     My.Application.DoEvents()

04. End While

05. wb.Document.Cookie = "ck_n01=***;" ’ 填用户名

06. wb.Document.Cookie = "ck_m01=***;" ’ 填密码代号

07. wb.Navigate("http://www.cgtsj.com/user/index.php")

08. While wb.ReadyState <> 4 Or wb.IsBusy

09.     My.Application.DoEvents()

10.End While

11.wb.Document.All("qiandao").InvokeMember("click")

必须先打开某段Cookies对应的“Domain”网站,才能且只能对该站内的Cookies进行操作,所以这里Navigate了两次。这里再补充说一句,第七行的Navigate替换为Refresh()好似是会有问题的,因为程序会默认浏览器不在加载状态,所以立刻会执行下一条语句(直接跳过While段);但这时候含签到按钮的页面还没有加载出来,所以会跳错。

事情到此貌似已经结束了?假设你在运行这段程序访问这个网站,你会发现网页跳出了一段错误信息:“JSON未定义”。找到错误提示对应的行号,发现有一段“var sj=JSON.stringify(jj)”,而这个JSON在上下文从来没有定义过是什么(看来浏览器没骗人)。其实这个是高版本(从IE 8开始)浏览器一个自带模块的内容,而对比较古老的版本则需要额外引用一个“json2.js”文件。你也许会纳闷,自己用的是Win 10系统,应该是IE 11了才对,但别忘了,WebBrowser和IE并不是完全挂钩的,它其实采用的是IE的低版本(应该是IE 6)兼容模式。为了让它以正常模式运行,MSDN给出了一个解决方案。

打开注册表

HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)

    SOFTWARE

       Microsoft

          Internet Explorer

             Main

                FeatureControl

                   FEATURE_BROWSER_EMULATION

                      contoso.exe = (DWORD) 00000000

其中的"contoso.exe"为您的程序名字,即嵌入了WebBrowser控件的可执行程序的名字。

后面的数值"00000000"代表WebBrowser控件使用的IE版本,值对应的IE版本如下表:”

Value

Description

11001 (0x2AF9)

Internet Explorer 11. Webpages are displayed in IE11 edge mode, regardless of the declared !DOCTYPE directive. Failing to declare a !DOCTYPE directive causes the page to load in Quirks.

11000 (0x2AF8)

IE11. Webpages containing standards-based !DOCTYPE directives are displayed in IE11 edge mode. Default value for IE11.

10001 (0x2711)

Internet Explorer 10. Webpages are displayed in IE10 Standards mode, regardless of the !DOCTYPE directive.

10000 (0x02710)

Internet Explorer 10. Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode. Default value for Internet Explorer 10.

9999 (0x270F)

Windows Internet Explorer 9. Webpages are displayed in IE9 Standards mode, regardless of the declared !DOCTYPE directive. Failing to declare a !DOCTYPE directive causes the page to load in Quirks.

9000 (0x2328)

Internet Explorer 9. Webpages containing standards-based !DOCTYPE directives are displayed in IE9 mode. Default value for Internet Explorer 9.

Important  In Internet Explorer 10, Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode.

8888 (0x22B8)

Webpages are displayed in IE8 Standards mode, regardless of the declared !DOCTYPE directive. Failing to declare a !DOCTYPE directive causes the page to load in Quirks.

8000 (0x1F40)

Webpages containing standards-based !DOCTYPE directives are displayed in IE8 mode. Default value for Internet Explorer 8

Important  In Internet Explorer 10, Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode.

7000 (0x1B58)

Webpages containing standards-based !DOCTYPE directives are displayed in IE7 Standards mode. Default value for applications hosting the WebBrowser Control.

所以只要在注册表中添加对应exe文件的名称,设置值为0x2AF8就可以了。但在VS里调试还是会有问题的,这里需要对VS做一些小的配置更改:把调试模式从Debug改为Release,并在工程设置的“调试”选项卡中,“启用调试器”栏目下禁用“启用Visual Studio宿主进程”一项。该调整在Visual Studio 2008及2010下通过。

这样所有的功能似乎已经完备了,但我们其实还可以走得更远。假设我有多个账号,都想自动签到呢?有人会说,清一下Cookies,打一段“wb.Document.Cookie.Delete(0wb.Document.Cookie.Count - 1)”就可以了吧?但事实上不行,首先,仅更改Cookies内容并刷新貌似并不能登入另一个账号,此时需要清除本会话(Session)的内容(记起刚刚提到的PHPSESSID了吗?);可即便清除了Session,登上了另一个号,网页还会提醒“您的其他账号今天已经签过到了”。真是个良心网站!但对于我们的程序而言,当然不希望有这样的障碍产生。究其原因,是因为有些Cookies是设置了矫顽性(persistence)的,Cookie.Delete方法去除不掉;比如说这个网站在其中一个号签完到后强制增添一个“最后签到时间”Cookie(qd_time=146…091),就是如此幸运的是,某个名为InternetSetOptionWin32API函数(都64位系统了不知道为啥还这么叫…)能实力解决这个问题。这次,MSDN又帮上了忙:

  * INTERNET_OPTION_SUPPRESS_BEHAVIOR (Const = 81):

  *   A general purpose option that is used to suppress behaviors on a process-wide basis.

  *   The lpBuffer parameter of the function must be a pointer to a DWORD containing the specific behavior to suppress.

  *   This option cannot be queried with InternetQueryOption.

  *  

  * INTERNET_SUPPRESS_COOKIE_PERSIST (Const = 3):

  *   Suppresses the persistence of cookies, even if the server has specified them as persistent.

  *   Version: Requires Internet Explorer 8.0 or later.

  *  

  * INTERNET_OPTION_END_BROWSER_SESSION(Const = 42):

  *   Flushes entries not in use from the password cache on the hard disk drive. Also resets the cache time used when the synchronization mode is once-per-session. No buffer is required for this option. This is used by InternetSetOption.

 

第一个是清Cookies的flag,第三个对应清除Session。第二个常数设置了无视矫顽性的属性;注意说明里还提到:只有IE 8以上才支持!真是幸运我们刚才已经解决了这个问题。

在代码一开始声明引用API:

01.Private Declare Function InternetSetOption Lib "wininet.dll" Alias "InternetSetOptionW" (ByVal hInternet As IntPtr, ByVal dwOption As Long, ByRef lpBuffer As IntPtr, ByVal dwBufferLength As Long) As Long

调用的时候这么写:

01.InternetSetOption(IntPtr.Zero, 81New IntPtr(3), Runtime.InteropServices.Marshal.SizeOf(0)) ’ Cookies(最后一长串是在获知储存一个Integer所需的比特数)

02. InternetSetOption(IntPtr.Zero, 42, IntPtr.Zero, 0’ Session

还有一件比较minor的事,那就是作为一个我希望开机自启自动签到的机器,我不希望它在加载页面的时候发出咔嗒咔嗒的怪声。这个也是好解决的,可以使用Win32API函数CoInternetSetFeatureEnabled。声明:

01.Private Declare Function CoInternetSetFeatureEnabled Lib "urlmon.dll"(ByVal FeatureEntry AsLong, ByVal dwFlags AsLong, ByVal fEnable AsLong) As Long 

然后在窗体加载时调用(常量FEATURE_DISABLE_NAVIGATION_SOUNDS值为21,意为禁止跳转声音;常量SET_FEATURE_ON_PROCESS值为2,意为仅对当前进程有效;1代表True):

01.Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

02.    CoInternetSetFeatureEnabled(21, 21)

02.End Sub

 

最后说明一点,这个实例可以应用到大部分论坛的自动登录/签到。要改的部分仅仅是Cookies的内容和要模拟点击的按钮。前者可通过一次登录获得其值;或者,如果有“记住”选项,这一部分甚至不用写进代码里。后者可以通过在按钮上方右键“检查元素”,从网页源代码中得知其ID,NAME,或执行的javascript。第一种情况,可以通过“wb.Document.All("id here")”或“wb.Document.GetElementByID("id here")”找到控件。第二种情况,虽然javascript有GetElementsByName,但貌似wb.Document集合里没有?难道要遍历wb.Document?其实多打一个.All就行了。用wb.Document.All.GetElementsByName("name here")得到的是一个数组,但一般叫这个名字的也就一个控件,找到它只要在后面加“(0)”取得第一个元素就可以了。第三种情况,直接wb.Navigate("javascript:js here")即可。

 



0 0
原创粉丝点击