Python实用技巧大任务切分

开发 后端
今天来说说,Python 中的任务切分。以爬虫为例,从一个存 url 的 txt 文件中,读取其内容,我们会获取一个 url 列表。我们把这一个 url 列表称为大任务。

 今天来说说,Python 中的任务切分。以爬虫为例,从一个存 url 的 txt 文件中,读取其内容,我们会获取一个 url 列表。我们把这一个 url 列表称为大任务。

[[283503]]

列表切分在

不考虑内存占用的情况下,我们对上面的大任务进行一个切分。比如我们将大任务切分成的小任务是每秒最多只访问5个URL。

 

  1. import os 
  2. import time 
  3.  
  4. CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) 
  5.  
  6. def read_file(): 
  7.     file_path = os.path.join(CURRENT_DIR, "url_list.txt"
  8.     with open(file_path, "r", encoding="utf-8"as fs: 
  9.         result = [i.strip() for i in fs.readlines()] 
  10.     return result 
  11.  
  12. def fetch(url): 
  13.     print(url) 
  14.  
  15. def run(): 
  16.     max_count = 5 
  17.     url_list = read_file() 
  18.     for index in range(0, len(url_list), max_count): 
  19.         start = time.time() 
  20.         fetch(url_list[index:index + max_count]) 
  21.         end = time.time() - start 
  22.         if end < 1: 
  23.             time.sleep(1 - end
  24.  
  25.  
  26. if __name__ == '__main__'
  27.     run() 

关键代码都在for循环里,首先我们通过声明range的第三个参数,该参数指定迭代的步长为5,这样每次index增加都是以5为基数,即0,5,10。。。

然后我们对url_list做切片,每次取其五个元素,这五个元素会随着index的增加不断的在改变,如果最后不够五个了,按照切片的特性这个时候就会有多少取多少了,不会造成索引超下标的问题。

随着url列表的增加,我们会发现内存的占用也在提高了。这个时候我们就需要对代码进行修改了,我们知道生成器是比较节省内存的空间的,修改之后代码变成,下面的这样。

生成器切分

 

  1. # -*- coding: utf-8 -*- 
  2. # @时间 : 2019-11-23 23:47 
  3. # @作者 : 陈祥安 
  4. # @文件名 : g.py 
  5. # @公众号: Python学习开发 
  6. import os 
  7. import time 
  8. from itertools import islice 
  9.  
  10. CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) 
  11.  
  12.  
  13. def read_file(): 
  14.     file_path = os.path.join(CURRENT_DIR, "url_list.txt"
  15.     with open(file_path, "r", encoding="utf-8"as fs: 
  16.         for i in fs: 
  17.             yield i.strip() 
  18.  
  19.  
  20. def fetch(url): 
  21.     print(url) 
  22.  
  23.  
  24. def run(): 
  25.     max_count = 5 
  26.     url_gen = read_file() 
  27.     while True
  28.         url_list = list(islice(url_gen, 0, max_count)) 
  29.         if not url_list: 
  30.             break 
  31.         start = time.time() 
  32.         fetch(url_list) 
  33.         end = time.time() - start 
  34.         if end < 1: 
  35.             time.sleep(1 - end
  36.  
  37.  
  38. if __name__ == '__main__'
  39.     run() 

首先,我们修改了文件读取的方式,把原来读列表的形式,改为了生成器的形式。这样我们在调用该文件读取方法的时候大大节省了内存。

然后就是对上面for循环进行改造,因为生成器的特性,这里不适合使用for进行迭代,因为每一次的迭代都会消耗生成器的元素,通过使用itertools的islice对url_gen进行切分,islice是生成器的切片,这里我们每次切分出含有5个元素的生成器,因为生成器没有__len__方法所以,我们将其转为列表,然后判断列表是否为空,就可以知道迭代是否该结束了。

修改之后的代码,不管是性能还是节省内存上都大大的提高。读取千万级的文件不是问题。

除此之外,在使用异步爬虫的时候,也许会用到异步生成器切片。下面就和大家讨论,异步生成器切分的问题

异步生成器切分

首先先来看一个简单的异步生成器。

我们知道调用下面的代码会得到一个生成器

 

  1. def foo(): 
  2.     for i in range(20): 
  3.         yield i 

如果在def前面加一个async,那么在调用的时候它就是个异步生成器了。

完整示例代码如下

 

  1. import asyncio 
  2. async def foo(): 
  3.     for i in range(20): 
  4.         yield i 
  5.  
  6.  
  7. async def run(): 
  8.     async_gen = foo() 
  9.     async for i in async_gen: 
  10.         print(i) 
  11.  
  12.  
  13. if __name__ == '__main__'
  14.     asyncio.run(run()) 

关于async for的切分有点复杂,这里推荐使用aiostream模块,使用之后代码改为下面这样

 

  1. import asyncio 
  2. from aiostream import stream 
  3.  
  4. async def foo(): 
  5.     for i in range(22): 
  6.         yield i 
  7.  
  8.  
  9. async def run(): 
  10.     index = 0 
  11.     limit = 5 
  12.  
  13.     while True
  14.         xs = stream.iterate(foo()) 
  15.         ys = xs[index:index + limit] 
  16.         t = await stream.list(ys) 
  17.         if not t: 
  18.             break 
  19.         print(t) 
  20.         index += limit 
  21.  
  22.  
  23. if __name__ == '__main__'
  24.     asyncio.run(run()) 

 

责任编辑:华轩 来源: Python学习开发
相关推荐

2009-09-04 10:27:28

Linux实用技巧linux操作系统linux

2022-03-23 09:18:10

Git技巧Linux

2009-12-21 15:50:39

2011-04-08 15:40:01

Oracle认证

2009-01-03 09:34:30

ASP.NET.NET性能优化

2024-05-17 08:52:43

SQL实用技巧行列转换

2022-11-03 10:28:59

PandasSAC机制

2022-10-11 08:00:47

多线程开发技巧

2024-05-22 09:29:43

2009-12-09 11:21:30

Linux实用技巧

2010-10-08 15:44:17

vim

2019-12-22 23:10:19

LinuxSSH加密

2010-09-14 10:41:24

DIV+CSS排版

2009-12-23 17:32:35

Linux构建软路由

2019-10-10 16:31:51

PyCharmPythonWindows

2023-04-26 00:34:36

Python技巧程序员

2011-06-21 10:02:29

Python

2018-05-10 17:39:13

Python 机器学习编程语言

2024-04-16 08:24:58

Python_str__()方法字符串

2021-11-15 10:02:16

Python命令技巧
点赞
收藏

51CTO技术栈公众号