Python爬虫中的协程

news2024/10/2 8:36:09

协程

基本概念

协程:当程序执行的某一个任务遇到了IO操作时(处于阻塞状态),不让CPU切换走(就是不让CPU去执行其他程序),而是选择性的切换到其他任务上,让CPU执行新的任务,当原来的任务不处于阻塞状态后,CPU可以快速的回到之前的任务继续执行,这样就不用让原本的程序去排队等待CPU调度。

微观上看,任务是一个一个的切换执行,切换条件就是某一个任务有IO操作, 而宏观上,我们看到的是多个任务一起执行,这就是多任务异步操作。上面的一切的前提就是单线程的情况下,因为多线程可以多个线程同时干多件事。

import time


def func():
    print('first, hi!')
    # 让程序睡眠3秒钟,此时线程处于阻塞状态,CPU不为线程工作
    # 当我们爬取一个网页时,向一个url发送请求,会通过网络传输将请求发送到服务器
    # 然后服务器会处理请求、准备数据、将数据通过网络传输回客户端等工作
    # 这一系列的操作也会耗费时间,所以在从发送请求开始,到接收服务器返回的数据这一段时间内
    # 即在网络请求返回数据之前,程序也处于阻塞状态
    # 程序进行处于IO操作时是处于阻塞状态的
    time.sleep(3)
    print('second, hello..')


if __name__ == '__main__':
    func()

协程和线程的区别(个人理解)

昨天仔细想的时候,感觉协程和线程很像,不知道它们之间的区别在哪,然后百度了一下,在这里说一下自己的理解(很不官方,不懂的可以百度一下,别人写的会比较详细和专业)。

打个比喻,一个公司有很多员工,老板给每个员工分配任务,员工之间各有分工,每个人负责自己的工作,如果遇到一个很会压榨人的老板,就会给每个员工安排很多任务。员工在完成自己分配到的多个任务时,因为自己只有一个人,不能同时把多个任务一起干,所以肯定是某个时刻内只干一件事。但是为了提高工作效率,在某个任务需要等待时,员工肯定不能傻傻的等着,而是利用这个等待的时间去干另一个任务(毕竟手上被万恶的资本家分配了很多活),比如正在跑的一个程序A要运行很久,那么在这个程序A运行的时间里,员工肯定去写另一个程序B了,如果这个程序B写完后也要运行很久,那么员工就会去完成程序C,或者此时程序A运行完了,接着完成程序A.....

上面所说的一个公司有多个员工,那么每个员工相当于一个线程,多个员工各有分工干自己的活,就是多个线程之间独立完成自己的工作。而一个员工充分利用时间完成多个任务(从一段时间上看(宏观),如一周内,员工同时完成多个任务,但是实际上(微观),某个时刻员工只做一件事),这每一个任务就是协程,所以协程实际上是一个单线程,宏观上同步完成多个任务,微观上异步完成多个任务。

协程可以充分的让一个线程忙起来,提高效率,不然当某个任务阻塞时,线程就处于空闲的等待状态,这使得线程资源没有得到充分利用,执行效率也大打折扣,就像老板想让打工人一刻都不停的给他创造价值一样。

用Python编写协程的程序

单个异步任务

有四种方式,但这里只选择其中的一种,如以下代码所示:

import asyncio


# 这种写法就是普通的函数
# def func():
#     print('你好,我是张三!')
#
#
# if __name__ == '__main__':
#     func()

# 在函数前面加async关键字,就表明该函数是异步协程函数
async def func():
    print('你好,我是张三!')

if __name__ == '__main__':
    # func()  # 如果直接调用,会得到一个警告:RuntimeWarning: ...
    g = func()  # 此时函数是一个异步协程函数,执行函数得到一个协程对象
    """
    输出:
    <coroutine object func at 0x000001F823066960>
    sys:1: RuntimeWarning: coroutine 'func' was never awaited
    """
    print(g)
    asyncio.run(g)  # 协程程序的运行需要asyncio模块的支持

多个异步任务

import asyncio
import time


# 在函数前面加async关键字,就表明该函数是异步协程函数
async def func1():
    print('你好,我是张三!')
    time.sleep(3)
    print('你好,我是张三!')

async def func2():
    print('你好,我是李四!')
    time.sleep(2)
    print('你好,我是李四!')

