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: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',

Python3 BeautifulSoup爬虫 POJ自动提交

POJ 提交代码采用Base64加密方式 import http.cookiejarimport loggingimport urllib.parseimport urllib.requestimport base64from bs4 import BeautifulSoupfrom submitcode import SubmitCodeclass SubmitPoj():de

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点: 鼠标左键双击,设定红色的起点。左键双击设定起点,用红色标记。 设定终点: 鼠标右键双击,设定蓝色的终点。右键双击设定终点,用蓝色标记。 设置障碍点: 鼠标左键或者右键按着不放,拖动可以设置黑色的障碍点。按住左键或右键并拖动,设置一系列黑色障碍点

Python:豆瓣电影商业数据分析-爬取全数据【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】

**爬取豆瓣电影信息,分析近年电影行业的发展情况** 本文是完整的数据分析展现,代码有完整版,包含豆瓣电影爬取的具体方式【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】   最近MBA在学习《商业数据分析》,大实训作业给了数据要进行数据分析,所以先拿豆瓣电影练练手,网络上爬取豆瓣电影TOP250较多,但对于豆瓣电影全数据的爬取教程很少,所以我自己做一版。 目