多线程 多进程 协程 Queue(爬虫代码)
快速理解多进程与多线程以及协程的使用场合和特点
首先我们来了解下python中的进程,线程以及协程!
从计算机硬件角度:
计算机的核心是CPU,承担了所有的计算任务。
一个CPU,在一个时间切片里只能运行一个程序。
从操作系统的角度:
进程和线程,都是一种CPU的执行单元。
进程:表示一个程序的上下文执行活动(打开、执行、保存...)
线程:进程执行程序时候的最小调度单位(执行a,执行b...)
一个程序至少有一个进程,一个进程至少有一个线程。
并行 和 并发:
并行:多个CPU核心,不同的程序就分配给不同的CPU来运行。可以让多个程序同时执行。
cpu1 -------------
cpu2 -------------
cpu3 -------------
cpu4 -------------
并发:单个CPU核心,在一个时间切片里一次只能运行一个程序,如果需要运行多个程序,则串行执行。
cpu1 ---- ----
cpu1 ---- ----
多进程/多线程:
表示可以同时执行多个任务,进程和线程的调度是由操作系统自动完成。
进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。
进程之间的通信有操作系统传递,导致通讯效率低,切换开销大。
线程:一个进程可以有多个线程,所有线程共享进程的内存空间,通讯效率高,切换开销小。
共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入"互斥锁"。
一个线程在访问内存空间的时候,其他线程不允许访问,必须等待之前的线程访问结束,才能使用这个内存空间。
互斥锁:一种安全有序的让多个线程访问内存空间的机制。
Python的多线程:
GIL 全局解释器锁:线程的执行权限,在Python的进程里只有一个GIL。
一个线程需要执行任务,必须获取GIL。
好处:直接杜绝了多个线程访问内存空间的安全问题。
坏处:Python的多线程不是真正多线程,不能充分利用多核CPU的资源。
但是,在I/O阻塞的时候,解释器会释放GIL。
所以:
多进程:密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。 multiprocessing
缺陷:多个进程之间通信成本高,切换开销大。
多线程:密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。
threading.Thread、multiprocessing.dummy
缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。
协程:又称微线程,在单线程上执行多个任务,用函数切换,开销极小。不通过操作系统调度,没有进程、线程的切换开销。genvent,monkey.patchall
多线程请求返回是无序的,那个线程有数据返回就处理那个线程,而协程返回的数据是有序的。
缺陷:单线程执行,处理密集CPU和本地磁盘IO的时候,性能较低。处理网络I/O性能还是比较高.
下面以这个网站为例,采用三种方式爬取。爬取前250名的电影。。
https://movie.douban.com/top250?start=0
通过分析网页发现第2页的url start=25,第3页的url start=50,第3页的start=75。因此可以得出这个网站每一页的数局是通过递增start这个参数获取的。
一般不看第一页的数据,第一页的没有参考价值。
这次我们主要爬取,电影名字跟评分。只是使用不同方式去对比下不同点,所以数据方面就不过多提取或者保存。只是简单的将其爬取下打印出来看看。
第一:采用多进程 , multiprocessing 模块。 当然这个耗时更网络好坏有关。在全部要请求都正常的情况下耗时15s多。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
#!/usr/bin/env python2 # -*- coding=utf-8 -*- from multiprocessing import Process, Queue import time from lxml import etree import requests class DouBanSpider(Process): def __init__( self , url, q): # 重写写父类的__init__方法 super (DouBanSpider, self ).__init__() self .url = url self .q = q self .headers = { 'Host' : 'movie.douban.com' , 'Referer' : 'https://movie.douban.com/top250?start=225&filter=' , 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36' , } def run( self ): self .parse_page() def send_request( self ,url): ''' 用来发送请求的方法 :return: 返回网页源码 ''' # 请求出错时,重复请求3次, i = 0 while i < = 3 : try : print u "[INFO]请求url:" + url return requests.get(url = url,headers = self .headers).content except Exception as e: print u '[INFO] %s%s' % (e,url) i + = 1 def parse_page( self ): ''' 解析网站源码,并采用xpath提取 电影名称和平分放到队列中 :return: ''' response = self .send_request( self .url) html = etree.HTML(response) # 获取到一页的电影数据 node_list = html.xpath( "//div[@class='info']" ) for move in node_list: # 电影名称 title = move.xpath( './/a/span/text()' )[ 0 ] # 评分 score = move.xpath( './/div[@class="bd"]//span[@class="rating_num"]/text()' )[ 0 ] # 将每一部电影的名称跟评分加入到队列 self .q.put(score + "\t" + title) def main(): # 创建一个队列用来保存进程获取到的数据 q = Queue() base_url = 'https://movie.douban.com/top250?start=' # 构造所有url url_list = [base_url + str (num) for num in range ( 0 , 225 + 1 , 25 )] # 保存进程 Process_list = [] # 创建并启动进程 for url in url_list: p = DouBanSpider(url,q) p.start() Process_list.append(p) # 让主进程等待子进程执行完成 for i in Process_list: i.join() while not q.empty(): print q.get() if __name__ = = "__main__" : start = time.time() main() print '[info]耗时:%s' % (time.time() - start) |
采用多线程时,耗时10.4s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
#!/usr/bin/env python2 # -*- coding=utf-8 -*- from threading import Thread from Queue import Queue import time from lxml import etree import requests class DouBanSpider(Thread): def __init__( self , url, q): # 重写写父类的__init__方法 super (DouBanSpider, self ).__init__() self .url = url self .q = q self .headers = { 'Cookie' : 'll="118282"; , 'Host' : 'movie.douban.com' , 'Referer' : 'https://movie.douban.com/top250?start=225&filter=' , 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36' , } def run( self ): self .parse_page() def send_request( self ,url): ''' 用来发送请求的方法 :return: 返回网页源码 ''' # 请求出错时,重复请求3次, i = 0 while i < = 3 : try : print u "[INFO]请求url:" + url html = requests.get(url = url,headers = self .headers).content except Exception as e: print u '[INFO] %s%s' % (e,url) i + = 1 else : return html def parse_page( self ): ''' 解析网站源码,并采用xpath提取 电影名称和平分放到队列中 :return: ''' response = self .send_request( self .url) html = etree.HTML(response) # 获取到一页的电影数据 node_list = html.xpath( "//div[@class='info']" ) for move in node_list: # 电影名称 title = move.xpath( './/a/span/text()' )[ 0 ] # 评分 score = move.xpath( './/div[@class="bd"]//span[@class="rating_num"]/text()' )[ 0 ] # 将每一部电影的名称跟评分加入到队列 self .q.put(score + "\t" + title) def main(): # 创建一个队列用来保存进程获取到的数据 q = Queue() base_url = 'https://movie.douban.com/top250?start=' # 构造所有url url_list = [base_url + str (num) for num in range ( 0 , 225 + 1 , 25 )] # 保存线程 Thread_list = [] # 创建并启动线程 for url in url_list: p = DouBanSpider(url,q) p.start() Thread_list.append(p) # 让主线程等待子线程执行完成 for i in Thread_list: i.join() while not q.empty(): print q.get() if __name__ = = "__main__" : start = time.time() main() print '[info]耗时:%s' % (time.time() - start) |
采用协程爬取,耗时15S,
用了多进程,多线程,协程,实现的代码都一样,没有测试出明显的那个好!都不分上下,可能跟网络,或者服务器配置有关。
但理论上来说线程,协程在I/O密集的操作性能是要高于进程的。
多线程 多进程 协程 Queue(爬虫代码)的更多相关文章
- python采用 多进程/多线程/协程 写爬虫以及性能对比,牛逼的分分钟就将一个网站爬下来!
首先我们来了解下python中的进程,线程以及协程! 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务.一个CPU,在一个时间切片里只能运行一个程序. 从操作系统的角度: 进程和线程,都 ...
- Cpython解释器下实现并发编程——多进程、多线程、协程、IO模型
一.背景知识 进程即正在执行的一个过程.进程是对正在运行的程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都 ...
- 深入浅析python中的多进程、多线程、协程
深入浅析python中的多进程.多线程.协程 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源 ...
- 也说性能测试,顺便说python的多进程+多线程、协程
最近需要一个web系统进行接口性能测试,这里顺便说一下性能测试的步骤吧,大概如下 一.分析接口频率 根据系统的复杂程度,接口的数量有多有少,应该优先对那些频率高,数据库操作频繁的接口进行性能测试,所以 ...
- python 多进程,多线程,协程
在我们实际编码中,会遇到一些并行的任务,因为单个任务无法最大限度的使用计算机资源.使用并行任务,可以提高代码效率,最大限度的发挥计算机的性能.python实现并行任务可以有多进程,多线程,协程等方式. ...
- Python自动化 【第十篇】:Python进阶-多进程/协程/事件驱动与Select\Poll\Epoll异步IO
本节内容: 多进程 协程 事件驱动与Select\Poll\Epoll异步IO 1. 多进程 启动多个进程 进程中启进程 父进程与子进程 进程间通信 不同进程间内存是不共享的,要想实现两个进程间 ...
- python单线程,多线程和协程速度对比
在某些应用场景下,想要提高python的并发能力,可以使用多线程,或者协程.比如网络爬虫,数据库操作等一些IO密集型的操作.下面对比python单线程,多线程和协程在网络爬虫场景下的速度. 一,单线程 ...
- python中多进程+协程的使用以及为什么要用它
前面讲了为什么python里推荐用多进程而不是多线程,但是多进程也有其自己的限制:相比线程更加笨重.切换耗时更长,并且在python的多进程下,进程数量不推荐超过CPU核心数(一个进程只有一个GIL, ...
- python进阶(二) 多进程+协程
我们大多数的时候使用多线程,以及多进程,但是python中由于GIL全局解释器锁的原因,python的多线程并没有真的实现 实际上,python在执行多线程的时候,是通过GIL锁,进行上下文切换线程执 ...
随机推荐
- yii2下使用支付宝
最近入坑了yii2 感觉是个很强大的框架.使用yii做支付宝的移动支付的时候出了点问题,记录下来避免以后忘记了. 使用的是支付宝立即到账的功能,网上很多集成好的接口我就不重复了,找不到的话github ...
- 深度残差网络——ResNet学习笔记
深度残差网络—ResNet总结 写于:2019.03.15—大连理工大学 论文名称:Deep Residual Learning for Image Recognition 作者:微软亚洲研究院的何凯 ...
- 乐观、悲观锁、redis分布式锁
悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给 ...
- ASCII、Unicode、UTF-8 字符串和编码
字符编码 我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题. 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特 ...
- NOIP2018提高组初赛选讲
说实话,这次的初赛比上一次的要简单. 不过还有些变态的题目. 在一条长度为1 的线段上随机取两个点,则以这两个点为端点的线段的期望 长度是( ). A. 1 / 2 B. 1 / 3 C. 2 / 3 ...
- vue 学习 二
动画 <transition name="fade"> <p v-if="show">hello</p> </tran ...
- 阿里云数据库再获学术顶会认可,一文全览VLDB最新亮点
一年一度的数据库领域顶级会议VLDB 2019于当地时间8月26日-8月30日在洛杉矶圆满落幕.在本届大会上,阿里云数据库产品团队浓墨登场,不仅有多篇论文入选Research Track和Indust ...
- vue 基本知识
1,内置事件对象event 原生JS事件中是直接通过传入 e 或者 event 就可以获取,但在vue中要传入 $event 才可以获取到内置事件对象 <body> <button ...
- SPSS分析技术:CMH检验(分层卡方检验);辛普森悖论,数据分析的谬误
SPSS分析技术:CMH检验(分层卡方检验):辛普森悖论,数据分析的谬误 只涉及两个分类变量的卡方检验有些时候是很局限的,因为混杂因素总是存在,如果不考虑混杂因素,得出的分析结论很可能是谬误的,这就是 ...
- JasperReports报表变量13
报表变量是建立在报表表达式之上的特殊对象.报表变量简化以下任务: 报表,其中大量使用在整个报告模板表达式.这些表达式可以通过使用报表变量只能声明一次. 计数,求和,平均,最低,最高,方差等:报表变量可 ...