async def func3():
    print('你好,我是王五!')
    time.sleep(4)
    print('你好,我是王五!')

if __name__ == '__main__':
    f1 = func1()
    f2 = func2()
    f3 = func3()
    # 把多个异步任务放到一个列表中
    tasks = [f1, f2, f3]
    t1 = time.time()
    # 一次性启动多个异步任务(协程)
    asyncio.run(asyncio.wait(tasks))
    t2 = time.time()
    print(t2 - t1)

上面三个函数是异步协程操作,理论上执行时间应该会小于9秒,因为异步任务会在某一个任务阻塞时去调用其他任务,但是观察上述代码执行时间,发现和同步执行三个函数效果一样,都是用了9秒多,如下图。出现这种的情况的原因是:函数里的time.sleep()是同步操作,而异步协程函数中出现同步操作的时候,异步就中断了,也就是说,当异步函数中有同步操作时,CPU不会切换去调用其他任务,而是像同步函数那样,执行完一个任务再去执行另一个任务(在这个例子中,就是执行完func1,再执行func2,再执行func3)。

修改上述代码,实现异步操作效果,如下:

import asyncio
import time


# 在函数前面加async关键字,就表明该函数是异步协程函数
async def func1():
    print('你好,我是张三!')
    # time.sleep(3)  # 异步程序中出现同步操作,会中断异步,即不会切换任务执行
    # 异步操作代码,表示挂起任务,让任务睡眠3秒,然后切换CPU去执行其他任务
    await asyncio.sleep(3)
    print('你好,我是张三!')

async def func2():
    print('你好,我是李四!')
    # time.sleep(2)
    await asyncio.sleep(2)
    print('你好,我是李四!')

async def func3():
    print('你好,我是王五!')
    # time.sleep(4)
    await asyncio.sleep(4)
    print('你好,我是王五!')


# 一般不会直接像下面那样调用多个异步任务,而是把它包装在一个异步协程函数里
# if __name__ == '__main__':
#     f1 = func1()
#     f2 = func2()
#     f3 = func3()
#     # 把多个异步任务放到一个列表中
#     tasks = [f1, f2, f3]
#     t1 = time.time()
#     # 一次性启动多个异步任务(协程)
#     asyncio.run(asyncio.wait(tasks))
#     t2 = time.time()
#     print(t2 - t1)

async def main():
    # 写法一(不推荐)
    # await 都是写在异步协程函数里,即与async配套使用
    # await后一般跟协程对象、task等对象
    # await表示挂起某个异步任务,即是执行某个异步任务
    # await asyncio.create_task(func1())
    # await asyncio.create_task(func2())
    # await asyncio.create_task(func3())

    # 写法二(推荐)
    tasks = [
        # asyncio.create_task(func1()) 把协程对象包装成task对象
        asyncio.create_task(func1()),
        asyncio.create_task(func2()),
        asyncio.create_task(func3())
    ]
    # 这里await作用和上面一样,表示挂起协程对象,即会异步执行tasks列表中的异步任务
    await asyncio.wait(tasks)

if __name__ == '__main__':
    t1 = time.time()
    asyncio.run(main())
    t2 = time.time()
    print(t2 - t1)

使用异步模拟爬虫程序

import asyncio

async def download(url):
    print('开始下载...')
    await asyncio.sleep(2)
    print('下载完成!')

async def main():
    tasks = []
    urls = ['url1', 'url2', 'url3']
    for url in urls:
        d = download(url)  # 得到一个异步协程对象
        # asyncio.create_task(d) 把协程对象包装成task对象
        tasks.append(asyncio.create_task(d))
    await asyncio.wait(tasks)

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

异步发送http请求

以下代码是根据多个图片地址异步下载图片

import asyncio
# 下载命令:pip install aiohttp
import aiohttp

# 图片地址
urls = [
    "https://img95.699pic.com/photo/50165/7667.jpg_wh860.jpg",
    "https://bpic.588ku.com/back_origin_min_pic/20/04/19/f753e29e3dbe2ad75b8f6d6053199faa.jpg"
]

