当前位置: 首页 > 科技观察

Python实战技巧大任务切分

时间:2023-03-17 21:56:06 科技观察

今天就来聊一聊Python中的任务切分。以一个爬虫为例,从一个存放url的txt文件中读取它的内容,我们会得到一个url列表。我们称这个url列表为一项伟大的任务。列表切分在不考虑内存占用的情况下,我们对上面的大任务进行切分。例如,我们将大任务分成小任务,每秒钟最多只访问5个URL。importosimporttimeCURRENT_DIR=os.path.dirname(os.path.abspath(__file__))defread_file():file_path=os.path.join(CURRENT_DIR,"url_list.txt")withopen(file_path,"r",encoding="utf-8")asfs:result=[i.strip()foriinfs.readlines()]returnresultdeffetch(url):print(url)defrun():max_count=5url_list=read_file()forindexinrange(0,len(url_list),max_count):start=time.time()fetch(url_list[index:index+max_count])end=time.time()-startifend<1:time.sleep(1-end)if__name__=='__main__':run()键代码都在for循环中。首先,我们声明range的第三个参数,指定迭代的步长为5,这样每次增加索引都是以5为基础的,即0、5、10。。.然后我们对url_list进行切片,每次取五个元素。这五个要素会随着指数的增加而不断变化。如果最后还不够五个,根据切片的特点,此时取多少个元素?,不会造成索引超标的问题。随着url列表的增加,我们会发现内存占用也在增加。这时候我们就需要修改代码了。我们知道生成器节省内存空间。修改后,代码变为如下。生成器分段#-*-编码:utf-8-*-#@时间:2019-11-2323:47#@作者:陈祥安#@文件名:g.py#@公众号:Python学习与开发importosimporttimefromitertoolsimportisliceCURRENT_DIR=os.path.dirname(os.path.abspath(__file__))defread_file():file_path=os.path.join(CURRENT_DIR,"url_list.txt")withopen(file_path,"r",encoding="utf-8")asfs:foriinfs:yieldi.strip()deffetch(url):print(url)defrun():max_count=5url_gen=read_file()whileTrue:url_list=list(islice(url_gen,0,max_count))ifnoturl_list:breakstart=time.time()fetch(url_list)end=time.time()-startifend<1:time.sleep(1-end)if__name__=='__main__':run()首先,我们修改了文件的读取方式,将原来阅读列表的形式改为生成器的形式。这样我们在调用文件读取方法的时候就大大节省了内存。然后就是改造上面的for循环,因为generator的特性,这里不适合用for来迭代,因为每次迭代都会消耗generator的元素,使用itertools的islice对url_gen进行分段,islice是生成器Slicing,这里我们每次切出一个包含5个元素的生成器,因为生成器没有__len__方法,所以我们把它转成一个列表,然后判断这个列表是否为空,就可以知道迭代应该结束。修改后的代码,无论是性能还是内存节省都有了很大的提升。读取千万级文件不成问题。另外,在使用异步爬虫的时候,可能会用到异步生成器切片。下面就和大家一起探讨一下异步发电机切分的问题Asynchronousgeneratorsegmentation首先来看一个简单的异步发电机。我们知道调用下面的代码会得到一个generatordefffoo():foriinrange(20):yieeldi如果在def前面加一个async,那么调用的时候就是一个异步generator。完整示例代码如下for有点复杂,这里推荐使用aiostream模块。使用后,代码改成如下:]t=awaitstream.list(ys)ifnott:breakprint(t)index+=limitif__name__=='__main__':asyncio.run(run())