优化爬虫初级篇

本文总阅读量

前记:之所以说是初级篇,是我现在用的爬虫最多也就是十万级数据,数据量还是不大,所以以下内容还是我实际遇到需要优化的内容或者网上看到的一些文章,该文章很多内容来自于网上,主要是做记录和写下个人理解。因为主要是记录,所以大多是只是阐述下有这个方法,具体还需要百度等去了解

1.增加爬行速度

爬虫是需要访问网络的,所以速度会比写入数据还慢,数据量少的话爬虫速度还看不出有多慢,当数据量多起来的时候,少则要几天,多则要几周的时间。

1.1 分布式爬虫

分布式爬虫,简单来说就是本来由一台电脑运行的爬虫任务分给其他电脑一起运作,从而达到提升爬虫速度。
分布式爬虫主要有主从模式和对等模式,两种模式都有对应的缺点。例如主从模式的爬虫速度会因为控制节点的瓶颈限制到速度(因为控制节点上面有URL管理器、数据存储器和控制调度器,而其他爬虫节点只是从调度器那里领取任务再执行任务(这里有一个简单的分布式爬虫主从模式的栗子,书里第7章的内容)),而对等模式会因为当有一台服务器死机或者添加新的服务器,那么所有URL的哈希求余的结果就都要变化。也就是说,这种方式的扩展性不佳,不过对等模式也有进行改进。这两种方案的对比和结果图示可以参考这里
虽然我现在没有那么多电脑/服务器来跑分布式爬虫,但是可以通过一些资料了解到分布式爬虫的存在和大概结构,运行原理。目前网上的资料大多都是采用主从模式的。所以对等式我也只是了解一下,并没有找到案例(网上基本都是Scrapy+Redis的主从分布式爬虫,我试着跑一下,发现数据量还不会造成瓶颈- -估计要上千万的数据量才会有瓶颈吧)

1.2 榨干性能去爬虫

首先先了解几个概念

  • 进程
    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
  • 线程
    线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
    不过要注意的是,python的线程并不是并行,而是并发。这也是之前很多人在使用Python时摒弃线程的原因
  • 协程
    协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
    简单来说,协程相比线程来说有一个优势,就是在协程间切换时不需要很大的资源开销。在使用时可以开多个进程,然后每个进程开多个线程,每个线程开多个协程来综合使用。
  • 并发与并行

并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。
并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
区别:并发和并行是即相似又有区别的两个概念,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。倘若在计算机系统中有多个处理机,则这些可以并发执行的程序便可被分配到多个处理机上,实现并行执行,即利用每个处理机来处理一个可并发执行的程序,这样,多个程序便可以同时执行。

  • 同步与异步,阻塞与非阻塞
    这个的概念很多人都清楚了,这里贴个直白的解释,看了容易理解
    老张爱喝茶,废话不说,煮开水。出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。1 老张把水壶放到火上,立等水开。(同步阻塞)老张觉得自己有点傻2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~ 的噪音。3 老张把响水壶放到火上,立等水开。(异步阻塞)老张觉得这样傻等意义不大4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)老张觉得自己聪明了。所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
    作者:愚抄
    链接:https://www.zhihu.com/question/19732473/answer/23434554
    来源:知乎
  • IO密集型任务 VS 计算密集型任务
    所谓IO密集型任务,是指磁盘IO、网络IO占主要的任务,计算量很小。比如请求网页、读写文件等。当然我们在Python中可以利用sleep达到IO密集型任务的目的。
    所谓计算密集型任务,是指CPU计算占主要的任务,CPU一直处于满负荷状态。比如在一个很大的列表中查找元素(当然这不合理),复杂的加减乘除等。

说了关于异步和爬虫的文章
说了多线程与多进程的
说了协程与线程的
说了为什么要使用进程+协程的方案,很多人都在使用这种方案
附加:
python3下multiprocessing、threading和gevent性能对比
Multiprocessing基础
如果数据量大了,就要把爬虫大概分为URL访问,网页解析,数据库读写等几大类。
像URL访问,数据库读写可以用线程或者协程。网页解析用进程,同时,URL访问应该使用异步IO,网页访问等待的时间大大大于CPU操作的时间
协程是用户自己来编写调度逻辑的,对CPU来说,协程其实是单线程,所以CPU不用去考虑怎么调度、切换上下文,这就省去了CPU的切换开销,所以协程在一定程度上又好于多线程
有些时候线程最多只能开几百个,理论上协程可以开无数个(不过会遇cant watch more than 1024 sockets),如果网速不够的话,其实两个的速度差不多
有时还需要考虑下网络IO电脑配置情况(如CPU核数,内存大小等)

2.解决反爬

