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…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...

路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...
计算机系统结构复习-名词解释2
1.定向:在某条指令产生计算结果之前,其他指令并不真正立即需要该计算结果,如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方,那么就可以避免停顿。 2.多级存储层次:由若干个采用不同实现技术的存储…...