Python爬虫 | 多线程、多进程、协程
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。
有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
进程、线程、协程的区别
多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程。(当然主进程挂了所有进程就全挂了,但是Master进程只负责分配任务,挂掉的概率低)著名的Apache最早就是采用多进程模式。
多进程模式的缺点是创建进程的代价大,在Unix/Linux系统下,用fork调用还行,在Windows下创建进程开销巨大。另外,操作系统能同时运行的进程数也是有限的,在内存和CPU的限制下,如果有几千个进程同时运行,操作系统连调度都会成问题。
多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。
协程的优势:
最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
一、多进程
Case 01
# 多进程,使用Pool from multiprocessing import Pool def f(x):
return x*x if __name__ =='__main__':
p = Pool(5)
list = [1,2,3,4,5,6,7,8,9]
print(p.map(f,list)) # map是做映射 输出:[1, 4, 9, 16, 25, 36, 49, 64, 81] Case 01-1
# 多进程,使用Pool import time
import requests
from multiprocessing import Pool task_list = [
'http://bj.maitian.cn/zfall/PG1',
'http://bj.maitian.cn/zfall/PG2',
'http://bj.maitian.cn/zfall/PG3',
'http://bj.maitian.cn/zfall/PG4',
'http://bj.maitian.cn/zfall/PG5',
'http://bj.maitian.cn/zfall/PG6',
'http://bj.maitian.cn/zfall/PG7',
'http://bj.maitian.cn/zfall/PG8',
'http://bj.maitian.cn/zfall/PG9',
'http://bj.maitian.cn/zfall/PG10',
] header = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
} def download(url):
response = requests.get(url,
headers=header,
timeout=30
)
return response.status_code if __name__ == '__main__':
p = Pool(10)
time_old = time.time()
for item in p.map(download, task_list):
print(item)
time_new = time.time()
time_cost = time_new - time_old
print(time_cost)
Case 02 # 多进程,使用Process对象 from multiprocessing import Process def f(name):
print('hello',name) if __name__ == '__main__':
p_1 = Process(target=f, args=('bob',)) # 注意:参数是只包含一个元素的元祖
p_1.start()
p_1.join() p_2 = Process(target=f, args=('alice',))
p_2.start()
p_2.join() 输出:
hello bob
hello alice Case 02-1
# 多进程,使用Process对象 import time
import requests
from multiprocessing import Process task_list = [
'http://bj.maitian.cn/zfall/PG1',
'http://bj.maitian.cn/zfall/PG2',
'http://bj.maitian.cn/zfall/PG3',
'http://bj.maitian.cn/zfall/PG4',
'http://bj.maitian.cn/zfall/PG5',
] header = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
} def download(url):
response = requests.get(url,
headers=header,
timeout=30
)
print(response.status_code) if __name__ == '__main__': for item in task_list:
p = Process(target=download, args=(item,))
p.start()
p.join()
二、多线程
Case 01 import threading
import time class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter def run(self):
print("Starting " + self.name)
# 获得锁,成功获得锁定后返回True
# 可选的timeout参数不填时将一直阻塞直到获得锁定
# 否则超时后将返回False
threadLock.acquire()
print_time(self.name, self.counter, 3)
# 释放锁
threadLock.release() def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1 threadLock = threading.Lock()
threads = [] # 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2) # 开启新线程
thread1.start()
thread2.start() # 添加线程到线程列表
threads.append(thread1)
threads.append(thread2) # 等待所有线程完成
for t in threads:
t.join() print("Exiting Main Thread") Case 02
import threadpool
import time def sayhello (a):
print("hello: "+a)
time.sleep(2) def main():
global result
seed=["a","b","c"]
start=time.time()
task_pool=threadpool.ThreadPool(5)
requests=threadpool.makeRequests(sayhello,seed)
for req in requests:
task_pool.putRequest(req)
task_pool.wait()
end=time.time()
time_m = end-start
print("time: "+str(time_m))
start1=time.time()
for each in seed:
sayhello(each)
end1=time.time()
print("time1: "+str(end1-start1)) if __name__ == '__main__':
main() Case 03
from concurrent.futures import ThreadPoolExecutor
import time def sayhello(a):
print("hello: "+a)
time.sleep(2) def main():
seed=["a","b","c"]
start1=time.time()
for each in seed:
sayhello(each)
end1=time.time()
print("time1: "+str(end1-start1))
start2=time.time()
with ThreadPoolExecutor(3) as executor:
for each in seed:
executor.submit(sayhello,each)
end2=time.time()
print("time2: "+str(end2-start2))
start3=time.time()
with ThreadPoolExecutor(3) as executor1:
executor1.map(sayhello,seed)
end3=time.time()
print("time3: "+str(end3-start3)) if __name__ == '__main__':
main()
多线程做爬虫,如果有一个线程出现问题,所有的都失败了。所以,不适合做爬虫。
三、协程
Case 01 Client example:await, 等待某某执行完成以后才执行下一步
import aiohttp
import asyncio async def fetch(session, url):
async with session.get(url,) as response:
return await response.text() # 注意text加括号了 async def main():
async with aiohttp.ClientSession() as session: # 使用aiohttp库里的ClientSession()函数创建一个session对象
html = await fetch(session, 'http://www.baidu.com') # 想要使用异步函数fetch的返回结果,必须加await参数,意思是必须等它执行完毕,才会去取它的返回值
print(html) loop = asyncio.get_event_loop() # 获取EventLoop
loop.run_until_complete(main()) # 执行coroutine
Case
通过gather实现并发,sleep,是暂时睡,把CPU给其他任务
通过gather方法实现并发.gather除了多任务外,还可以对任务进行分组。优先使用gather.
gather的意思是「搜集」,也就是能够收集协程的结果,而且要注意,它会按输入协程的顺序保存的对应协程的执行结果。 #coding:utf-8
import asyncio async def a(t):
print('-->', t)
await asyncio.sleep(0.5) # 暂停0.5秒,在这期间把CPU让给其他协程,可以让其他协程去执行
print('<--', t)
return t * 10 def main():
futs = [a(t) for t in range(6)] # 列表生成式
print(futs) # coroutine object 协程对象 ret = asyncio.gather(*futs) #记得加 *
print(ret) # <_GatheringFuture pending> 收集未来对象 loop = asyncio.get_event_loop()
ret1 = loop.run_until_complete(ret) print(ret1) main() Case 03
loop.create_task比gather方法使用的更普遍一些,loop.create_task让任务开始执行 #coding:utf-8 import asyncio async def a(t):
print('-->', t)
await asyncio.sleep(0.5) # 这里睡0.5s
print('<--', t)
return t * 10 async def b():
# loop = asyncio.get_event_loop() cnt = 0
while 1: # 死循环,无限执行下去
cnt += 1 # counter计数器的缩写
cor = a(cnt) # coroutine
resp = loop.create_task(cor)
await asyncio.sleep(0.1) # 睡的过程中,a 函数就可以执行。先执行a(1),睡0.1s;再执行a(2),睡0.1s;再执行,执行到a(5)时,用时0.5s
print(resp) loop = asyncio.get_event_loop()
loop.run_until_complete(b())
参考:
Python的线程与进程
https://www.jianshu.com/p/262594f44549
Python的全局解释器锁(GIL)
https://www.jianshu.com/p/9eb586b64bdb
Python分布式计算
https://www.jianshu.com/p/a8ec42f6cb4e
深入理解Python异步编程(上) - 简书
https://www.jianshu.com/p/fe146f9781d2
Awesome Asyncio 《碉堡的Asyncio·中文版》 - 简书
https://www.jianshu.com/p/4f667ecae64f
Kotlin Coroutines(协程) 完全解析(一),协程简介 - 简书 携程系列文章
https://www.jianshu.com/p/2659bbe0df16
Aiohttp相关博客
Welcome to AIOHTTP — aiohttp 3.5.4 documentation
https://aiohttp.readthedocs.io/en/stable/
https://www.cnblogs.com/shijieli/p/10826743.html
Python爬虫 | 多线程、多进程、协程的更多相关文章
- 多线程 多进程 协程 Queue(爬虫代码)
快速理解多进程与多线程以及协程的使用场合和特点 首先我们来了解下python中的进程,线程以及协程! 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务.一个CPU,在一个时间切片里只能运 ...
- python进阶(二) 多进程+协程
我们大多数的时候使用多线程,以及多进程,但是python中由于GIL全局解释器锁的原因,python的多线程并没有真的实现 实际上,python在执行多线程的时候,是通过GIL锁,进行上下文切换线程执 ...
- Python爬虫进阶 | 异步协程
一.背景 之前爬虫使用的是requests+多线程/多进程,后来随着前几天的深入了解,才发现,对于爬虫来说,真正的瓶颈并不是CPU的处理速度,而是对于网页抓取时候的往返时间,因为如果采用request ...
- python爬虫--多任务异步协程, 快点,在快点......
多任务异步协程asyncio 特殊函数: - 就是async关键字修饰的一个函数的定义 - 特殊之处: - 特殊函数被调用后会返回一个协程对象 - 特殊函数调用后内部的程序语句没有被立即执行 - 协程 ...
- 静听网+python爬虫+多线程+多进程+构建IP代理池
目标网站:静听网 网站url:http://www.audio699.com/ 目标文件:所有在线听的音频文件 附:我有个喜好就是听有声书,然而很多软件都是付费才能听,免费在线网站虽然能听,但是禁ip ...
- python 爬虫 多线程 多进程
一.程序.进程和线程的理解 程序:就相当于一个应用(app),例如电脑上打开的一个程序. 进程:程序运行资源(内存资源)分配的最小单位,一个程序可以有多个进程. 线程:cpu最小的调度单位,必须依赖 ...
- Python 中多进程、多线程、协程
进程: 一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享.开销大. 线程: 调度执行的最小单位,也叫执行路径,不 ...
- 也说性能测试,顺便说python的多进程+多线程、协程
最近需要一个web系统进行接口性能测试,这里顺便说一下性能测试的步骤吧,大概如下 一.分析接口频率 根据系统的复杂程度,接口的数量有多有少,应该优先对那些频率高,数据库操作频繁的接口进行性能测试,所以 ...
- python 多进程,多线程,协程
在我们实际编码中,会遇到一些并行的任务,因为单个任务无法最大限度的使用计算机资源.使用并行任务,可以提高代码效率,最大限度的发挥计算机的性能.python实现并行任务可以有多进程,多线程,协程等方式. ...
- 深入浅析python中的多进程、多线程、协程
深入浅析python中的多进程.多线程.协程 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源 ...
随机推荐
- 【LEETCODE】64、链表分类,medium&hard级别,题目:2,138,142,23
package y2019.Algorithm.LinkedList.medium; import y2019.Algorithm.LinkedList.ListNode; /** * @Projec ...
- Python入门 .变量 常量 基础数据类型 用户输入 流程控制语句 小练习题
# 2.name = input(“>>>”)通过代码来验证name变量是什么数据类型?--str name = input(">>>") pr ...
- 记CentOS 发布.NET Core 2.0
centos 7.x sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc sudo sh -c 'echo -e & ...
- C#获取文件夹下的所有文件的方法
目录 #基础知识 #只获取目录下一级的文件夹与文件 # 递归地输出当前运行程序所在的磁盘下的所有文件名和子目录名 正文 #基础知识 1.获得当前运行程序的路径 1 string rootPath ...
- sqoop从mysql导数据到hive报错:Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
背景 使用sqoop从mysql导数据到hive,从本地服务器是可以访问mysql的(本地服务器是hadoop集群的一个datanode),但是sqoop导数据的时候依然连接不上mysql 报错如下: ...
- 【转载】sqlserver中小数类型float和deciaml类型比较
在sqlserver数据库中,float类型和double类型都可以用来表示小数类型,float类型是一种近似数值的小数类型,而decimal类型则是精确数值的小数类型.如果需要在sqlserver数 ...
- Android数据库GreenDao配置版本问题
感谢该贴解决我多天的困惑:https://blog.csdn.net/u013472738/article/details/72895747 主要是降低了GreenDao版本 网上很多教程说的版本都是 ...
- base64的使用
import base64with open("test.jpg", "rb") as f: file = f.read()file_base64 = base ...
- MySQL Percona Toolkit--pt-osc执行SQL命令
pt-osc执行日志 在对数据量为100000的表tb004做DROP COLUMN操作,pt-osc工具日志为: Operation, tries, wait: analyze_table, , c ...
- java Excel文件流输出(后台)没有下载提示
/**方案: *1. *问题源头应该是前端用的是ajax方式请求路径,要想出现下载Excel的弹窗,我个人理解应该是等同于重新打开一个窗口,而ajax默认不动页面直接将值返回,所以没有提升下载. *解 ...