Python爬虫之协程

news2024/11/26 0:45:04

Python爬虫之协程

为什么要用协程
协程声明
await
aiohttp
aiofiles
案例修改
案例完整代码

为什么要用协程

  1. 轻量级:协程是轻量级的执行单元,可以在同一个线程中并发执行。相比于多线程或多进程,创建和切换协程的开销更小。
  2. 高效利用资源:由于协程可以在同一个线程中并发执行,因此不会涉及多个线程或进程之间的上下文切换,从而减少了额外的开销。这使得协程能够高效地利用计算资源。
  3. 简化并发编程:协程采用显式的挂起和恢复机制,程序员可以明确控制协程的执行流程。相比于多线程或多进程的共享内存模型,协程通过显式的消息传递(如使用 awaitasyncio.Queue 等)来实现数据交换,简化了并发编程的复杂性。
  4. 异步非阻塞:协程通常与异步编程一起使用,可以在 I/O 密集型任务中实现非阻塞的操作。通过使用 await 关键字等待 I/O 操作完成时,可以在等待期间释放 CPU,执行其他协程任务,提高整体的并发性能。

协程声明

创建协程函数:async def func()

运行协程函数:asyncio.run(func())

注意:当调用协程函数func()后,内部代码是不会执行的,只是得到了一个协程对象,如果想要运行协程对象,则必须将其交给事件循环来处理

import asyncio

async def func():
    print("Hello, coroutine!")

# 调用协程
asyncio.run(func())

也可以这么写

import asyncio

async def func():
    print("Hello, coroutine!")

# 创建事件循环对象
loop = asyncio.get_event_loop()

# 将事件封装为任务
task = loop.create_task(func())

# 运行事件直到任务完成
loop.run_until_complete(task)

执行时间循环:.wait().gather

  • await asyncio.wait(tasks):接受一个任务集合作为参数,并等待所有任务完成。返回两个集合(Sets):已完成的任务集合和仍在进行中的任务集合。

  • await asyncio.gather(*tasks):接受一个任务集合作为参数,并等待所有任务完成。返回每个任务的实际返回值

  • await task:执行单个任务,返回每个任务的实际返回值

await

await关键字后面可以定义可等待对象,例如协程对象,Future,Task对象

此处的可等待对象其实就是I/O阻塞,当await包裹的协程任务遇到阻塞时会自动从当前任务切换到另一个任务中,以节省时间和内存

result = await 表示result就是await后面的指令运行完毕后得到的结果

import asyncio


async def fun1():
    print('1')
    await asyncio.sleep(2)
    return '结束'


async def main():
    # 创建任务
    task1 = asyncio.create_task(fun1())
    task2 = asyncio.create_task(fun1())
    
    # 创建事件循环
    res1 = await task1
    res2 = await task2
    print(res1, res2)


asyncio.run(main())

也可以这么写

import asyncio


async def fun1():
    print('1')
    await asyncio.sleep(2)
    return '结束'


async def main():
	# 创建任务
    task = [asyncio.create_task(fun1()) for i in range(10)]
    
    # 创建事件循环
    res = await asyncio.gather(task)

    print(res)


asyncio.run(main())

数量太少看不出效率,但是可以证明await会等待所有任务返回结果后再继续往下运行

image-20240122213003553

aiohttp

aiohttprequests相比最大的区别就是aiohttp支持异步操作,因此用协程编写爬虫时aiohttp是相当重要的一个模块

aiohttp.ClientSession()

  • 用于创建异步的HTTP客户端会话对象
  • 通过该对象发送异步请求并处理响应

session.get(url)session.post(url)

  • ClientSeesion对象上调用这些方法可以发送GET/POST请求
  • url作为参数传递,一般就是访问的主网址

response.statusresponse.text()

  • 这俩响应ClientResponse对象的属性和方法
  • response.status返回响应状态码(如200、404等)
  • response.status返回响应内容的文本字符串

response.json()

  • 当服务器返回JSON格式的响应是,可以用该方法将响应内容解析为Python对象(字典、列表)

async with session.get(url) as responseasync with session.post(url) as response

  • 使用async with语法结构,可以在异步上下文管理器中发送请求和处理响应
  • response是一个异步上下文管理器返回的响应对象,可以执行response.status之类的操作,并且使其能够被await包裹

