【目标】
对于普通网页操作的话,登陆:
会跳转到类似于这样的,另外的一个页面:
输入用户名和密码后,就可以登陆skydrive中,而且网页地址又跳转到:
了,然后可以看到并操作自己的所有的(共享出来的和自己私有)的文件了。
其中,多解释一句:
而如果不登陆skydrive,而只是知道自己的skydrive的主要链接地址,类似于这样的:
https://skydrive.live.com/?cid=9A8B8BF501A38A36
也是可以查看skydrive中共享出来的文件的,但是是没有权限操作的。
必须先登陆,才可以操作的。
而此处就是想要实现登陆后的,查看和操作对应的文件和文件夹。
所以,此处目的就是:
用C#代码,实现模拟登陆skydrive的操作:
先登陆:
再跳转到:
然后在给定了用户名和密码的前提下,提交对应的http请求,以实现登陆网站,然后获得登陆后的skydrive中所返回的内容。
对于此套流程,我作为用户的话,看起来,那肯定很简单,但是此处需要去使用我自己很不熟悉的C#,尤其是http相关的分析工作,还是不太容易的,不过经过努力,最终还是搞定了,实现了此功能。
而此文,目的在于将这个可以说是九曲十八弯的分析过程,尽量记录下来,一则后背可查,记录经验,二则算是给其他有需要的人一些参考吧,或者说,也算是搜人鱼不如授人以鱼吧,减少其他人再走类似的弯路。
【分析如何通过C#来实现模拟登陆skydrive网站的过程】
【开始之前,多说几句,为何要用C#代码,调用对应http相关的库,去实现模拟登陆skydrive网站,而不是走捷径,用webbrowser控件等方式】
最开始打算用c#的代码去实现这套逻辑的时候,无意间发现一个C#(.Net)中有一个webbrowser的控件,简单尝试了一下,还是很方便好用的,直接给了地址,就可以像一个小浏览器一样,打开网页了,而另外如果花点时间去实现与该控件交互,传递用户名和密码等参数,然后获得对应的网页返回值,这套逻辑的话,虽然我没花时间,但是也知道相对还是很简单的,虽然可以实现我要的功能,但是却不利于搞懂浏览器内部的逻辑,这样以后再遇到分析其他网站的话,那么还是不懂,所以,不利于进步,从此点意义上来说,必须要硬着头皮去努力尝试分析网页执行的内部流程和机制,搞懂之前那么多只是知道名词的东西,比如http的request header,response,cookie,cache等等,以及后来才知道和熟悉的IE9和Chrome的开发人员工具等,总之这套东西,是要搞懂的,才能为以后做类似的东西做铺垫。
而对于上面网页抓取的逻辑,其实之前已经接触一点点了,也实现了对应的skydrive不登陆情况下的网页内容的抓取,解析,其对应的就是之前刚实现的WLW(Windows Live Writer)的一个plugin插件,InsertSkydriveFiles,实现在不登陆skydrive的情况下,抓取分析网页,解析后,用listview实现浏览对应的文件和文件夹,选择文件后,可以插入,比如图片,音乐等。这也是我一直所需要的功能,但是没有找到可以且好用的工具,被逼自己写的插件。
另外,对于网页抓取方面,之前就实现过wordpress博客搬家BlogsToWordpress,以及用Python脚本去获得skydrive上文件的真实地址链接 + 提供脚本下载,所以上面插件开发过程中,也算多少对网页抓取和分析,有点了概念,但是此处对于网页登陆问题,其实就是之前就没有搞定的事情,这次既然遇到类似问题,正好顺便一起给解决了。
【模拟登陆网站相关的背景知识】
在要通过C#实现模拟登陆skydrive之前,就已经去查找了一些资料。
但是所得有效的资料,相对很有限,只是介绍了大概思路,那就是,
分析网页内容,然后再找到要分析的网站,点击对应的登陆等按钮的时候,其所提交的http请求是什么,
如果是GET类型的,那么就是url地址中包含了对应的用户名和密码,
如果是POST类型的,那就是对应的http请求中,还需要添加对应的post数据。
至于如何分析出来这部分的内容,就只找到了某人介绍的,关于分析一个页面的html源码中,找到对应的,类似于
username,password等元素,然后在GET或POST的时候,赋值,提交,即可。
其他的更深入的,关于如何利用工具去分析出来更加复杂的网页的内部数据是如何填充的,需要填充哪些数据,都没有人解释。
而卧下面要介绍的,就是相关的,相对来说,很复杂的skydrive的模拟登陆问题,因为如果想要模拟登陆此网站之前,除了分析登陆页面的html源码,找到对应的username和password之外,还要搞懂对应的cookie,user-agent等复杂的内容,更甚者,还遇到了目前还不知道具体原因的某个诡异问题,导致实际上即使获得了所需要的数据,但是读取该数据的时间太长,本来应该是毫秒级别的,其读取的时间变成了好多分钟,以至于完全死掉的感觉。
诸如此类,想要用C#实现模拟登陆skydrive,反正是需要对于网页分析工具,比如IE9的F12和Chrome的开发人员工具等的熟练使用之外,还要懂得和http协议相关的众多内容,包括request相关的redirect,user-agent,cookie等,response相关的cookie,甚至是还需要一定的运气,否则,即使代码都对了,但是结果也会遇到上面说的怪问题,导致假死,让你以为程序写错了。。。
不过话说回来,现在真的把这条路走通了,对于以后去处理其他类似的模拟网站登陆的问题,算是涨了足够的经验,基本上算是有了足够的基础,以解决这类问题了。
关于用何工具去分析网页,抓取网页内容,可参考这里:
【总结】浏览器中的开发人员工具(IE9的F12和Chrome的Ctrl+Shift+I)-网页分析的利器
【关于如何用C#实现模拟登陆skydrive的详细的分析过程】
1.从https://skydrive.live.com/到login.srf的cbcxt=sky
此处是从
跳转到:
而对于此步骤的动作,看起来很简单,但是分析过程,很是费了一番功夫。
因为之前,关于网页跳转的原理,只是知道是通过在提交http的request的参数中,设置为允许自动重定向,在C#中即为:
req.AllowAutoRedirect = true;
即可让http的库自动判断网页是否跳转,如果其本身http请求在处理过程中,自动跳转了,那么程序返回的结果,也就是跳转之后的内容了。
但是,此处对于访问https://skydrive.live.com/的结果却是,始终发现返回的地址,不是期望的那个cbcxt=sky地址,却还是原先的https://skydrive.live.com/。
接着就是痛苦的一番折腾,最后终于发现根本原因了:
原来此处网页跳转,是通过返回的内容中,包含了对应的refresh动作,而实现的:
<meta http-equiv="refresh" content="2;url=https://login.live.com/login.srf?wa=wsignin1.0&rpsnv=11&ct=1327988927&rver=6.1.6206.0&wp=MBI_SSL_SHARED&wreply=https:%2F%2Fskydrive.live.com%2F&lc=1033&id=250206&mkt=en-US&cbcxt=sky" />
知道了原理,就很容易实现了,即先模拟提交http请求:
然后对其返回的html解析,而得到对应的地址,即可。
总体来说,用C#模拟此步骤,还是相对不是很麻烦的。
2.从login.srf的cbcxt=sky到resourcespreload.mvc
此步骤是从:
跳转到:
https://skydrive.live.com/handlers/resourcespreload.mvc?view=Folders.All&id=250206&mkt=ZH-CN
对于此过程,其实是费了千辛万苦后才得知的。
因为,本来的理解,根据抓取的网页,觉得很简单,应该是直接接着跳转去访问:
而已。但是用去调试了半天,得到的返回值中,对应的cookie值中的xidseq始终是1,而不是抓取的网页中得到的xidseq=2,所以去继续找原因,后来无意间通过chrome中开发工具中看到,对于cbcxt=sky之后,原来还有个resourcespreload.mvc的http请求,其返回的http的response中,包含的cookie中,有对应的Set-Cookie:xidseq=2,所以,就又去模拟登陆resourcespreload.mvc。
此步骤,实现起来容易,但是对于分析出来此过程,还是很不容易的。
尤其是开始对于cookie,是一点都不了解,看着就头大,只是由于后来分析抓取到的网页的详细信息,然后一点点看,看的多了,自然就稍微熟悉了,也才看出来是可能和cookie相关的,加上一定的细心和运气,才找到对应的http请求的。
3.从resourcespreload.mvc到login.srf的cbcxt=sky&bk=1328070683
此步骤是从:
https://skydrive.live.com/handlers/resourcespreload.mvc?view=Folders.All&id=250206&mkt=ZH-CN
到
而对于post.sr的cbcxt=sky这个地址,其实是从最开始,就很容易分析出来了。
只是由于对于此地址,提交对应的http的post请求之前,就
需要填充一堆的的http的query string之外,还要填充对应的post data。
其中query string,已经在url地址中有了。
对应于这样的地址:
可以分解为:
https://login.live.com/login.srf
?
wa=wsignin1.0
&rpsnv=11
&ct=1327974680
&rver=6.1.6206.0
&wp=MBI_SSL_SHARED
&wreply=https:%2F%2Fskydrive.live.com%2F
&lc=2052
&id=250206
&mkt=zh-CN
&cbcxt=sky
对应的query string,即为上面那一个个参数和对应的值。
而对于post data,就是我们要提交的,关于用户名是啥,密码是啥,以及相关附加的一些其他数据。而对于用户名和密码,那是很容易搞定,自己输入即可,但是对于其他的附加数据,则是非常繁琐。
此处post相关数据,可以从分析工具中抓取到,如下:
login:[email protected]
passwd:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
SI:登录
type:11
LoginOptions:3
NewUser:1
MEST:
PPSX:Passp
PPFT:CqAfP!GnoRX8ALLmW80K0CQCqXBf2cMRg*etLkexUd6zxjWLov4DdyKA4lLobzNiS9bQ4klYEQfBBc987!mxYRajGNhBMG*dmY2tlZfHepxPa9K2dmOkZ1fDteo8HezP7se4!p5Ngu47eSqxYaeh7dxkpnFDNTmDHO*osw2Y5dJ1Wt*0iUigCTiBMR0s!ZdTXkPIIxkUwSd8qItI7XVlyjP0ot5LY2kx108PScO!xZ7VyM4ZiinNEKA7c1bsbGyGQQ$$
idsbho:1
PwdPad:
sso:
i1:
i2:1
i3:617226
i4:
i12:1
i13:
i14:104
i15:907
i16:1140
i17:
经过多次分析,得知,上述中的post的参数,除了用户名和密码之外,对于PPSX,PPFT,i3,i14,i15,i16,这些值,是需要经过分析计算而得的,其他都是固定的值。
所以,问题到此,看起来,就是转为化如何计算对应的这些值了。
此问题,看起来不复杂,但是实际上很麻烦,因为要涉及到,去分析那些javascript源文件,看懂其中的程序的逻辑,搞懂这些值是从何计算出来的。
而如果是正常的程序源码的话,那么再复杂,也不是很难分析,但是此处的javascript源码,是那种压缩后的,即普通的变量,函数名等,全部都是最精简的一个字母,两个字母这种的,目的是为了减少访问网络锁产生的数据量,但是这样的代码,明显不是留给人看的。而其中,如果是简单的几个函数,之前也自己分析过,还是可以慢慢看懂的。但是,此处是那种涉及到N多函数的,所以是,再好的分析能力,都没辙。
最后,经过努力,算是发现了IE9的F12开发工具中,有javascript的debug功能,然后用了对应的debug去实时分析,才搞懂上面那些值,是如何计算出来的。
但是问题又来了,这些值,有些是关于计算整个网络下载很多个资源文件所消耗的时间的,所以,如果你真的想要精确计算出来对应的时间值,就得完全真实模拟整个浏览器的操作,即一个个下载对应的文件,最后计算对应的值等等,而且除此之外,还发现,如果这么做的话,还有对应的cookie的值中,还包含对应的多少个文件被cache,如此等等,让人完全头大,实在很难模拟。
只是,后来,暂时对于上述一些值,先直接以浏览器抓取出来的值去赋值,先用着,感觉应该不会是这些值不精确,而导致的无法获得对应的response。
解决了query string和暂时模拟了此处的post的data之外,还有很多问题要解决。
主要就是,对于此处的cookie,还是要保证此处通过代码提交的所有内容,包括提交的cookie,都和网页所抓取出来的内容所一致,这样才可能正常登陆网站,返回我们所要的值。
而对于cookie,就是上面所提及的,除了正常的http的response中所获得cookie的值之外,后来发现,另外有些cookie值,却是另外通过javascript生成的,主要是这几个:
CkTst=G1328193719193
wlidperf throughput=10&latency=283
wl_preperf req=131&com=131&cache=129
而对于这些cookie,还是要自己手动去计算的,但是却发现,其中的wl_preperf中的cache,却是无法精确模拟的,否则就需要去一个个下载对应文件,并且模拟javascript中的逻辑去计算所消耗的时间,以及有多少被cache了,才能得到最后的这个cache=129,而且另外对于wlidperf,也是类似的问题。
总之,好像都是非常难以精确模拟,所以索性都暂时先以工具分析出来的一组数据,先用着再说。
等实在绕不过去,始终无法获得我所需要的返回的html内容,到时候再回来去模拟计算出对应的值。
分析过程到了这里,遇到的问题是,
即使都完全模拟了对应的http请求,包括已经提交了正确的url,正确的query string,正确的post data,和cookie,但是就是无法获得和抓取内容所一致的cookie,因为获得cookie中,包含了对应的authentication认证信息,而此处代码获得返回的cookie中,一直都没有cookie信息,所以,很明显是没有成功登陆,再加上没有获得对应返回期望的html源码所以,一直都以为整个过程出了错。
最后,通过两点,才知道,其实前面过程是对的。
一是无意间发现,原来最后提交的整个url,其实是内部过程包含了重定向的,即,本身此处去模拟访问:
给定了正确的值,其内部是认证了之后,重新定向到:
的,
其中,下图是正常没有重定向的:
这个是debug发现,原来此处是重定向的:
而重定向之后所得到的cookie的值,是没有包含对应的带认证信息的cookie的值的。
二是,调试期间,无意间发现,原来访问上述地址,最后是有html源码返回的,而且是包含了所希望的primedResponse信息的。
而之前从网页分析的记过来看,由于没看出来是重定向,所以看到的结果,就正好是没有response data的,而程序debug的结果,也正好是在
sr = new StreamReader(resp.GetResponseStream());
之后,再去通过
string srfUpostHtml = sr.ReadToEnd();
获得返回的html源码,就会死掉,是很长时间没有反应的那种死掉,所以就以为是,没有返回值的话,去读取返回值,就会死掉的,所以也符合逻辑。
最后哪知道,原来是由于sr.ReadToEnd()所读取的内容太大,消耗时间太长所导致的。
要知道,本来正常返回的html源码的话,大概也就几十K,此函数的执行时间,也就毫秒级别的就可以了,debug过程中,单步执行,瞬间就可以完成的,而此处由于而内容太大,后经计算,有4,5M的内容,后经查实,其中间,包含了4M多的全部是“\0”的字符串,而至于为何返回这么多的“\0”的无效数据,其原因至今为止。
个人猜测,好像是库函数问题,但是也只是猜测而已。
说到此,已经知道了,原来最后一步的http请求,原来是带自动跳转的,结果也就是,本来要介绍的步骤:
从login.srf的cbcxt=sky&bk=1328070683再回到https://skydrive.live.com/
就不存在了,因为其是自动重定向,而不是单独发起的http请求。
到此为止,整个过程就over了。
【小结】
其实,即使上面写的已经够多的内容,但是对于整个分析过程所遇到的问题,还只是一部分。
而如果想要了解从无到有如何分析skydrive模拟登陆的过程,还是要自己去用工具,抓取对应的内容,然后参考一下上面所写的内容,才会有个更加详细的了解的。
而如果真的是掌握了这部分的内部原理,那么再去分析其他网站登陆过程,应该不是很难了。至少多数的网站登陆,好像都没这么复杂,不会有这么多的直接和间接的重定向,这么多的cookie内容需要提交和管理。
整个分析过程,其实是很痛苦的,但是只有痛苦了之后,才会有进步,所以为了进步,那痛苦也是值得的。
【经验总结】
1. 最大的感悟和总结,其实就是验证了之前的一句话:
(计算机行业)只要有源码,没有解决不了的问题。
该句话,省去了后半句,其实该加上:
只要有一定的基础和经验,加上足够的悟性和学习能力,给上一定的时间(未必需要很长),总是可以把问题解决的。
余下的,就是以基础加上经验,努力学习,摸索,折腾了,总是会一步步朝着问题被彻底解决的方向走的。
途中,最多也就是被1个或几个大点的问题,绊住了一会,但是够努力和继续学习不懂的之后,还是可以慢慢找到解决问题的办法的。
下面的总结,那就是一些技术方面的心得和经验了:
2.对于分析网页跳转方面的知识,除了那种访问某网页,其内部直接redirect的之外,还可能有返回的htnml网页内容中,包含对应的头,其包含了refresh动作,由此而产生同样的网页跳转的效果。
3 对于模拟网页登陆网站,或者抓取网站内容的时候,尤其要注意user-agent,除了网站有可能通过user-agent中的refer来实现防盗链之外,可能就会遇到,不同版本的浏览器的信息,导致网页返回内容有区别,导致无法获得正确的返回的内容。在调试的时候,要注意,如果返回内容错了,至少要想到,多尝试一下不同的user-agent,对解决问题,可能会有帮助。
4.有时候调试过程中,可能会假死,比如此处遇到的,其实读取一个http请求之后的response的stream,对于正常网页来说,独缺所返回内容,瞬间就完成了,但是如果遇到极个别特殊情况,其会读取N多秒,导致让我误以为没有返回值,所以去读取返回值,会死掉,是正常的呢,实际后来无意间发现,原来是网页返回内容有问题,或者是底层http相关的库有问题,或者是自己没有使用好对应的库,导致返回值的读取超慢,误以为死掉了。总之,以后调试,可以再多点耐心,等待程序执行彻底的完毕,再看看输出结果如何。多些尝试,多些解决问题的可能。
转载请注明:在路上 » 【经验总结】用C#实现模拟登陆Skydrive网站的分析的全过程