async def download(url):
    file_name = url.rsplit('/', 1)[1]
    # aiohttp.ClientSession()对象等价于requests模块,所以也有get、post方法
    # 且用法差不多
    async with aiohttp.ClientSession() as req:  # => req = aiohttp.ClientSession()
        # 因为是异步操作,所以要加上async关键字
        # with的作用和文件操作中的with类似,可以管理上下文,在使用完req对象之后会自动关闭
        # req.get(url) 发送请求获取图片数据
        async with req.get(url) as resp:  # => resp = req.get(url)
            # 这里的文件读写操作也是IO操作,也是会造成阻塞,所以也可以通过异步协程来完成
            # 具体可以学习aiofiles模块来实现
            with open(file_name, mode='wb') as f:
                # resp.content.read()是异步操作,所以前面要加await表示挂起
                # 挂起的意思就是resp.content.read()什么时候有东西了什么时候写入文件
                # 即什么时候有需要的内容了什么时候进行对应的操作
                # resp.content.read() 表示以字节的形式读取返回的数据的内容
                # 在这里就是读取图片的字节数据,然后存入文件,即保存图片数据
                f.write(await resp.content.read())
        # req.close() 使用with之后不用手动写上这句话
    print(file_name, '下载完成')

async def main():
    tasks = [asyncio.create_task(download(url)) for url in urls]
    await asyncio.wait(tasks)

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

使用异步爬虫爬取西游记小说内容

详见:异步爬取西游记

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QtApplets-SystemInfo

QtApplets-SystemInfo ​ 今天是2024年1月3日09:18:44&#xff0c;这也是2024年的第一篇博客&#xff0c;今天我们主要两件事&#xff0c;第一件&#xff0c;获取系统CPU使用率&#xff0c;第二件&#xff0c;获取系统内存使用情况。 ​ 这里因为写博客的这个本本的环境配置不…

C++学习day--25 俄罗斯方块游戏图像化开发

项目分析 项目演示、项目分析 启动页面 启动页面&#xff1a; 分析&#xff1a; 开发环境搭建 1&#xff09;安装vc2010, 或其他vs版本 2&#xff09;安装easyX图形库 代码实现: # include <stdio.h> # include <graphics.h> void welcome(void) { initgraph(55…

【MySQL】MySQL如何查询和筛选存储的JSON数据?

MySQL如何查询和筛选存储的JSON数据&#xff1f; 一、背景介绍二、支持的JSON数据类型三、基础数据3.1 创建表3.2 插入 JSON 数据3.3 查询 JSON 数据 四、操作函数4.1 JSON_OBJECT4.2 JSON_ARRAY4.3 JSON_EXTRACT 一、背景介绍 JSON(JavaScript Object Notation)是一种轻量级的…

如何通过内网穿透实现无公网IP远程访问内网的Linux宝塔面板

文章目录 一、使用官网一键安装命令安装宝塔二、简单配置宝塔&#xff0c;内网穿透三、使用固定公网地址访问宝塔 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。…

dubbo的基础知识

dubbo是什么 Dubbo是一个分布式服务框架&#xff0c;是一种高性能的远程通讯框架。它提供了基于Java的RPC&#xff08;远程过程调用&#xff09;通信机制&#xff0c;使得应用之间可以方便地进行远程调用&#xff0c;实现分布式服务的调用和管理。Dubbo提供了服务注册、发现、负…

轻松获取CHATGPT API:免费、无验证、带实例

免费获取和使用ChatGPT API的方法 快速开始&#xff1a;视频教程 章节一&#xff1a;GPT-API-Free开源项目介绍 GPT-API-Free 是一个开源项目&#xff0c;它提供了一个中转API KEY&#xff0c;使用户能够调用多个GPT模型&#xff0c;包括gpt-3.5-turbo、embedding和gpt-4。这…

cesium键盘控制模型

效果&#xff1a; 由于对添加模型和更新位置api进行二次了封装&#xff0c;下面提供思路 1.添加模型 const person reactive({modelTimer: null,position: {lon: 104.07274,lat: 30.57899,alt: 1200,heading: 0,pitch: 0,roll: 0,}, }); window.swpcesium.addEntity.addMo…

8.1分发饼干(LC455-E)