利用aiohttp模块获取一个简单的浏览器响应

import asyncio
import aiohttp
from lxml import etree

url = 'https://www.baidu.com'


async def main():
    connector = aiohttp.TCPConnector(ssl=False)

    async with aiohttp.ClientSession(connector=connector) as session:
        async with session.get(url) as response:
            res = await response.text()
    et = etree.HTML(res)
    print(et)


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

aiofiles

aiofiles与python中常用的with open操作类似,并且支持异步操作,且与asyncio配合良好

具体操作也和with open类似

async def read_file():
    async with aiofiles.open('file.txt', mode='r') as file:
        contents = await file.read()
        print(contents)
        
        
if __name__ == '__main__':
    asyncio.run(read_file())

async def write_file():
    async with aiofiles.open('file.txt', mode='w') as file:
        await file.write('Hello, World!')
        
        
if __name__ == '__main__':
    asyncio.run(write_file())

案例修改

掏出上次我们写的线程池爬虫案例:

from concurrent.futures import ThreadPoolExecutor

import requests
from lxml import etree

url = 'https://loryx.wiki/%E6%B5%8F%E8%A7%88/%E7%89%8C%E5%BA%93'


def download(name, src):
    with open(name, 'wb') as f:
        f.write(requests.get(src).content)
        print(f'{name}已下载')


def main():
    res = requests.get(url=url)
    res.encoding = 'utf-8'
    et = etree.HTML(res.text)
    src = et.xpath("//td[@class='col15 leftalign']/a/@href")
    name = et.xpath("//td[@class='col0 leftalign']/text()")
    for i, index in enumerate(name):
        name[i] = index.strip()
    with ThreadPoolExecutor(64) as t:
        for i in range(len(src)):
            file_name = f"img/{name[i]}.png"
            t.submit(download, file_name, src[i])


if __name__ == '__main__':
    main()

现在开始改写

首先跟requests相关的可以全部删了换成aiohttp,比如

res = requests.get(url=url)
    res.encoding = 'utf-8'
    et = etree.HTML(res.text)

替换为

async with aiohttp.ClientSession(connector=connector) as session:
    async with session.get(url) as response:
        res = await response.text()
et = etree.HTML(res)

然后ThreadPoolExecutor相关的也可以全部用asyncio替换

for i, index in enumerate(name):
        name[i] = index.strip()
    with ThreadPoolExecutor(64) as t:
        for i in range(len(src)):
            file_name = f"img/{name[i]}.png"
            t.submit(download, file_name, src[i])

替换为

tasks = []
for i in range(len(src)):
    file_name = f"img/{name[i]}.png"
    if not os.path.exists('img'):
        os.makedirs('img')

    task = asyncio.create_task(spider(file_name, src[i]))
    tasks.append(task)
await asyncio.gather(*tasks)

文件读写的部分也可以用aiofiles重写

def download(name, src):
    with open(name, 'wb') as f:
        f.write(requests.get(src).content)
        print(f'{name}已下载')

替换成

async def spider(name, src):
    connector = aiohttp.TCPConnector(ssl=False)

    async with aiohttp.ClientSession(connector=connector) as session:
        async with session.get(src) as response:
            count = await response.read()

    async with aiofiles.open(name, 'wb') as f:
        await f.write(count)
        print(f'{name}已下载')

案例完整代码

import asyncio
import os.path

import aiofiles
import aiohttp
from lxml import etree

url = 'https://loryx.wiki/%E6%B5%8F%E8%A7%88/%E7%89%8C%E5%BA%93'


async def spider(name, src):
    # 关闭SSL证书验证
    connector = aiohttp.TCPConnector(ssl=False)

    # 创建图片链接对象
    async with aiohttp.ClientSession(connector=connector) as session:
        async with session.get(src) as response:
            # 读取图片信息 准备写入本地
            count = await response.read()
    # 写入本地 下载时遇到io阻塞自动跳转其他任务
    async with aiofiles.open(name, 'wb') as f:
        await f.write(count)
        print(f'{name}已下载')


