使用python写过几个小玩具,基本上都与爬虫、Web相关的,自己也积累了下经验,也看过很多文章,一直想总结下,可惜现在要忙于找工作,实验室活也比较多,也就落下了。感觉如果时间长不记录,也就懒散了,并且许多东西我写完过个两天,我自己都记不住当时怎么想的了。
0、HTTP协议
基本上常见的Web开发里,Web内容都是通过HTTP协议进行传输的(虽然咱不懂Web开发,但是基本的计算机网络知识还是了解的),通过TCP连接服务器的80端口,爬虫其实质就是通过模拟浏览器发送HTTP请求,至于HTTP请求相关知识,点击这里。
1 | HTTP通常通过创建到服务器80端口的TCP连接进行通信 |
1、最基础的抓取网站内容
使用python编写一个网络爬虫是非常简单的,如下例所示:
1 | import urllib2 |
但是实际工作中,这样肯定是不行的,你会遇到各种各样的问题,如下:
- 网络出现错误,任何错误都可能。例如机器宕了,网线断了,域名出错了,网络超时了,页面没有了,网站跳转了,服务被禁了,主机负载不够了…
- 服务器加上了限制,只让常见浏览器访问
- 需要登录才能抓取数据
- IP被封掉或者IP访问次数受到限制
- 服务器加上了防盗链的限制
- 某些网站不管你HTTP请求里有没有Accept-Encoding头部,也不管你头部具体内容是什么,反正总给你发gzip后的内容
- URL链接千奇百怪,带汉字的也罢了,有的甚至还有回车换行
- 某些网站HTTP头部里有一个Content-Type,网页里有好几个Content-Type,更过分的是,各个Content-Type还不一样,最过分的是,这些Content-Type可能都不是正文里使用的Content-Type,从而导致乱码
- 需要抓取的数据很多,怎么提高抓取效率
- 网站内容通过AJAX请求加载的
- 需要验证码识别
至于怎么解决上述问题,我们一一道来。
2、网络或网站出现错误等处理
之前我搞过百度音乐的爬虫,因为当时百度音乐有个bug,就是可以任何人都下载百度音乐的无损、高品质音乐,我就写了个爬虫,开了几十个进程抓取下载地址(抓包解析JSON串数据)存储到数据库里,虽然百度无ip访问限制这些麻烦事,但是程序无任何错误处理的情况下,依然出现各自异常错误,所以我们只需要捕捉异常,处理异常就可以了。我们可以根据urlopen的返回值,读取它的HTTP状态码。除此之外,我们在urlopen的时候,也需要设置timeout参数,以保证处理好超时。
1 | import urllib2 |
3、服务器做了限制
3.1、防盗连限制
某些站点有所谓的反盗链设置,其实说穿了很简单,就是检查你发送请求的header里面,referer站点是不是他自己,我们只要把headers的referer改成该网站即可,以V2EX的领取每日奖励地址为例:
1 | header = {'Referer':'http://www.v2ex.com/signin'} |
3.2、限制浏览器登录,需伪装成浏览器
某些网站做了限制,进制爬虫的访问,此时我们可以更改HTTP的header,与3.1相似:
1 | header = {"User-Agent":"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 |
3.3、IP访问次数受到限制
如果IP被封掉、或者访问次数受到限制,使用代理服务器比较有用,代理IP可以自己抓取一些代理网站的数据:
1 | import urllib2 |
4、需要登录才能抓取数据
4.1、表单的处理
抓包,Chrome或者Firefox+Httpfox浏览器插件就能办到,查看POST数据,模拟表单就可以了,这里不再细说,这里主要注意,在GET/POST一些数据的时候,需要对GET/POST的数据做些编码,即使用urlencode(),我们以北邮校园网关登录为例:
1 | postdata = { |
4.2、Cookie处理
1 | #获取一个cookie对象 |
如果同时需要代理和Cookie,这样就可以:
1 | opener = urllib2.build_opener(proxy_ip, cookie_p, urllib2.HTTPHandler) |
如果上面未使用install_opener(opener),则使用:
1 | response = opener.open(url).read() |
以V2EX每日签到为例,把上面的流程走一遍:
1 | #全局变量装载Cookie省略 |
5、gzip/deflate支持
某些网站不管你请求头带不带Accept-Encoding:gzip,它返回的都是gzip压缩的内容,urlopen不会处理gzip压缩的内容,所以得到的就是乱码,以下为解决方法:
1 | f = urllib2.urlopen(url) |
6、URL编码和网页编码处理
如果URL里含有中文,要对相应的中文进行编码,可以使用urllib.quote(‘要编码的字符串’),如下所示:
1 | http://www.google.com/#newwindow=1&q=你好 |
至于处理网页编码,按照一般浏览器的处理流程,判断网页的编码首先是根据HTTP服务器发过来的HTTP响应头部中Content-Type字段,例如text/html; charset=utf-8就表示这是一个HTML网页,使用的是utf8编码。如果HTTP头部中没有,或者网页内容的head区中有charset属性或者其http-equiv属性为Content-Type的meta元素,如果有charset属性,则直接读取这个属性,如果是后者,则读取这个元素的content属性。但是实际情况是许多网页不按照规矩出牌,首先,HTTP响应里不一定有Content-Type头部,其次某些网页里可能没有Content-Type或者有多个Content-Type(例如百度搜索的缓存页面)。所以,我的经验基本是自己多猜测几次吧。其实GBK、UTF-8占了绝大部分,使用xx.decode(‘gbk’,’ignore’).encode(‘utf-8’) (或相反)试试,其中ignore是指忽略非法字符。
7、AJAX
AJAX 是一种用于创建快速动态网页的技术。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。
AJAX依然是HTTP请求,只是来自页面发起的另一个HTTP请求。我之前做百度音乐的时候,在chrome下的network选项分析每一个选项,找到对应的ajax数据,分析json,在正则、查找之类的。
当然,也可以通过selenium、phantomjs、mechanize等模拟浏览器引擎构建一个真实的浏览器执行JS、渲染页面(在写这篇文章时,我还没用到过这些),这应该算是终极大招了吧。
8、效率
目前使用过多线程、多进程,多进程注意下僵尸进程。
异步:用twisted进行异步I/O抓取,tornado(如果拿到实习offer后,我可能会抽出几天时间学习这个,我感觉我应该学习下web编程,纠结了下flask还是tornado,其实无论哪个一周完全可以入门了,python你懂的。)
9、验证码
图像识别方面的知识,好吧,等我学会后再写吧。。。
根据我的经验,网络爬虫里网络带宽是主要瓶颈,上文只说了抓取,其实网页抽取依然有很多知识要做的,我现在会的都是些皮毛,要学的还很多,等拿到dream 公司的实习offer后,有空继续折腾下,现在主要精力是弥补自己的一些知识缺点和面试长考的问题。
参考链接:
http://blog.binux.me/2013/09/howto-crawl-web/
http://blog.raphaelzhang.com/2012/03/issues-in-python-crawler/
http://yxmhero1989.blog.163.com/blog/static/112157956201311821444664/