算法&#xff1a; 局部最优&#xff1a;大尺寸的饼干既可以满足胃口大的孩子也可以满足胃口小的孩子&#xff0c;那么就应该优先满足胃口大的。局部最优就是大饼干喂给胃口大的&#xff0c;充分利用饼干尺寸喂饱一个。 全局最优&#xff1a;喂饱尽可能多的小孩。 为什么局部…

C++开发小技巧

C开发一些小技巧 积累一些能用得到的C开发小技巧。 错误码/状态码 错误码/状态码在项目很常见&#xff0c;用于提示错误类型、状态&#xff0c;通常还会附带一些相关描述。通常错误码是统一管理的&#xff0c;例如使用宏或者枚举定义。 平时我的做法 使用宏或者枚举定义错…

Redis缓存保卫战:拒绝缓存击穿的进攻【redis问题 三】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Redis缓存保卫战&#xff1a;拒绝缓存击穿的进攻 前言缓存击穿的定义和原理为何会发生缓存击穿缓存击穿的危害防范缓存击穿结语: 前言 你是否曾经遇到过系统在高并发情况下出现严重性能问题&#xff…

【ASP.NET Core 基础知识】--环境设置

一、简介 1.1 .NET Core SDK 概述 .NET Core SDK&#xff08;Software Development Kit&#xff09;是Microsoft推出的一个开源跨平台框架&#xff0c;用于开发和部署.NET应用程序。它是.NET Core平台的核心组件之一&#xff0c;为开发者提供了在多个操作系统上构建高性能、可…

ssm基于VUE的图书馆管理系统的设计与实现论文

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差&#x…

imazing破解版百度云2.17.3(附激活许可证下载)

iMazing是一款强大的 iOS 设备管理软件&#xff0c;不管是 iPhone、iPad 或 iPod Touch 设备&#xff0c;只要将 iOS 设备连接到计算机&#xff0c;就可以处理不同类型的数据。 iPhone 和 iPad 备份 借助 iMazing 的独有 iOS 备份技术&#xff08;无线、隐私和自动&#xff09…

CarRacing DQN: 深度 Q 学习训练自驾车

OpenAI GYM CarRacing DQN: 深度 Q 学习训练自驾车 引言DQN 算法原理Q 值和 Bellman 方程DQN 结构 训练过程设计经验回放&#xff08;Experience Replay&#xff09;目标网络&#xff08;Target Network&#xff09;训练循环 训练结果和模型演变400 轮训练后500 轮训练后600 轮…

tomcat session cookie值设置逻辑

tomcat session cookie 值设置&#xff0c;tomcat jsessionid设置 ##调用request.getSession() Controller RequestMapping("/cookie") public class CookieController {RequestMapping("/tomcatRequest")ResponseBodypublic String tomcatRequest(HttpS…

buuctf-Misc 题目解答分解109-111

109.[CFI-CTF 2018]webLogon capture 流量包分析&#xff0c; wireshark 打开 就这几个数据包&#xff0c;追踪http 进行url 解码 URL网址解码器 - 在线网址解码 得到flag CFI{1ns3cur3_l0g0n} 110.[GKCTF 2021]excel 骚操作 下载 excel 文件 &#xff0c;打开 发现点击其他地…

编程语言的发展趋势和未来方向

1、编程语言的未来&#xff1f; 随着科技的飞速发展&#xff0c;编程语言在计算机领域中扮演着至关重要的角色。它们是软件开发的核心&#xff0c;为程序员提供了与机器沟通的桥梁。那么&#xff0c;在技术不断进步的未来&#xff0c;编程语言的走向又将如何呢&#xff1f; 方…

通信原理期末复习——计算大题(一)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

金和OA C6 MailTemplates.aspx sql注入漏洞

产品介绍 金和网络是专业信息化服务商,为城市监管部门提供了互联网监管解决方案,为企事业单位提供组织协同OA系统开发平台,电子政务一体化平台,智慧电商平台等服务。 漏洞概述 金和 OA C6 MailTemplates.aspx接口处存在SQL注入漏洞&#xff0c;攻击者除了可以利用 SQL 注入漏…

计算机毕业设计选题分享-SSM律师事务所业务管理系统01664(赠送源码数据库)JAVA、PHP,node.js,C++、python,大屏数据可视化等

SSM律师事务所业务管理系统 摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;律师事务所业务管理系统当然也不能排除在外。律师事务所业务管理系统是以实际运用为开发背景…