python项目实战——多协程下载美女图片

协程

文章目录

  • 协程
  • 协程的优劣势
  • 什么是IO密集型任务
      • 特点
      • 示例
      • 与 CPU 密集型任务的对比
      • 处理 I/O 密集型任务的方式
      • 总结
  • 创建并使用协程
    • asyncio模块
  • 创建协程函数
  • 运行协程函数asyncio.run(main())
  • aiohttp模块
    • 调用aiohttp模块步骤
  • aiofiles————协程异步函数
  • 遇到的问题
  • 一 await asyncio.wait(task)的作用
      • 1. **理解 `await` 的作用**
      • 2. **`asyncio.wait(task)` 的作用**
      • 3. **启动多个任务**
  • 多协程案例——爬取图片
    • 完整代码
    • 设计思路
    • 按顺序解读代码
    • 主函数

🌟 亲爱的读者们, 🌟

感谢你们访问我的博客!如果你希望更深入、透彻地了解文章中的内容,欢迎随时私信我。我可以为你录制视频讲解,让知识变得更加生动有趣。

当然,若你们对这个想法感兴趣的朋友们越多,我会更有动力去制作这些视频哦!让我们一起探索更多的知识,共同成长!

期待你的私信!📩

协程(coroutine):也叫作微线程,纤程,协程是单线程下的并发。
协程的作用:在执行函数A时,随时中断去执行函数B,然后再中断函数B.返回来执行函数A,该操作类似多线程,但协程中只有一个线程在执行。
微观上:一个一个任务的切换,切换条件一般是IO操作
宏观上:是多个任务在同时执行——多任务异步操作

协程的优劣势

  • 协程的优势:

    非常适用于I/O密集型任务;

    执行效率高(切换函数,而不切换线程,没有多余开销)

    不需要锁机制。

  • 协程的劣势:

    无法利用多核资源

    进行阻塞操作会阻塞掉整个程序。

什么是IO密集型任务

I/O 密集型任务(I/O-bound task)是指那些在执行过程中主要受限于输入/输出(I/O)操作性能的任务。这类任务的执行效率往往取决于数据的读写速度,而不是 CPU 处理能力。

特点

  1. 依赖于外部设备:I/O 密集型任务常常涉及与外部设备的交互,如硬盘、网络、数据库等。
  2. 等待时间长:由于 I/O 操作(如文件读取、网络请求)通常比 CPU 操作慢,I/O 密集型任务在执行时可能会有较长的等待时间。
  3. CPU 空闲:在等待 I/O 操作完成时,CPU 可能处于空闲状态,未能充分利用计算资源。

示例

  • 文件读取/写入:长时间读取或写入大文件。
  • 网络请求:等待远程服务器的响应,如 HTTP 请求。
  • 数据库操作:查询或写入数据库的过程,这些操作通常涉及网络通信和磁盘访问。

与 CPU 密集型任务的对比

  • I/O 密集型任务:主要依赖于 I/O 操作,CPU 通常在执行期间处于等待状态。
  • CPU 密集型任务:主要依赖于计算和处理,通常会占用大量的 CPU 资源。

处理 I/O 密集型任务的方式

  • 采用 异步编程:如使用回调或 Promises 来处理 I/O 操作,而不是阻塞主线程。
  • 利用 多线程或多进程:同时处理多个 I/O 操作,以减少整体等待时间。

总结

I/O 密集型任务是在执行中高度依赖于数据交换和处理速度的任务。理解 I/O 密集型任务的概念有助于在设计和优化系统时选择合适的编程模型和架构。

因为爬虫主要就是和网络进行交互,不太消耗我们自己电脑的CPU,负责接收网路返回的数据,是一种写入操作,就是适合爬虫

创建并使用协程

asyncio模块

python的标准库

  • 创建协程函数
    async def function()
    await IO操作
  • 调用协程函数
    async def main()
    await function()
  • 并发执行多个协程:
    asyncio.create task()
  • 运行协程:
    asyncio.run(main())
import asyncio  # 导入模块
import time