async def main():
    # 关闭SSL证书验证
    connector = aiohttp.TCPConnector(ssl=False)

    # 创建异步HTTP客户端对象
    async with aiohttp.ClientSession(connector=connector) as session:
        # 发送get请求
        async with session.get(url) as response:
            # 返回响应内容的字符串
            res = await response.text()
    et = etree.HTML(res)
    src = et.xpath("//td[@class='col15 leftalign']/a/@href")  # 图片链接
    temp_name = et.xpath("//td[@class='col0 leftalign']/text()")  # 图片名称
    for i, index in enumerate(temp_name):
        temp_name[i] = index.strip()

    # 任务列表
    tasks = []
    for i in range(len(src)):
        # 下载到本地的名称
        file_name = f"img/{temp_name[i]}.png"
        if not os.path.exists('img'):
            os.makedirs('img')

        # 批量创建asyncio异步任务 执行spider函数
        task = asyncio.create_task(spider(file_name, src[i]))
        tasks.append(task)

    # 启动
    await asyncio.wait(tasks)


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

(src)):
        # 下载到本地的名称
        file_name = f"img/{temp_name[i]}.png"
        if not os.path.exists('img'):
            os.makedirs('img')

        # 批量创建asyncio异步任务 执行spider函数
        task = asyncio.create_task(spider(file_name, src[i]))
        tasks.append(task)

    # 启动
    await asyncio.wait(tasks)


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

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

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

相关文章

78.网游逆向分析与插件开发-背包的获取-背包类的C++还原与获取物品名称

内容参考于:易道云信息技术研究院VIP课 上一个内容:77.网游逆向分析与插件开发-背包的获取-物品类的C还原-CSDN博客 码云地址(ui显示角色数据 分支):https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&…

影响ETL数据传输性能的9大因素及主流ETL应对策略

前言 现在很多企业在选择ETL工具时都特别关注ETL的数据传输性能,而有很多开源ETL工具都说自已是性能如何如何快,而事实上数据传输性能是不是这些工具说的那样快呢? 数据传输性能受制于哪些因素呢?企业在自身数据库性能受制的情况…

Redis(七)复制

文章目录 是什么功能配置配主库不配从库权限细节 案例配置文件修改 一主二仆固定配置文件主从问题命令操作手动指定 薪火相传反客为主复制原理和工作流程存在问题 是什么 https://redis.io/docs/management/replication/ 就是主从复制,master以写为主,S…

农业气象站的工作原理!

TH-NQ8农业气象站的工作原理是基于传感器技术、数据采集技术、数据传输技术和数据处理技术等多个环节相互配合而实现的。 首先,农业气象站通过各种传感器对不同的气象指标进行实时监测和记录。传感器的种类有很多,包括温度传感器、湿度传感器、风速传感…

3dmax渲不出模型是什么原因---模大狮模型网

3DMax无法渲染模型可能有多种原因。以下是一些常见的问题和解决方法: 材质设置错误:检查模型的材质设置是否正确,包括纹理贴图的路径、UV映射是否正确等。确保材质的属性设置正确,如颜色、反射率、透明度等。 灯光设置问题&#…

vue3中form对象无法赋值问题

加上 async await还是不行 有时候对象的值死活赋不上值,这时候可以看下赋值的对象变量名是否和页面组件中的ref相同,如果存在相同,则参照以下解决方案: 问题定位:setup 中抛出的变量不能与页面组件中的 ref 重复 解决…

第40集《佛法修学概要》

请大家打开讲义第一百零六页。我们讲到大乘的果位。大乘佛法的修学跟小乘最大的差别,主要在于一句话,就是大乘佛法是一种“称性起修,全修在性”。大乘佛法的功德第一个“称性”,这个“称”就是随顺。我们一念明了的心,…

Java 异常及处理|Error、Throwable、Exception

目录 一、Java 异常概述 二、异常类 1、Throwable: 1.1 Throwable 类的常用方法包括: 1.2 创建和抛出 Throwable 2、Error: 2.1 Error 与异常处理的关系 3、Exception: 3.1 如何处理 Exception 方式1 、往外抛&#xff1…

Qt6入门教程 10:菜单栏、工具栏和状态栏