爬虫速度上去了,基本都会跟反爬见面了。爬虫与反爬的斗争从有爬虫就开始了,虽然最后都是爬虫的胜利,但也付出了很多代价
要解决反爬虫,需要先了解有哪些反爬措施,反爬措施有以下几种:

  • 基于验证码的反爬虫,这种反爬是我们从拥有一个网络账号开始就能看得见的反爬技术,而且已经从一开始的传统验证码、逻辑验证码,到现在12306的图片验证码,还有许多网站开始用的滑动验证码,和最近刚出现的那种用鼠标点击一个圆点就能通过的验证码(这种验证码我是暑假也就是7月后才看到的实在不知道他的原理是什么,也是滑动图片验证码的公司出的,好像是利用判断鼠标位置和移动的行为是不是用户来验证的- -)

  • 基于Headers的反爬,这是一种从请求头Headers进行反爬是比较常见的,大部分网站都有对请求头的User-Agent和Referer字段进行检测,同时也是最容易解决的一种反爬,直接从自己浏览器获取请求头,或搜索一些请求头来使用就可以了(下文不再描述)

  • 基于用户行为的反爬,通俗来见就是不像人为访问他的网站,例如同一ip短时间内大量的访问页面。

  • 基于动态页面的反爬,现在越来越多的网站采用动态加载技术,无法直接从页面上获取数据,需要分析Ajax请求,再进行发送接受。

  • 基于页面请求分析,有些网页在填写表单时会有一些隐藏的表单,他们在你登录时就会为一个值自动赋值,当提交表单时就把这个值提交,如果你的代码没填写这个值时,对面服务器就会拒绝你的访问,不过这个值 一般都很容易找到(例子为知乎的登录)。还有一种是加密数据,直接把用户提交的数据进行加密再传送(例如密码),这时就要通过多次登录来获取到哪些数据是是登录时才生成的,再分析是用哪个方法生成的,有时还有二次登录分开加密数据(例子为百度登录),这种反爬每个网站的规律都不一样,所以要逐一分析,这也是考验一个爬虫开发者的能力。(下文也不再对这种反爬方式再次描述)

    了解了一些反爬措施后,我们可以来研究如何解决反爬了。

    2.1 解决验证码问题

  • COOKIE登录
    我们可以把登录时获取的cookie获取到,并提取里面的信息,下次使用时再调用出来。具体调用方法得看使用使用的是什么库,不过原理都是提取cookies还有获取这个cookies时所使用的headera一起传给请求那里。

  • 验证码识别
    验证码识别中的传统验证码识别只要是肯下功夫,通常情况下识别率能达到90%以上了。
    python的传统验证码识别需要安装tesseract-ocr,pytesseract和pillow
    pillow主要是用来,把图像进行转换,比如将一个验证码图片进行二值化处理,字符翻转等最后生成一张容易被ocr引擎识别的图片。
    pytesseract主要用来识别图片里面的内容,如果要识别更多内容,需要用tesseract-ocr来训练,生成数据。
    还有一些比较奇葩的验证码,可以使用人工打码,不过需要一定的费用,具体打码平台自行百度吧~
    还有一种这几年非常流行的滑动验证码,这种方法可以使用多账号登录后获取cookie信息再组建cookie池的方法绕行。关于如何破解这种验证码可以访问这里

  • 移动端网页
    这部分我不知道要放在哪里比较好- -,我在爬虫时,一般发现电脑端比较难爬取且移动端有我需要获取的数据时我都直接转到移动端了。。。
    通过修改UA可以达到变成安卓手机,苹果手机或者平板电脑去访问,从而页面变成为移动端的页面,通常开头的WWW都变为M,加入这个页面比较难的话,还可以通过wap开头去访问
    通常来说,移动端的页面验证码没那么复杂,也没用Ajax技术生成的动态网页

2.2 ip问题

通常网站都会自行判断同一个IP的访问在一定时间内的访问速度,如果过高时,服务器就会拒绝给这个IP发送信息
解决方法有以下几种:
-Tor 代理
这个可以让你匿名浏览,可以对访问的网站隐藏IP地址,但是他的访问速度比较慢,一般很少人考虑用到

  • VPN
    这里说的VPN主要是利用VPN提供商可以分配不同的网络线路且自动更换IP,实时性很高,速度也很快,稳定性可靠。不过价格都不低。。。。。
  • ADSL拨号
    也就是网上拨号,因为ADSL每次断开重连的时候接受的IP都会有变化,可以利用这个原理来改变IP,不过每次重连的时候时间比较久,所以实时性并不高(手机的打开飞行模式再关闭也是可以这样的)
  • IP代理池
    IP代理池如果是商用的也是需要钱的,但是国内有些免费的IP,我们可以通过抓取IP并判断IP是否可以用了来组建IP代理池让自己的爬虫可以调用,当然,已经有一些开源的IP代理池项目了
    首先是如何自己打造一个IP代理池
    第一篇文章
    第二篇文章
    一些开源的代理池
    IPProxyPool
    这个有说了设计思路且开源了自己制作的代理池

2.3动态页面加载问题

遇到一些需要动态加载的页面可以使用selenium+PhantomJS来解决,不过PhantomJS已经停止维护了,而我经常使用selenium+chrome,听说最近有个Headless Chrome这个网页也有介绍官方文档请点击里(这个网页并没有问题,自行解决)不过我还没了解。。
selenium可以对元素进行选取,可以对页面进行操作等等,几乎我们在页面进行的操作他都可以进行操作,如果要深入了解的建议访问官方文档(英文的)或者可以看看这篇文章了解一下
一般selenium+PhantomJS容易被反爬识别到,不建议使用了。。这种爬取方法可以说比较万能,但是速度有点慢

3数据库选择(非次要)

一开始会总犹豫我爬取的数据放哪里好。现在觉得都无所谓了。
因为前面已经说过了,我现在爬取的数量只是十万级别,用哪个数据库都差不多。随心,不过这里放一下看的一些文章。反正我喜欢用mongoDB,不过要注意NOSQL虽说是非关系数据库,不过不是说没关系。NOSQL是 NOT ONLY SQL的意思,这一点要注意下
介绍了关系型和非关系型数据库的特点
同样是说两大类数据库的区别,不过更详细,推荐看这个
顺便放一个说到倒排索引的文章大型数据读写时可以用到倒排索引。起这名字的人好奇葩,如果翻译为互换位置索引我还是容易理解,翻译为倒排,我以为是从后面数过来- -
这篇文章讲了下关系型数据库的限制以及发展(简述)
最后推荐下MongoDB的中文社区点击这里跳转

查看评论