# 创建协程函数
async def singing():
    print('start singing')
    time.sleep(2)
    print('end singing')

async def dancing():
    print('start dance')
    time.sleep(3)
    print('end dance')

async def main():
    # 调用协程函数
    await singing()
    await dancing()

if __name__ == '__main__':
    start_time = time.time()
    # 运行协程函数
    asyncio.run(main())
    end_time = time.time()
    print(f'time:{end_time-start_time}')
    
start singing
end singing
start dance
end dance
time:5.002954006195068
发现是5秒,就是串行

上面的这个程序是协程程序,但是是单协程,也就是串行,一个一个来

创建协程函数

asyncio.create_task(singing())

await asyncio.wait([task1,task2])

import asyncio  # 导入模块
import time


# 创建协程函数
async def singing():
    print('start singing')
    await asyncio.sleep(2)
    print('end singing')

async def dancing():
    print('start dance')
    await asyncio.sleep(3)
    print('end dance')

async def main():
    # 使用协程里面的任务函数,来创建并发多协程
    task1 = asyncio.create_task(singing())
    task2 = asyncio.create_task(dancing())
    await asyncio.wait([task1,task2])

if __name__ == '__main__':
    start_time = time.time()
    # 运行协程函数
    asyncio.run(main())
    end_time = time.time()
    print(f'time:{end_time-start_time}')
    
start singing
start dance
end singing
end dance
time:3.0030245780944824
和多线程一样,都是有一个模块自己的函数,来启动线程/协程

time是一个同步函数,不能让CPU异步执行,即便是使用了并发执行多任务函数,CPU也不能同时运行

使用await调用协程函数自带的sleep函数

CPU遇到await语句,并且发生了堵塞,比如网络请求还没到,或者这个sleep等待函数未执行完,CPU都会自己切换出去

运行协程函数asyncio.run(main())

import asyncio  # 导入模块
import time

# 创建协程函数
async def singing():
    print('start singing')
    await asyncio.sleep(2)
    print('end singing')

async def dancing():
    print('start dance')
    await asyncio.sleep(3)
    print('end dance')

async def main():
    # 和多线程一样,可以把要启动的多协程任务放到一个列表里面,最后在启动列表
    task = []
    for i in range(3):
        task.append(asyncio.create_task(singing()))
        task.append(asyncio.create_task(dancing()))

    await asyncio.wait(task) # 启动协程 

if __name__ == '__main__':
    start_time = time.time()
    # 运行协程函数
    asyncio.run(main())
    end_time = time.time()
    print(f'time:{end_time-start_time}')
    
start singing
start dance
start singing
start dance
start singing
start dance
end singing
end singing
end singing
end dance
end dance
end dance
time:3.003164768218994

和多线程一样,先是用asyncio模块下面的create task也就是字面意思,创建一个多协程任务,把协程函数写在里面

外面使用for循环,控制循环次数,也就是协程的数量,每循环一次,就会在这个create task里面多两个任务

所以循环了三次,就有6个任务被创建

因为这是协程任务,里面的sleep就是阻塞,所以程序一直在切换协程

aiohttp模块

相当于异步requests模块,适合在协程中使用使用方法类似 requests模块

官网:https://docs.aiohttp.org/en/stable/client_quickstart.html

请添加图片描述

requests是同步模块在协程中不能及时的释放CPU,所以需要使用协程专用的aiohttp模块

调用aiohttp模块步骤

  1. async with 是关键字
  2. aiohttp.ClientSession() 这是一个方法,然后起名字
  3. async with aiohttp.ClientSession() as session:
  4. async with session.get(url) as r: 前面两个是关键字,后面的是请求方式,和requests.get()一样
  5. 返回方式有三种:text,content,json
  6. 这里打印返回值的时候还要在后面加一个括号,r.text(),r.centent.read(),调用centent的时候还要加一个read()后缀,r.json()
import asyncio
import aiohttp