目录 一.菜单栏 1.Qt Designer 1.1添加菜单和菜单项 1.2添加二级菜单 1.3给菜单和菜单项添加图标 1.4给菜单项添加功能 2.纯手写 二.工具栏 1.Qt Designer 1.1添加工具栏按钮 1.2工具栏的几个重要属性 2.纯手写 三.状态栏 1.Qt Designer 2.纯手写 用Qt Creator新…

Unity - 简单音频视频

“Test_04” 音频 使用AudioTest脚本控制Audio Source组件,在脚本中声明"music"和"se"之后,在unity中需要将音频资源拖拽到对应位置。 AudioTest public class AudioTest : MonoBehaviour {// 声明音频// AudioClippublic AudioC…

Raspbian安装云台

Raspbian安装云台 1. 源由2. 选型3. 组装4. 调试4.1 python3-print问题4.2 python函数入参类型错误4.3 缺少mjpg-streamer可执行文件4.4 缺失编译头文件和库4.5 python库缺失4.6 图像无法显示,但libcamera-jpeg测试正常4.7 异常IOCTL报错4.8 Git问题 5. 效果5.1 WEB…

CSS文本外观属性内容(知识点1)

知识引入 使用HTML可以对文本外观进行简单的控制,但是效果并不理想,为此CSS提供了一系列的文本外观样式属性,具体如下。 color:文本颜色 color属性用于定义文本的颜色,其取值方式有以下三种。 (1)预定义…

springboot中一些注解

springboot中一些注解 1:项目启动时会去扫描启动的注解,一般是启动时就想要被加载的方法: 2:springBoot中MSApplication启动类的一些其他注解: EnableAsync:这是一个Spring框架的注解,它用于开启方法异步调用的功能。当…

RTDETR 引入 UniRepLKNet:用于音频、视频、点云、时间序列和图像识别的通用感知大卷积神经网络 | DRepConv

大卷积神经网络(ConvNets)近来受到了广泛研究关注,但存在两个未解决且需要进一步研究的关键问题。1)现有大卷积神经网络的架构主要遵循传统ConvNets或变压器的设计原则,而针对大卷积神经网络的架构设计仍未得到解决。2)随着变压器在多个领域的主导地位,有待研究ConvNets…

Linux 一键部署influxd2-telegraf

influxd2前言 influxd2 是 InfluxDB 2.x 版本的后台进程,是一个开源的时序数据库平台,用于存储、查询和可视化时间序列数据。它提供了一个强大的查询语言和 API,可以快速而轻松地处理大量的高性能时序数据。 telegraf 是一个开源的代理程序,它可以收集、处理和传输各种不…

Docker容器引擎(1)

目录 一.Docker 概述 为什么要用到容器? docker是什么? 容器与虚拟机的区别? docker的三个核心概念: 二.安装docker 安装依赖包: 安装 Docker-CE并设置为开机自动启动: 查看 docker 版本信息&#…

制作高端的电子杂志神器推荐

根据市场调查数据显示,越来越多的消费者开始青睐电子杂志这种阅读方式。相比传统纸质杂志,电子杂志具有更高的阅读体验、更便捷的分享和传播方式以及更环保的阅读方式。此外,越来越多的企业也开始重视电子杂志的宣传作用,将其作为…

植物神经功能紊乱到底是什么疾病?今天来告诉你原因和治疗方法!

首先,什么是植物神经功能紊乱?简单来说,植物神经是自主神经系统的一部分,它控制着自主生理反应,比如呼吸、心跳、血压、消化系统等。当这个系统出现紊乱时,就会导致植物神经功能紊乱。咨询:tdhy…

Unity中URP下的 额外灯 逐像素光 和 逐顶点光

文章目录 前言一、额外灯 的 逐像素灯 和 逐顶点灯1、存在额外灯的逐像素灯2、存在额外灯的逐顶点灯 二、测试这两个宏的作用1、额外灯的逐像素灯2、额外灯的逐顶点灯 前言 在之前的文章中,我们了解了 主光相关的反射计算。 Unity中URP下的SimpleLit的 Lambert漫反…

eNSP学习——VLAN基础配置及Access接口

目录 原理概述 实验内容: 实验目的: 实验步骤: 实验拓扑 配置过程 实验编址 基本配置 创建vlan 配置Access接口 原理概述 早期的局域网技术是基于总线型结构的。总线型拓扑结构是由一根单电缆连接所有主机,就导致所…