Python异步编程并发执行爬虫任务,用回调函数解析响应
一、问题:当发送API请求,读写数据库任务较重时,程序运行效率急剧下降。
异步技术是Python编程中对提升性能非常重要的一项技术。在实际应用,经常面临对外发送网络请求,调用外部接口,或者不断更新数据库或文件等操作。 这这些操作,通常90%以上时间是在等待,如通过REST, gRPC向服务器发送请求,通常可能等待几十毫秒至几秒,甚至更长。如果业务较重,按顺序执行编程,会导致大量时间用在等待上,程序运行效率急剧下降。
常见的场景,就是爬虫软件通常会发起很多请求,如果采用同步编程方式工,往往运行时间很长。
二、异步编程的优势
通常的编程,如果有4个任务,采用同步编程模式,4个任务是按顺序执行的,分别用时:10s,7s,5s,6s,共耗时28s; 而异步方式,就是让4个任务同时执行,总耗时降为10s,改善效果是很明显的。

那时异步编程是如何做到的?
异步编程,将每个任务改成协程执行,在遇到需要等待的语句时,即暂时将执行权交还给主程序的控制循环event loop,其它协程可以继续使用CPU等资源。而当该协程收到响应后,会用事件通知event loop,申请继续执行。 这样就避免了由于等待期间还占用CPU资源的情形。 因此程序执行效率大为提高。
但如果任务是计算密集型的,那么异步技术对性能提升帮助不大,需要采用其它方式,如多进程编程。或者Cython 等。
三、用同步编程方式,抓取多个网站数据
先看一下,采用同步编程顺序执行,抓取多个网站数据的耗时。 这些网站中,
其中http://www.google.com 是无响应的,会超时。因此在 requests.get()方法,设置 timeout=3, 即超过3秒,会抛出TimeOutException 异常。
代码如下:
import requests
import time# 测试时将测试网址替换
urls = ["http://www.bxxxx.com","http://www.aaaa.com","http://www.bbbb.com","http://www.cccc.com","http://www.sdddd.com","http://www.jdddd.com","http://www.zeeee.com","http://www.tffff.com","http://www.cgggg.com","http://www.zhhhhh.com.cn","http://www.google.com","https://www.yiiiii.com/",
]def check_one_ip(url):headers = {"user-ageng": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \(KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69"}TIMEOUT = 3result = ()try:response = requests.get(url, headers=headers, timeout=TIMEOUT)print(f"response from {url} is : {response.status_code}")if 200 <= response.status_code < 300:print(f"length of response body is {len(response.text)}")result = (url, response.status_code)except Exception as e:print(f"{url} met timeout error")return (url, 999)return resultdef main():results = []for url in urls:result = check_one_ip(url)results.append(result)if __name__ == "__main__":t1 = time.time()main()t2 = time.time()print(f"total time: {t2-t1:.3f}s")
运行代码,向12个网站发送request, 打印response的状态码,总耗时为:6.035s,
response from url is : 200
length of response body is 2381
response from url is : 200
length of response body is 24000
response from url is : 200
length of response body is 106117
response from url is : 403
response from url is : 404
response from url is : 200
length of response body is 177104
response from url is : 200
length of response body is 37989
response from url is : 200
length of response body is 89513
response from url is : 200
length of response body is 32642
response from url is : 403
url met timeout error
response from url is : 200
length of response body is 834
total time: 6.035s
四、用异步方式,同时抓取多个网站数据
现在,采用Asyncio异步编程,以并发的运行方式,向多个网站同时发送request, 总耗时,应该是用时最长那个协程的用时。这里我们使用了timeout, 就是3秒左右。
AsyncIO异步编程步骤:
- 定义异步任务函数
使用 asyc / await 关键字。在耗时操作前加await - 创建asyncio.create_task() 方法创建协程任务
- 在main()方法中用gather() 汇集协程任务,以便并发执行。
gather()方法返回结果是一个由所有返回值聚合而成的迭代器 - 在主线程的event loop中运行main()
asyncio模块提供了1个.run()来启动 event loop 异步控制循环,并执行main()方法, - 可选,给协程添加回调函数来解析网站响应结果
对于每个Task, 可用 add_done_callback(task_callback) 方法添加回调函数,此例中,对显示response的状态码。
其它说明
- 由于requests库的 response对象不支持 await语句,因此这里使用htppx 库来代替requests, 除了异步接口外,其它使用方式完全一致。
完整代码
import asyncio
import httpx
from concurrent.futures import ThreadPoolExecutor, Future
import time
import contextvars# 测试时将测试网址替换
urls = ["http://www.bxxxx.com","http://www.aaaa.com","http://www.bbbb.com","http://www.cccc.com","http://www.sdddd.com","http://www.jdddd.com","http://www.zeeee.com","http://www.tffff.com","http://www.cgggg.com","http://www.zhhhhh.com.cn","http://www.google.com","https://www.yiiiii.com/",
]async def check_one_ip(url):headers = {"user-ageng": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \(KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69"}TIMEOUT = 3result = ()try:async with httpx.AsyncClient() as client:response = await client.get(url, headers=headers,timeout=TIMEOUT)print(f"response from {url} is : {response.status_code}")if 200 <= response.status_code < 300:print(f"length of response body is {len(response.text)}")result = (url, response.status_code)except Exception as e:print(f"{url} met timeout error")return (url, 999)return result def task_callback(context):# print response.status_code url, code = context.result()print(f"It is callback, got status_code: {code} of {url}")async def main():tasks=[]for url in urls:task = asyncio.create_task(check_one_ip(url))task.add_done_callback(task_callback)tasks.append(task)await asyncio.gather(*tasks) if __name__=="__main__":t1 = time.time()asyncio.run(main())t2 = time.time()print(f"total time: {t2-t1:.3f}s")
运行结果如下,可以看到,总耗时: 3.161s,相比同步编程方式,耗时减少了1半。 随着发送请求量的增加,可以看到更加明显的效果。
response from url is : 302
It is callback, got status_code: 302 of url
response from url is : 302
It is callback, got status_code: 302 of url
response from url is : 200
length of response body is 23508
It is callback, got status_code: 200 of url
response from url is : 302
response from url is : 301
It is callback, got status_code: 302 of url
It is callback, got status_code: 301 of url
response from url is : 301
response from url is : 301
response from url is : 301
response from url is : 200
length of response body is 396837
It is callback, got status_code: 301 of url
It is callback, got status_code: 301 of url
It is callback, got status_code: 301 of url
It is callback, got status_code: 200 of url
response from url is : 404
It is callback, got status_code: 404 of url
response from url is : 200
length of response body is 1151330
It is callback, got status_code: 200 of url
url met timeout error
It is callback, got status_code: 999 of url
total time: 3.161s
五、异步编程注意事项
1)协程不应该执行耗时长的任务
异步event loop执行期间,虽然各个协程是在工作,但主线程是被阻塞的。本例中,异步耗时的总时长与访问google.com超时时长相同,那么意味着,如果协程中如果有1个是耗时很长的任务,那么主线程还将被阻塞,异步解决不了这个问题,这时耗时协程应该拿出来,用子线程、或者子进程来执行。
2) 协程应该汇集后并发执行
遇到一些开发者咨询,为什么采用了异步编程,但性能没有明显提升呢? 创建多个协程任务后,必须按第3步,用gather()方法来汇集创建的协程任务,然后用asyncio.run()方法并发运行。 另外官方文档要求 event loop要在主线程main() 方法中运行。
3)慎用底层编程接口
另外由于官方文档并未清晰说明 event loop、future对象等低层编程接口,除非你很了解异步低层的实现机制,否则不建议使用低层接口,
使用ayncio.run() 来启动evnetloop, 使用 task 对象,而非future 对象。
相关文章:
Python异步编程并发执行爬虫任务,用回调函数解析响应
一、问题:当发送API请求,读写数据库任务较重时,程序运行效率急剧下降。 异步技术是Python编程中对提升性能非常重要的一项技术。在实际应用,经常面临对外发送网络请求,调用外部接口,或者不断更新数据库或文…...
React组件化开发
1.组件的定义方式 函数组件Functional Component类组件Class Component 2.类组件 export class Profile extends Component {render() {console.log(this.context);return (<div>Profile</div>)} } 组件的名称是大写字符开头(无论类组件还是函数组件…...
LuatOS-SOC接口文档(air780E)--crypto - 加解密和hash函数
crypto.md5(str) 计算md5值 参数 传入值类型 解释 string 需要计算的字符串 返回值 返回值类型 解释 string 计算得出的md5值的hex字符串 例子 -- 计算字符串"abc"的md5 log.info("md5", crypto.md5("abc"))crypto.hmac_md5(str, k…...
自动化测试的定位及一些思考
大家对自动化的理解,首先是想到Web UI自动化,这就为什么我一说自动化,公司一般就会有很多人反对,因为自动化的成本实在太高了,其实自动化是分为三个层面的(UI层自动化、接口自动化、单元测试)&a…...
展会动态 | 迪捷软件邀您参加2023世界智能网联汽车大会
*9月18日之前注册的观众免收门票费* 由北京市人民政府、工业和信息化部、公安部、交通运输部和中国科学技术协会联合主办的2023世界智能网联汽车大会将于9月21日-24日在北京中国国际展览中心(顺义馆)举行。 论坛背景 本届展会以“聚智成势 协同向新——…...
jenkins自动化部署springboot、gitee项目
服务器需要安装jdk11、maven、gitee 1. jenkins安装 # yum源 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.repo # 公钥 sudo rpm --import https://pkg.jenkins.io/redhat/jenkins.io-2023.key # 安装 yum install jenkins如果yum源报…...
Python环境配置及基础用法Pycharm库安装与背景设置及避免Venv文件夹
目录 一、Python环境部署及简单使用 1、Python下载安装 2、环境变量配置 3、检查是否安装成功 4、Python的两种模式(编辑模式&交互模式) 二、Pycharm库安装与背景设置 1、Python库安装 2、Pycharm自定义背景 三、如何避免Venv文件夹 一、P…...
PHP常见的SQL防注入方法
利用Mysqli和PDO 产生原因主要就是一些数据没有经过严格的验证,然后直接拼接 SQL 去查询。导致产生漏洞,比如: $id $_GET[id]; $sql "SELECT name FROM users WHERE id $id";因为没有对 $_GET[‘id’] 做数据类型验证…...
分布式和中间件等
raft协议 paxos算法ddos 如何避免?怎么预防?怎么发现?利用了TCP什么特点?怎么改进TCP可以预防?服务端处理不了的请求怎么办?连接数最大值需要设置吗?怎么设置? Thrift RPC过程是什么样子的?异构系统怎么完成通信?跟http相比什么优缺点?了解grpc吗?kafka topic part…...
通过http发送post请求的三种Content-Type分析
通过okhttp向服务端发起post网络请求,可以通过Content-Type设置发送请求数据的格式。 常用到的三种: 1)application/x-www-form-urlencoded; charsetutf-8 2)application/json; charsetutf-8 3)multipart/form-dat…...
Vue中的自定义指令详解
文章目录 自定义指令自定义指令-指令的值(给自定义指令传参数) 自定义指令 自定义指令:自己定义的指令,可以封装一些dom 操作,扩展额外功能(自动聚焦,自动加载,懒加载等复杂的指令封…...
[管理与领导-100]:管理者到底是什么?调度器?路由器?交换机?监控器?
目录 前言: 二层交换机 三层路由器 监视器(Monitor) 调度器 前言: 人在群体中,有点像设备在网络中,管理者到底承担什么的功能? 二层交换机 交换机是计算机网络中,用于连接多台…...
保研CS/软件工程/通信问题汇总
机器学习 1.TP、TN、FP、FN、F1 2.机器学习和深度学习的区别和联系 模型复杂性:深度学习是机器学习的一个子领域,其主要区别在于使用深层的神经网络模型。深度学习模型通常包含多个隐层,可以学习更加复杂的特征表示,因此在某些任…...
word、excel、ppt转为PDF
相关引用对象在代码里了 相关依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.0.1</version></dependency> <dependency><groupId>org.apache.poi</group…...
2023华为杯D题——基于Kaya模型的碳排放达峰实证研究
一、前言 化石能源是推动现代经济增长的重要生产要素,经济生产活动与碳排放活动密切相关。充分认识经济增长与碳排放之间的关系对转变生产方式,确定碳达峰、碳中和路径极为必要。本研究在对经济增长与碳排放关系现有研究梳理的基础上,系统地分…...
有哪些好用的上网行为管理软件?(上网行为管理软件功能好的软件推荐)
随着互联网的快速发展,企业的信息化管理和员工的上网行为已经成为企业信息化建设的重要组成部分。上网行为管理软件作为一种新型的管理工具,可以帮助企业实现对员工上网行为的管控和优化,进而提高企业的工作效率和网络安全。本文将对多款市场…...
npm install报错 code:128
报的错误: npm ERR! code 128 npm ERR! An unknown git error occurred npm ERR! command git --no-replace-objects ls-remote ssh://gitgithub.com/nhn/raphael.git npm ERR! gitgithub.com: Permission denied (publickey). npm ERR! fatal: Could not read from remote re…...
爬虫 — Scrapy 框架(一)
目录 一、介绍1、同步与异步2、阻塞与非阻塞 二、工作流程三、项目结构1、安装2、项目文件夹2.1、方式一2.2、方式二 3、创建项目4、项目文件组成4.1、piders/__ init __.py4.2、spiders/demo.py4.3、__ init __.py4.4、items.py4.5、middlewares.py4.6、pipelines.py4.7、sett…...
Python编程语言学习笔记
目录 1 书写格式1.1 程序框架格式1.1 注释1.2 保留字 2 数据2.1 整数类型2.2 浮点类型2.3 复数类型2.4 数值运算符2.5 数值运函数2.6 数值类型转换函数2.7 math 库2.8 字符串2.8.1 字符串的表示2.8.2 字符串的区间访问2.8.3 字符串操作符2.8.4 字符串操作函数 2.9 字符串类型的…...
【运维面试100问】(三)说说你在故障排除方面的经历
本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》:python零基础入门学习 《python运维脚本》: python运维脚本实践 《shell》:shell学习 《terraform》持续更新中:terraform_Aws学习零基础入门到最佳实战 《k8…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
