最近更新:2013-07-03
背景
之前,对于折腾,静态或动态的网页抓取,模拟登陆,也算有些时日了。
在此期间,遇到很多问题,也都基本上靠自己慢慢的解决了。
此处,把其中相对常见和通用的问题或者是注意事项,
再加上一些心得,在此整理出来,供参考:
网页抓取和模拟登陆的注意事项和心得
如果不加User-Agent,则有些网址的访问,会被禁止的
此点,之前别的很多人已总结过了。
此处,还是再啰嗦一遍。
比如,直接通过浏览器访问,csdn的某个图片地址:
http://my.csdn.net/uploads/201205/03/1336005998_9131.png
是可以的。
那是因为浏览器访问时,(用IE9的F12可以抓取到)已添加对应的User-Agent:
User-Agent Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
而当用程序去模拟访问时,直接用Python代码,比如:
resp = urllib2.urlopen(fileUrl, timeout=gConst['defaultTimeout']);
是会出错的:
HTTP Error 403: Forbidden |
而加上对应的User-Agent后,即可:
gConst = { 'UserAgent' : 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E)', 'defaultTimeout': 20, # default timeout seconds for urllib2.urlopen } request = urllib2.Request(fileUrl, headers={'User-Agent' : gConst['UserAgent']}); resp = urllib2.urlopen(request, timeout=gConst['defaultTimeout']);
网页执行过程中由于自动跳转(AutoRedirect)而导致获得不到对应的cookie
正常的网页执行过程,经过分析后,
都是一步步的执行,然后有些步骤中,获得对应的cookie的。
然后后续的执行,则利用前面所得到的cookie,访问后面的地址,
因此才一步步,正常的执行下去的。
但是,有时候,由于,前面的某个步骤中,发生了自动跳转,但是实际上,却是在自动跳转期间,获得的cookie
而导致你写程序模拟此过程中,不小心设置了autoredirect为true,然后只获得了,跳转后的网页返回的结果,其中没有对应所需要的cookie。
即:
如果程序中得不到cookie(因此无法模拟登陆)可以看看是否是 内部自动跳转 auto redirect 而导致 无法获得cookie
在模拟登陆的时候,常常会遇到,虽然代码已经正常模拟执行某个http的请求了。
但是返回的结果中,却没有得到对应的cookie(以及返回的html值也不对,是之后的某个网页的内容)
此时,就可以去看看,分析出来的该http请求过程中,是不是带了自动跳转
如果是,用程序模拟时,禁止自动跳转,一般即可得到所期望的cookie,以及返回的html。
解决办法:
禁止掉自动跳转,即设置autoredirect为false,通过代码去手动实现自动跳转,如此,跳转期间所产生的cookie,就可以得到了。
例子:
中,也是得不到对应的cookie,后来用了:
headerDict.Add( "AllowAutoRedirect" , "false" ); |
去禁止自动跳转,才得到对应的cookie的。
(2)skydrive模拟登陆期间,就是由于其内部自动跳转,而导致获得不到cookie,而导致无法模拟后续的过程。
后来禁止掉自动跳转,才获得了所需要的cookie,才能继续正常的模拟后续的过程。
有些post data是回车换行作为分隔符的
一般都是&作为分隔符的
xxx=xxx&xxx=xxx&xxx=xxx
特殊的,是用\r\n,即回车换行作为分隔符的
xxx=xxx
xxx=xxx
xxx=xxx
例子:
(1)【记录】给BlogsToWordPress添加支持导出网易的心情随笔
中遇到的:
POST请求中,Content-Type不是application/x-www-form-urlencoded而是text/plain
正常的话,写程序模拟http的POST时,会添加相应的头信息Content-Type,值为
application/x-www-form-urlencoded
的,
但是,有时候,遇到特殊的情况是,虽然是POSTGET请求,Content-Type也却是普通GET的值:
text/plain
的。
例子:
(1)同上面的例子:
【记录】给BlogsToWordPress添加支持导出网易的心情随笔
中遇到的:
有时候需要手动修改设置cookie的domain,以使得后续的http的请求中,能包含对应的cookie
有些时候,网络执行过程中,所分析出来的过程中,看到的cookie,
其实是经过相关的js等过程中,已经改变了domain的cookie
所以,在接着,从原先的一种domain的地址,去访问另外一种domain的时候,对应的cookie,才是能够正常传递过去的
而此时,如果你写代码去模拟,若是忽略掉这一点,没有手动去设置对应的cookie的话,则是无法成功模拟整个流程的。
例子:
(1)skydrive模拟登陆后,再上传文件的时候,就需要手动设置cookie的domain,然后才能正常上传文件的
(2)模拟baidu开发者去获得对应的token的过程中,也是需要先把模拟登陆所获得的cookie的,手动修改domain为后续的所要访问的url的domain,然后才可以正常获得对应的后续的返回的verify code和token的。
写程序模拟执行过程时,不能直接拷贝分析而得的数据,而要自己用程序模拟出来
注:此问题,已经发现不止一个人遇到了;很多人,都犯了(我觉得本来不需要说明)的明显的错误。
所以在此专门要详细解释一下:
一般来说,你所分析出来的网页的执行逻辑中,所涉及的数据,往往都是有相应的上下文的,即数据往往都是前后相关的,再换句话说:
后面的数据,往往都是前面的某些逻辑和过程,所产生出来的,
再换句话说,你想要模拟此逻辑过程的话,需要同时模拟对应的前后逻辑,以获得所需要的数据,
而不能是,直接从抓取网络执行过程中,所获得的“临时”的数据,
即你网络分析的时候,所得到的数据,那往往都是和当时网页执行的时刻,所相关的;
而你要是想要模拟整个流程,自然就要模拟前面的逻辑过程,以得到你要的数据,再继续模拟后面的逻辑了。
举个例子:
某人需要模拟登录:
http://www.studentart.com.cn/feed/home/57626
对应的分析逻辑出来的内部过程就是:
访问
http://www.studentart.com.cn/feed/home/57626
对应的header是:
post data是:
对应的,正确的返回的结果是json格式的内容 response :
以及相关的cookie的内容:
其中,很明显,有些数据,比如:
post data中的:
xjxr=1370269905900
Cookie中的:
Hm_lpvt_d933aa4479d795ed42bb9c037c756c47=1370269889
__utma=1.646193226.1370269625.1370269625.1370269625.1
等等的数据,都是,动态变化的。
意味着:
你不能直接拷贝这些数据,到你的程序里面,去模拟此登录过程。
因为这些数据,都是和当时网页执行时候的动态过程相关的,动态产生的;
而放到你程序里面,也比如是无效的。
如何得到有效的?
很明显,还是需要你自己去:
先找到这些数据是如何产生的;
然后再用程序模拟此过程,以产生该数据;
然后有人会问,这些数据,到底从何而来,如何分析得到的?
此时,就只能靠自己分析了。
比如对于xjxr这个变量,通过分析,最后可以找到,是在js中赋值的:
而对应的js的代码:
var dNow=new Date(); rd.push('&xjxr=');
可以看出,其就是对应的时间戳。
剩下的就是用程序模拟出来了。
总之,还是那句话:
很明显,其中模拟登录,网页抓取等过程中,所分析出来的数据,
是和(网页执行)过程相关的,是动态的,
直接将这些,已经过期的,动态产生的数据,拷贝到程序里面用,必然是会失败的。
解决办法也很明显:
先分析逻辑,搞懂这些数据是如何产生的;
然后再写程序,模拟这段逻辑,产生所需的数据;
得到了所需的数据,才能接着继续模拟后面的过程的;
上述所涉及的内容中:
1. 关于如何使用工具去分析对应的逻辑,不懂的去参考:
详解抓取网站,模拟登陆,抓取动态网页的原理和实现(Python,C#等)
所提到的:
【教程】手把手教你如何利用工具(IE9的F12)去分析模拟登陆网站(百度首页)的内部逻辑过程
【整理】各种浏览器中的开发人员工具Developer Tools:IE9的F12,Chrome的Ctrl+Shift+J,Firefox的Firebug
【总结】浏览器中的开发人员工具(IE9的F12和Chrome的Ctrl+Shift+I)-网页分析的利器
2.关于时间戳,如何通过程序所获得,我其实都帮你们写好函数了,详见:
4.1.1. 当前时间转换为时间戳:getCurTimestamp
3. 更多的,关于我写的库函数,参见(其中就有Python语言的):
crifanLib – crifan’s Library(C#/Python/PHP/C/…)
最后送上一句:
多看我的,完整的教程,以及多多思考,还是可以自己就发现很多显而易见的错误的。
返回的html内容是乱码
返回的html的内容,得到之后,发现是乱码,是那种英文字母之类的,可以正常显示,中文之类的,没法显示。
原因是:
返回的html,作为普通的字符串,其编码格式,和你所用到编码格式不一致。
一种常见的问题是:
比如python中,你得到的是UTF-8的html字符串,但是你将其print到windows的cmd中,由于cmd中的是GBK编码,所以导致显示出来的是乱码。
还有一种相对也很常见的:
对于得到的某种编码,比如GBK的html字符串,想要正确的解码,得到其unicode的字符串
但是却用错了解码方式,比如用UTF-8去解码本身是GBK的html,所以当然也会出错。
所以,终究是要:
对于字符编码本身很清楚:
不清楚的去看:
对于html返回的字符串是什么编码的:
关于html的charset相关的知识,不了解的去看:
【整理】关于HTML网页源码的字符编码(charset)格式(GB2312,GBK,UTF-8,ISO8859-1等)的解释
如果是python中,还要清楚python中的字符串的编码是如何处理的:
如果涉及到print到windows的cmd中,还要清楚cmd是GBK编码的:
不清楚的去看:
如此,才算,真的搞懂错误的根本原因,上述背景知识都懂了,自然就可以利用对应的只是,写出相关的解决办法了。
解决办法:
(1)Python中解决思路:
通过html的charset(或者自己看html源码,自己直接找到charset的值),得到html是什么编码的,然后直接decode解码即可得到unicode字符串。
核心代码:
#假设当前respHtml是GBK编码类型的 htmlCharset = "GBK"; decodedUnicodeStr = respHtml.decode(htmlCharset);
(2)C#中的解决思路:
在获得的html后,需要通过,在调用StreamReader时,传递对应的Encoding编码格式,即可正确解码为所需要的unicode类型的字符串了。
核心代码:
string htmlCharset = "GBK"; //use songtaste's html's charset GB2312 to decode html //otherwise will return messy code Encoding htmlEncoding = Encoding.GetEncoding(htmlCharset); StreamReader sr = new StreamReader(resp.GetResponseStream(), htmlEncoding); //read out the returned html //here we got the, unicode (non-messy code) html string respHtml = sr.ReadToEnd();
(3)更全面的,完整的代码,则可以参考:
(上面已经给出的)
【整理】关于HTML网页源码的字符编码(charset)格式(GB2312,GBK,UTF-8,ISO8859-1等)的解释
中的,Python和C#的示例代码,说明如何去将得到的html的字符串,进行对应的编码和解码方面的处理的。
返回的html内容是二进制的乱码
和上面类似,但是返回的内容,不仅仅是乱码,是连英文字母,都看不到,是哪种二进制级别的,混乱的内容,比如:
python代码调试时看到的,类似于这样的:
C#代码调试时,看到类似于这样的:
(
【已解决】设置Accept-Encoding为gzip,deflate,返回的网页是乱码
中遇到的)
此处,出现,返回的html是二进制乱码的:
原因:
此处,返回的http的response中的header中会有对应的Content-Encoding,即:
Content-Encoding: gzip |
或:
Content-Encoding: deflate |
表示返回的html内容是gzip或deflate压缩的内容。
由于你没有解压缩,所以看起来,以为是二进制的乱码。
解决办法:
解压缩对应的内容,即可得到正常的html了。
如何解压?
(1)Python中的解决方案:
根据Content-Encoding是gzip还是deflate,调用zlib.decompress传入不同的参数去解压
具体代码:
参考我的crifanLib.py中的getUrlRespHtml中的:
#print "---before unzip, len(respHtml)=",len(respHtml); respInfo = resp.info(); # Server: nginx/1.0.8 # Date: Sun, 08 Apr 2012 12:30:35 GMT # Content-Type: text/html # Transfer-Encoding: chunked # Connection: close # Vary: Accept-Encoding # ... # Content-Encoding: gzip # sometime, the request use gzip,deflate, but actually returned is un-gzip html # -> response info not include above "Content-Encoding: gzip" # eg: http://blog.sina.com.cn/s/comment_730793bf010144j7_3.html # -> so here only decode when it is indeed is gziped data #Content-Encoding: deflate if("Content-Encoding" in respInfo): if("gzip" == respInfo['Content-Encoding']): respHtml = zlib.decompress(respHtml, 16+zlib.MAX_WBITS); elif("deflate" == respInfo['Content-Encoding']): respHtml = zlib.decompress(respHtml, -zlib.MAX_WBITS);
(2)C#中的解决方案:
给HttpWebRequest添加上对应的AutomaticDecompression为gzip即可:
具体代码:
参考我的crifanLib.cs中的getUrlResponse中的:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); ...... req.Headers["Accept-Encoding"] = "gzip, deflate"; req.AutomaticDecompression = DecompressionMethods.GZip;
即可。
分析网页执行逻辑之前,记得要彻底清除cookie
之前已经在教程:
【总结】浏览器中的开发人员工具(IE9的F12和Chrome的Ctrl+Shift+I)-网页分析的利器
中,提到这点了。
即,在用工具,比如IE9的F12去分析网页执行过程,网页登陆过程的内部逻辑的时候,确保自己已经
通过工具
或
手动
清除了相关的cookie
然后,接下来的分析网页执行的过程,才是“全新”的过程,才是真正的你的代码执行的过程中,所遇到的情景:
你代码的最开始,又没有保存什么cookie,所以用工具分析网页执行过程时,也要保证没有,由于之前某次登陆网站,访问网页,而产生了相关的cookie,所以要去清除掉。
比如,之前用IE9的F12,去分析某网站登陆过程时,截图如下:
很明显,意味着:
你去用IE9的F12,去分析该网站执行的过程时,
第一个抓取处理的http的访问,就已经包含了对应的一个叫做UNAME的cookie
->
说明你没有彻底清除和此网站相关的cookie
->
说明你之前就用过IE9去打开过该网站,对应的本地生成了此UNAME的cookie
并且还没有过期,所以会存在,所以你再次用IE9打开该网站,才会看到有此cookie
->
只有清除掉cookie后,再去默认分析,才是正常的。
如何清除cookie?
详见:
【总结】浏览器中的开发人员工具(IE9的F12和Chrome的Ctrl+Shift+I)-网页分析的利器
中的说明,包括了用工具去清除,和手动去清除,都有详细的解释。
总结
关于网页抓取,模拟登陆,涉及到的东西,其实还是很多的。
至少包括http,cookie,html,css,字符编码,json,gzip,等等内容。
真的是需要大量的实践,才能得出一些真知的。
上述的整理,相信能够帮到很多人。
另外,再贴一次:
关于网页抓取,模拟登陆方面的教程,从原理到代码,都整理到这里了:
详解抓取网站,模拟登陆,抓取动态网页的原理和实现(Python,C#等)
另外对于python,也有单独的解释:
转载请注明:在路上 » 【总结】静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得