async def downloads(url):
    # print(url)
    name = url.split('/')[-1]
    # print(name)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as r: # 这里是把得到的网络请求内容赋值给r
            with open(fr'E:\python笔记\{name}','wb') as f:
                f.write(await r.content.read()) # 这里需要使用等待关键字,因为是网络请求,网络响应的速度比不上CPU运算的速度
                print(f'已完成……{name}')

async def main():
    urls = ('https://i1.huishahe.com/uploads/allimg/202302/9999/bcc80e5d24.jpg',
           'https://i1.huishahe.com/uploads/allimg/202302/9999/8ccff5510c.jpg',
           'https://i1.huishahe.com/uploads/allimg/202302/9999/186094180f.jpg')
    task = []
    for url in urls:
        task.append(asyncio.create_task(downloads(url)))  # 把协程任务存进列表

    await asyncio.wait(task) # 启动列表内的任务,并告诉协程这是一个等待语句


if __name__ == '__main__':
    asyncio.run(main())

设计思路:

  1. 设计协程函数入口
    1. 把协程函数写入协程任务
    2. 使用await 运行协程任务
  2. 设计协程函数
    1. async with aiohttp.ClientSession() as session这个是aiohttp的方法,必须要写
    2. async with session.get(url) as r 这个就是异步网络请求了,返回内容在r里面

aiofiles————协程异步函数

也是协程里面的函数,也是异步函数,可以主动等待网络的返回值

import asyncio
from os import write
import aiofiles
import aiohttp

async def downloads(url):
    # print(url)
    name = url.split('/')[-1]
    # print(name)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as r: # 这里是把得到的网络请求内容赋值给r
            # with open(fr'E:\python笔记\{name}','wb') as f:
            #     f.write(await r.content.read()) # 这里需要使用等待关键字,因为是网络请求,网络响应的速度比不上CPU运算的速度
            #     print(f'已完成……{name}')
            async with aiofiles.open(fr'E:\python笔记\{name}','wb') as f:
                await f.write(await r.content.read())
                print(f'已完成……{name}')

和原来的文件保存方式相似,就是换了个关键字

  1. 因为要使用协程函数,所以要使用async关键字
  2. 因为网络请求需要等待,可能阻塞,还要写await让程序挂起

遇到的问题

一 await asyncio.wait(task)的作用

在使用 Python 的 asyncio 库时,协程的调度和执行是通过事件循环来处理的。await asyncio.wait(task) 是用来等待一组任务完成的关键工具。下面是它的重要性和工作原理的详细解释:

1. 理解 await 的作用

  • await 用于暂停协程的执行,直到被 await 的任务完成。它允许事件循环在等待的同时执行其他任务,从而提高效率。
  • async 函数中, 使用 await 的时候,协程会被挂起,控制权返回到事件循环,从而允许其他协程运行。

2. asyncio.wait(task) 的作用

  • asyncio.wait 是一个并发等待工具,它接受一个任务或任务列表,直到这些任务中的一个或全部完成。它返回一个集合,包含已完成和未完成的任务,方便后续处理。
  • 通过等待任务的完成,我们可以获得每个任务的结果或获取异常信息。

3. 启动多个任务

  • 使用 asyncio.wait 启动和管理多个协程任务。例如,当有多个协程需要并发执行时,可以将它们封装在一个任务列表中,然后调用 asyncio.wait 来调度。
  • 这样可以让我们更好地控制任务的执行,以及处理结果和异常。

多协程案例——爬取图片

效果图

请添加图片描述

请添加图片描述

完整代码

import pprint

import aiofiles
import aiohttp
import asyncio

# 使用BS4解析网页
from bs4 import BeautifulSoup
from requests.packages import target


# 获取翻页地址
async def get_all_url(url):
    # print(url)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as r:
            # print(await r.text())  # 可等待对象-----网页源代码
            soup = BeautifulSoup(await r.text(),'html.parser')
            links = soup.find('div',class_='slist').find_all('a',target="_blank") # 从目录页得到的总链接
            return_links = []
            for link in links:
                return_links.append(link['href'])

    return return_links


