Python爬虫之协程

2024-01-24 02:12
文章标签 python 爬虫 之协程

本文主要是介绍Python爬虫之协程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Python爬虫之协程

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

为什么要用协程

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

协程声明

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

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

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

import asyncioasync def func():print("Hello, coroutine!")# 调用协程
asyncio.run(func())

也可以这么写

import asyncioasync 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 asyncioasync def fun1():print('1')await asyncio.sleep(2)return '结束'async def main():# 创建任务task1 = asyncio.create_task(fun1())task2 = asyncio.create_task(fun1())# 创建事件循环res1 = await task1res2 = await task2print(res1, res2)asyncio.run(main())

也可以这么写

import asyncioasync 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 etreeurl = '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 ThreadPoolExecutorimport requests
from lxml import etreeurl = '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.pathimport aiofiles
import aiohttp
from lxml import etreeurl = '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())

这篇关于Python爬虫之协程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/638238

相关文章

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文

Python设置Cookie永不超时的详细指南

《Python设置Cookie永不超时的详细指南》Cookie是一种存储在用户浏览器中的小型数据片段,用于记录用户的登录状态、偏好设置等信息,下面小编就来和大家详细讲讲Python如何设置Cookie... 目录一、Cookie的作用与重要性二、Cookie过期的原因三、实现Cookie永不超时的方法(一)

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

Python函数作用域示例详解

《Python函数作用域示例详解》本文介绍了Python中的LEGB作用域规则,详细解析了变量查找的四个层级,通过具体代码示例,展示了各层级的变量访问规则和特性,对python函数作用域相关知识感兴趣... 目录一、LEGB 规则二、作用域实例2.1 局部作用域(Local)2.2 闭包作用域(Enclos

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

Python中win32包的安装及常见用途介绍

《Python中win32包的安装及常见用途介绍》在Windows环境下,PythonWin32模块通常随Python安装包一起安装,:本文主要介绍Python中win32包的安装及常见用途的相关... 目录前言主要组件安装方法常见用途1. 操作Windows注册表2. 操作Windows服务3. 窗口操作

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

python常用的正则表达式及作用

《python常用的正则表达式及作用》正则表达式是处理字符串的强大工具,Python通过re模块提供正则表达式支持,本文给大家介绍python常用的正则表达式及作用详解,感兴趣的朋友跟随小编一起看看吧... 目录python常用正则表达式及作用基本匹配模式常用正则表达式示例常用量词边界匹配分组和捕获常用re