Python 大任务切分小任务

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

列表切分

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

import os
import time CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) def read_file():
file_path = os.path.join(CURRENT_DIR, "url_list.txt")
with open(file_path, "r", encoding="utf-8") as fs:
result = [i.strip() for i in fs.readlines()]
return result def fetch(url):
print(url) def run():
max_count = 5
url_list = read_file()
for index in range(0, len(url_list), max_count):
start = time.time()
fetch(url_list[index:index + max_count])
end = time.time() - start
if end < 1:
time.sleep(1 - end) if __name__ == '__main__':
run()

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

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

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

生成器切分

# -*- coding: utf-8 -*-
# @时间 : 2019-11-23 23:47
# @作者 : 陈祥安
# @文件名 : g.py
# @公众号: Python学习开发
import os
import time
from itertools import islice CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) def read_file():
file_path = os.path.join(CURRENT_DIR, "url_list.txt")
with open(file_path, "r", encoding="utf-8") as fs:
for i in fs:
yield i.strip() def fetch(url):
print(url) def run():
max_count = 5
url_gen = read_file()
while True:
url_list = list(islice(url_gen, 0, max_count))
if not url_list:
break
start = time.time()
fetch(url_list)
end = time.time() - start
if end < 1:
time.sleep(1 - end) if __name__ == '__main__':
run()

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

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

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

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

异步生成器切分

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

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

def foo():
for i in range(20):
yield i

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

完整示例代码如下

import asyncio
async def foo():
for i in range(20):
yield i async def run():
async_gen = foo()
async for i in async_gen:
print(i) if __name__ == '__main__':
asyncio.run(run())

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

import asyncio
from aiostream import stream async def foo():
for i in range(22):
yield i async def run():
index = 0
limit = 5 while True:
#测试发现foo应该是个callable类型,并且运行之后是个async_generator。
xs = stream.iterate(foo())
ys = xs[index:index + limit]
t = await stream.list(ys)
if not t:
break
print(t)
index += limit
await asyncio.sleep(0.01) if __name__ == '__main__':
asyncio.run(run())

python实用技巧之任务切分的更多相关文章

  1. Python 实用技巧

    模块相关 导入模块时,可以通过模块的 __file__ 属性查看模块所在磁盘的路径位置,参考:关于Python包和模块的10个知识清单 Pip 安装Pip 方法一: sudo apt-get purg ...

  2. Python实用技巧

    1.改变工作目录 import os os.chdir('C:/Users/Mr.Zhao') 2.搜索制定目录下的文件 1 import glob 2 glob.glob('C:/User/Mr.Z ...

  3. python 实用技巧:几十行代码将照片转换成素描图、随后打包成可执行文件(源码分享)

    效果展示 原始效果图 素描效果图 相关依赖包 # 超美观的打印库 from pprint import pprint # 图像处理库 from PIL import Image # 科学计算库 imp ...

  4. python实用技巧 : Filtering os.walk(转)

    ''' Created on Mar 7, 2010 @author: Diego 需求: 得到某个目录下, 符合过滤条件的文件夹/文件.实现: 将os.walk再次包装. TODO: 不知道本程序的 ...

  5. python实用30个小技巧

    python实用30个小技巧 展开1.原地交换两个数字Python 提供了一个直观的在一行代码中赋值与交换(变量值)的方法,请参见下面的示例: In [1]: x,y = 10 ,20 In [2]: ...

  6. 「Python实用秘技07」pandas中鲜为人知的隐藏排序技巧

    本文完整示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/PythonPracticalSkills 这是我的系列文章「Python实用秘技」的第7期 ...

  7. iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示

    iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示 本文介绍其简单使用: 第一步:在本地建立一个访问的服务端.  打开本地终端,在本地新建一个文件夹,在该文件夹中存放测试的html页面.   ...

  8. JavaScript函数作用域与对象以及实用技巧

    1. JS作用域 1.1 全局作用域和局部作用域 函数外面声明的就是 全局作用域 函数内是局部作用域 全局变量可以直接在函数内修改和使用 变量,使用var是声明,没有var是使用变量. 如果在函数内使 ...

  9. 「Python实用秘技01」复杂zip文件的解压

    本文完整示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/PythonPracticalSkills 这是我的新系列文章「Python实用秘技」的第1 ...

随机推荐

  1. React Native 开发豆瓣评分(二)路由配置

    路由管理使用官方推荐的 React Navigation; 配置环境 安装相关依赖 yarn add react-navigation react-native-gesture-handler Lin ...

  2. flashdevelop调用ios方法

    来源:http://blog.csdn.net/zu12jing/article/details/7331397 flash开发工具用的是flashdevelop(由于flashdevelop还能直接 ...

  3. uc/xi

    一个较为通用的定义为:嵌入式系统是对对象进行自动控制而使其具有智能化并可嵌入对象体系统中的专用计算机系统. 实时性:目前,嵌入式系统广泛应用于生产过程控制.数据采集.传输通信等场合,这些应用的共同特点 ...

  4. python之编码与解码、is 与==的区别

    一.编码与解码 编码的过程其实就是采用一定的编码格式将unicode字符转换成str字符的过程 非ASCII码字符按字节为单位被编码成十六进制转义字符 解码采用的编码格式跟设置和环境有关 ascii ...

  5. containerd简述

    containerd是容器虚拟化技术,从docker中剥离出来,形成开放容器接口(OCI)标准的一部分. docker对容器的管理和操作基本都是通过containerd完成的.Containerd 是 ...

  6. 【Flask】 python学习第一章 - 2.0 视图方式和返回值

    路由参数与请求方式制定   ALT+回车 pycharm 快速导包  demo3  指定访问地址  与请求方式  #   给路由传入参数 使用尖括号 ,视图函数需要接收参数 @app.route(&q ...

  7. 运维开发笔记整理-django日志配置

    运维开发笔记整理-django日志配置 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Django日志 Django使用python内建的logging模块打印日志,Pytho ...

  8. C++(三十七) — 字符串的函数重载—案例

    1.MyString.h 头文件 #pragma once #include <iostream> using namespace std; class MyString { public ...

  9. 利用Python openpyxl操作Excel

    from IPython.core.interactiveshell import InteractiveShell InteractiveShell.ast_node_interactivity = ...

  10. react 学习记录

    1.  脚手架搭建项目  create-react-app https://www.jianshu.com/p/d196761c8332 2. UI框架 https://ant.design/docs ...