# 获取目录页图片地址
async def get_url(url):
    # print(url)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as r:
            soup = BeautifulSoup(await r.text(), 'html.parser')
            pic_address = soup.find('div',class_="photo-pic").find('img')['src']
            title = soup.find('div',class_="photo-pic").find('img')['title']
            # print(pic_address,title)
    return pic_address,title

# 下载图片
async def download_pic(jpg_address,title):
    # print(jpg_address,title)
    async with aiohttp.ClientSession() as session:
        async with session.get(jpg_address) as r:
            # print(r) # ok
            async with aiofiles.open(fr'E:\python笔记\{title}.jpg','wb') as f:
                await f.write(await r.content.read())
                print(f'已完成……{title}')

async def main():
    task1 = []
    for i in range(1,2):
        if i == 1:
            url = 'https://www.moyublog.com/hdwallpapers/meinv/index.html'
        else:
            url = f'https://www.moyublog.com/hdwallpapers/meinv/index_{i}.html'
        task1.append(asyncio.create_task(get_all_url(url)))
    dones,pending = await asyncio.wait(task1) # await 关键字会返回两个内容,第一个是在函数里面return的

    # for link in dones: # 这里的dones返回的是一个列表,在改变目录页数的时候,列表的数量也会改变
    #     print(link.result()) # 当你发起异步操作时,该操作可能尚未完成。任务对象在执行完成前只是一个占位符。只有在它们完成后,调用 result() 才能获取实际的返回值。
    # 不调用 result() 直接打印任务对象,会返回任务的信息(如状态),而不是实际的返回值。

    task2 = []
    for link in dones:
        pages = link.result() # 把一个列表储存在pages里面
        for rt in pages: # 这里是在使用rt读取一个pages列表,最后打印出来
            # print(rt)  # 发现得到网页正常
            task2.append(asyncio.create_task(get_url(rt)))
    dones,pending = await asyncio.wait(task2)

    task3 = []
    for t in dones:
        # print(t.result())  # 发现返回的内容都在元组里面
        jpg_address,title = t.result()  # 可以直接自定义两个参数接收元组里面的内容
        # print(jpg_address,title) # ok
        task3.append(asyncio.create_task(download_pic(jpg_address,title)))
    await asyncio.wait(task3)

if __name__ == '__main__':
    asyncio.run(main())

设计思路

  1. 找个比较合适的网站
  2. 找个你感兴趣的分类
  3. 获取目录页网址,自己找规律组合其他的目录页
  4. 获取目录页里面的每一种图片网址
  5. 从图片的网址找到图片的链接
  6. 下载并保存图片

按顺序解读代码

先要清楚,这里是多协程代码,会有网络请求等语句,也就是程序堵塞的地方,都要使用aio的关键字,比如异步网络请求aiohttp,异步文件储存aiofiles,异步函数执行async,异步函数的运行asyncio.wait,还有异步任务的创建asyncio.creata_task()

  1. 找目录页里面的图片链接的时,首先按照aiohttp.ClientSession()的方法,创建session,在使用sesssion.get的方式向网络发送请求,这里的session.get的用法和requests.get一样
  2. 在上一步已经把返回的内容储存在对象r里面了
  3. 这里使用了BS4的语法进行文件解析,也算是练习一下BS4的语法
  4. 在这里获取到了图片页的链接

我们获得了图片页的链接和名称,这个协程的任务就结束了,现在我们把这两个内容返回出去

这是一个协程函数,它的返回内容有两个dones和pending,前者是已完成的内容,后者是未完成的内容

想要正常获取协程函数的返回值,我们需要使用这两个参数结束返回值,后面的参数不用管

我们对前面的参数使用result()方法解析,就可以正常获取内容了

这里我们得到了很多列表,也就是一个目录页里面的所有链接在一个列表里面

把这个列表传递给下一个函数get_url这个函数的工作是专门从图片页的链接得到图片的jpg文件和名称

同样也是协程函数,和上面的处理方式一样

再把图片的jpg链接和名称给下载及保存图片的函数,这里就使用了异步文件保存的方式,因为网络请求有一定的延迟,还要使用await关键字

主函数

我们使用for循环构造目录页的链接,并存入协程任务

