静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得

最近更新:2013-07-03

背景

之前,对于折腾,静态或动态的网页抓取,模拟登陆,也算有些时日了。

在此期间,遇到很多问题,也都基本上靠自己慢慢的解决了。

此处,把其中相对常见和通用的问题或者是注意事项,

再加上一些心得,在此整理出来,供参考:

网页抓取和模拟登陆的注意事项和心得

如果不加User-Agent,则有些网址的访问,会被禁止的

此点,之前别的很多人已总结过了。

此处,还是再啰嗦一遍。

比如,直接通过浏览器访问,csdn的某个图片地址:

?
1
https://img-my.csdn.net/uploads/201205/03/1336005998_9131.png

是可以的。

那是因为浏览器访问时,(用IE9的F12可以抓取到)已添加对应的User-Agent:

?
1
User-Agent  Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)

而当用程序去模拟访问时,直接用Python代码,比如:

?
1
resp = urllib2.urlopen(fileUrl, timeout = gConst[ 'defaultTimeout' ]);

是会出错的:

HTTP Error 403: Forbidden

而加上对应的User-Agent后,即可:

?
1
2
3
4
5
6
7
8
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,就可以得到了。

例子:

(1)【记录】模拟登陆google

中,也是得不到对应的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添加支持导出网易的心情随笔

中遇到的:

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第1张图片

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添加支持导出网易的心情随笔

中遇到的:

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第2张图片

有时候需要手动修改设置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是:

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第3张图片

post data是:

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第4张图片

对应的,正确的返回的结果是json格式的内容 response :

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第5张图片

以及相关的cookie的内容:

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第6张图片

其中,很明显,有些数据,比如:

  • post data中的:

    • xjxr=1370269905900

  • Cookie中的:

    • Hm_lpvt_d933aa4479d795ed42bb9c037c756c47=1370269889

    • __utma=1.646193226.1370269625.1370269625.1370269625.1

等等的数据,都是,动态变化的。

意味着:

你不能直接拷贝这些数据,到你的程序里面,去模拟此登录过程。

因为这些数据,都是和当时网页执行时候的动态过程相关的,动态产生的;

而放到你程序里面,也比如是无效的。

如何得到有效的?

很明显,还是需要你自己去:

先找到这些数据是如何产生的;

然后再用程序模拟此过程,以产生该数据;

然后有人会问,这些数据,到底从何而来,如何分析得到的?

此时,就只能靠自己分析了。

比如对于xjxr这个变量,通过分析,最后可以找到,是在js中赋值的:

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第7张图片

而对应的js的代码:

?
1
2
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中的字符串的编码是如何处理的:

    • 不清楚的去看:

    • 【整理】Python中字符编码的总结和对比:Python 2.x的str和unicode vs Python 3.x的bytes和str

  • 如果涉及到print到windows的cmd中,还要清楚cmd是GBK编码的:

    • 不清楚的去看:

    • 设置字符编码:简体中文GBK/英文

如此,才算,真的搞懂错误的根本原因,上述背景知识都懂了,自然就可以利用对应的只是,写出相关的解决办法了。

解决办法:

(1)Python中解决思路:

通过html的charset(或者自己看html源码,自己直接找到charset的值),得到html是什么编码的,然后直接decode解码即可得到unicode字符串。

核心代码:

?
1
2
3
#假设当前respHtml是GBK编码类型的
htmlCharset = "GBK" ;
decodedUnicodeStr = respHtml.decode(htmlCharset);

(2)C#中的解决思路:

在获得的html后,需要通过,在调用StreamReader时,传递对应的Encoding编码格式,即可正确解码为所需要的unicode类型的字符串了。

核心代码:

?
1
2
3
4
5
6
7
8
9
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代码调试时看到的,类似于这样的:

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第8张图片

C#代码调试时,看到类似于这样的:

【已解决】设置Accept-Encoding为gzip,deflate,返回的网页是乱码

中遇到的)

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第9张图片

此处,出现,返回的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中的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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中的:

?
1
2
3
4
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,去分析某网站登陆过程时,截图如下:

静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得_第10张图片

很明显,意味着:

你去用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,也有单独的解释:

Python专题教程:抓取网站,模拟登陆,抓取动态网页

转载请注明:在路上 » 【总结】静态网页抓取,动态网页抓取,模拟登陆的注意事项和心得

你可能感兴趣的:(反爬,爬虫,python)