spider 06-min.jpg

Python中内置的序列,如list、tuple、str、bytes、dict、set、collections.deque等都是可迭代的,但它们不是迭代器。迭代器可以被 next() 函数调用,并不断返回下一个值。Python从可迭代的对象中获取迭代器。迭代器和生成器都是为了惰性求值(lazy evaluation),避免浪费内存空间,实现高效处理大量数据。在Python 3中,生成器有广泛的用途,所有生成器都是迭代器,因为生成器完全实现了迭代器接口。迭代器用于从集合中取出元素,而生成器用于"凭空"生成元素 。PEP 342 给生成器增加了 send() 方法,实现了"基于生成器的协程"。PEP 380允许生成器中可以return返回值,并新增了 yield from 语法结构,打开了调用方和子生成器的双向通道

spider 05-min.png

I/O密集型最适合使用多线程,当然包括网络I/O。我们要下载多张图片,每次去下载一张图片,就是发起一次HTTP请求(使用TCP协议),客户端首先通过socket.socket()创建一个套接字,然后调用connect()方法经过三次握手与服务端建立TCP连接,这个过程是阻塞的。建立连接后,客户端将请求(要访问图片资源)发送给服务端,然后服务端返回响应,客户端用recv()方法每次接收一定数量的字节,客户端在每个响应报文(一张图片有多个数据包)到达操作系统内核时,是阻塞的。网络I/O对于CPU来说是无比漫长的,如果是依序下载,CPU就要一直阻塞到第1张图片的字节全部下载完成后,才能下载第2张,这些等待的时间(对CPU来说,比它处理数据的时间多出无数倍)就白白浪费了。为了合理利用CPU资源,可以使用多线程,每个线程去下载一张图片,当下载第1张图片的任务阻塞时,CPU切换到第2个线程,它开始下载第2张图片,依次类推,当第1张图片有响应报文到达时,等其它线程阻塞后,CPU又会切换到下载第1张图片的那个线程

spider 03-min.jpg

从这一篇开始,将介绍如何用Python3实现网络爬虫,多任务快速抓取你想要的数据。每一次HTTP请求--响应要经过TCP三次握手、客户端发送请求数据、服务端分多次返回响应数据,这个过程中,客户端的CPU在等待网络I/O时会阻塞。Python依序下载是一个主线程依次等待每个网络I/O完成,而多线程是多个线程并发(不是并行)等待多个网络I/O,当一个线程因为等待网络I/O而阻塞时,会自动切换到另一个线程继续执行,而不是CPU浪费时间阻塞在唯一的线程上。所以I/O密集型适合用多线程,像加密解密这种CPU密集型适合用多进程(并行)

spider 02-min.jpg

本文是整个爬虫系列的理论基础,试想一下,如果你的爬虫只能一次下载一张图片,那要爬完整个图片网站的时间会让人抓狂,所以我们需要让程序能够并发,同时请求多张图片资源,因为网络传输时间对于CPU来说太漫长了,并发的好处是可以合理的解决CPU和网络I/O之间的速度鸿沟