然后是把链接给处理目录页的函数

再把返回内容给图片页函数

再把返回内容给下载图片的函数

这些都是异步协程操作,也就是I/O密集型任务

所以可以在极短的时间保存大量的图片,比之前的多线程快了很多

awaitasyncio.wait 中并不会直接返回 donespendings 两个参数。相反,await asyncio.wait(...) 返回的是一个包含两个集合的元组

  • done: 包含所有已完成的 Task 对象。
  • pending: 包含所有未完成的 Task 对象。

研究的好几天才能较为熟练的使用协程函数,就是比较麻烦,但是因为是异步加载的请求,所以可以不用在等待网络返回内容时一直占用CPU了,CPU会去执行其他的协程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/902994.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux系统编程】——探索Shell:工作原理与运行机制以及Linux的权限管理

文章目录 1. 什么是 Shell?2. Shell 的工作原理3. Shell 的运行机制4. Shell 的应用场景5. Shell 脚本的优缺点Linux权限的概念Linux权限管理文件权限值的表示方法文件访问权限的相关设置方法 目录的权限粘滞位关于权限的总结 1. 什么是 Shell? Shell 是…

Linux下的文件系统(进程与文件)

windows下的文件构成 .内容 .属性 所以, 文件的构成为内容和属性。 文件 内容 属性 推此即彼, linux下的文件构成也是如此。 liunx下,文件 文件的内核数据结构(属性)内容 深入理解c语言中的文件操作 在c语言中如…

【笔记】LLM位置编码之标准位置编码

标准位置编码 起源原理证明:对于任何固定的偏移量 k k k, P E p o s k PE_{posk} PEposk​可以表示为 P E p o s PE_{pos} PEpos​的线性函数。计算 P E p o s k 与 P E p o s PE_{posk} 与PE_{pos} PEposk​与PEpos​的内积结论 通俗理解缺点 起源 由…

论文笔记:LaDe: The First Comprehensive Last-mile Delivery Dataset from Industry

2023 KDD 1 intro 1.1 背景 随着城市化进程的加快和电子商务的发展,最后一公里配送已成为一个关键的研究领域 最后一公里配送,如图1所示,是指连接配送中心和客户的包裹运输过程,包括包裹的取件和配送除了对客户满意度至关重要外…

诺基亚的裁员风暴

大家好,我是鸭鸭! 不知道 80、90 后还记得童年神机诺基亚吗? 虽然诺基亚早就把自家手机业务出售,但依然是一代通信巨头。 鸭鸭最近看到新闻,诺基亚已经在大中华区裁减了近 2000 名员工 。 根据 2023 年底&#xff0…

YOLOv8实战野生动物识别

本文采用YOLOv8作为核心算法框架,结合PyQt5构建用户界面,使用Python3进行开发。YOLOv8以其高效的实时检测能力,在多个目标检测任务中展现出卓越性能。本研究针对野生动物数据集进行训练和优化,该数据集包含丰富的野生动物图像样本…

9.Linux按键驱动-工作队列

1.思路 1.1在gpio结构体中定义工作队列 1.2 在probe函数中初始化工作队列 1.3.在中断服务程序中调度工作队列 1.4工作队列处理函数&#xff1a; 2.编程 程序&#xff1a; #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h> #…

C语言程序设计:现代设计方法习题笔记《chapter6》下篇

第七题 square3.c代码 #include<stdio.h>int main() { int i, n, odd, square;printf("This program prints a table of squares.\n");printf("Enter number of entries in table: ");scanf_s("%d", &n);i 1;odd 3;for (square 1;…

数据库课程 第一周

1.数据库的安装与卸载 1.1数据库的卸载&#xff1a; &#xff08;1&#xff09;第一种卸载方式&#xff1a;删除文件目录 &#xff08;2&#xff09;第二种卸载方式&#xff1a;在控制面版中卸载&#xff0c;然后在c盘里找到mysql文件删除 1. 2.在隐藏目录programdata里 1.2…

新王Claude 3.5的6大应用场景

Anthropic AI深夜发布了备受期待的Claude 3.5系列更新&#xff0c;包括了全新升级的Claude 3.5 Sonnet和首发的Claude 3.5 Haiku。 Claude 3.5 Sonnet能够理解细微的指令和上下文&#xff0c;识别并纠正自身错误&#xff0c;还能从复杂数据中生成深入的分析和洞察。 结合最先进…

从零入门扣子Bot开发

从零入门扣子Bot开发 工作流简单介绍问题思考工作流实例 图像流简单介绍瘦脸图像流的设计创建图像流设计流程 总结参考链接 工作流简单介绍 工作流起源于生产组织和办公自动化领域&#xff0c;是指在计算机应用环境下&#xff0c;对业务过程的部分或整体进行自动化处理。它通过…

文理学院数据库应用技术实验报告0

文理学院数据库应用技术实验报告0 实验内容 打开cmd,利用MySQL命令连接MySQL服务器。 mysql -u root -p查看当前MySQL服务实例使用的字符集(character)。 SHOW VARIABLES LIKE character_set_server;查看当前MySQL服务实例支持的字符序(collation)。 SHOW VARIABLES LIKE c…

Unity编辑器 连接不到SteamVR问题记录

问题表现&#xff1a;之前正常的工程&#xff0c;某天打开后运行&#xff0c;在SteamVR未打开时&#xff0c;Unity工程运行后无法调用起来Steam VR&#xff0c;无任何反应&#xff0c;但用其他软件则可以调用起来SteamVR&#xff0c;并且运行正常&#xff0c;在重装了XR的一些插…

【网络面试篇】从输⼊ URL 到⻚⾯展示到底发⽣了什么?

目录 一、大致流程 1. 流程概述 2. 全流程描述 二、流程解析 1. URL 解析 2. DNS 查询 3. TCP 连接 4. 渲染页面 一、大致流程 1. 流程概述 &#xff08;1&#xff09;URL 解析 &#xff08;2&#xff09;DNS 查询 &#xff08;3&#xff09;TCP 连接 &#xff08…

「C/C++」C/C++标准库之#include<cstdlib>通用工具库

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

消息会话—发送消息自动滚动到最底部

背景 在项目开发中&#xff0c;实现用户友好的输入交互是提升用户体验的关键之一。例如&#xff0c;在消息会话页面中&#xff0c;为了确保用户在发送新消息后页面能自动滚动到最底部&#xff0c;从而始终保持最新消息的可见性&#xff0c;需要实现自动滚动功能。这不仅提升了…

【教程】如何查看IEEE会员证书Membership Card

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 1、先打开以下网站&#xff0c;并登录你的账号&#xff1a; https://www.ieee.org/profile/myprofile/myprofile.html 2、如果你没有缴费注册会员&…

ENGAGE SHE连锁品牌盛启,寻找更多城市合伙人

在这个充满个性与品质追求的时代,饰品已悄然超越了单纯装饰的范畴,它成为了每个人独特个性的展现,是生活态度的鲜明宣言。自2021年成立以来,ENGAGE SHE凭借其“自在、自然、清新”的独特品牌风格,以及“简约、品质、设计”的核心理念,迅速在时尚界掀起了一股清新之风,赢得了无数…

Segugio:一款针对恶意软件的进程执行跟踪与安全分析工具

关于Segugio Segugio是一款功能强大的恶意软件安全分析工具&#xff0c;该工具允许我们轻松分析恶意软件执行的关键步骤&#xff0c;并对其进行跟踪分析和安全审计。 Segugio允许执行和跟踪恶意软件感染过程中的关键步骤&#xff0c;其中包括从点击第一阶段到提取恶意软件的最…

中航资本:什么是主板创业板及科创板?主板创业板及科创板有什么区别?

什么是主板创业板及科创板&#xff1f; 主板、创业板和科创板都是股票商场的组成部分。 主板商场分为沪市主板和深市主板&#xff0c;首要服务与有安稳的盈利才华、较大的本钱规划和较强的商场竞争力的企业。 创业板首要服务于成长型、中小型、高新技术企业等&#xff0c;包…