异步爬虫之aiohttp的使用

在上一篇博客我们介绍了异步爬虫的基本原理和 asyncio 的基本用法,并且在最后简单提及了使用aiohttp 实现网页爬取的过程。本篇博客我们介绍一下 aiohttp 的常见用法。

基本介绍

前面介绍的 asyncio模块,其内部实现了对 TCP、UDP、SSL协议的异步操作,但是对于 HTTP 请求来说,就需要用 aiohttp 实现了。

aiohttp 是一个基于 asyncio 的异步 HTTP 网络模块,它既提供了服务端,又提供了客户端。其中我们用服务端可以搭建一个支持异步处理的服务器,这个服务器就是用来处理请求并返回响应的,类似于 Django、Flask、Tormado 等一些 Web服务器。而客户端可以用来发起请求,类似于使用 requests 发起一个 HTTP 请求然后获得响应,但 requests 发起的是同步的网络请求,aiohttp 则是异步的。

本篇博客我们主要了解一下 aiohttp 客户端部分的用法。

基本实例

我们来看一个基本的 aiohttp 请求案例,代码如下:

import aiohttp
import asyncioasync def fetch(session, url):async with session.get(url) as response:return await response.text(), response.statusasync def main():async with aiohttp.ClientSession() as session:html, status = await fetch(session, 'https://cuiqingcai.com')print(f'html:{html[:100]}...')print(f'status:{status}')if __name__ == '__main__':loop = asyncio.get_event_loop()loop.run_until_complete(main())

这里使用 aiohttp 爬取了本书作者的个人博客,获得了源码和响应状态码,并打印出来,运行结果如下:

html:<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content...
status:200

由于网页源码过长,这里只截取了输出的一部分。可以看到,我们成功获取了网页的源代码及响应状态码 200,也就是完成了一次基本的 HTTP请求,即我们成功使用 aiohttp 通过异步的方式完成了网页爬取。当然,这个操作用之前讲的 requests 也可以做到。

能够发现,aiohttp 的请求方法的定义和之前有明显的区别,主要包括如下几点。

  • 首先在导人库的时候,除了必须引人 aiohttp这个库,还必须引人asyncio 库。因为要实现异步爬取,需要启动协程,而协程则需要借助于asyncio 里面的事件循环才能执行。除了事件循环asyncio 里面也提供了很多基础的异步操作。
  • 异步爬取方法的定义和之前有所不同,每个异步方法的前面都要统一加 async 来修饰。
  • with as 语句前面同样需要加 async 来修饰。在 Pvthon 中,with as 语句用于声明一个上下文管理器,能够帮我们自动分配和释放资源。而在异步方法中,withas前面加上async代表声明一个支持异步的上下文管理器。
  • 对于一些返回协程对象的操作,前面需要加 await 来修饰。例如 response 调用 text 方法,查询 API可以发现,其返回的是协程对象,那么前面就要加 await;而对于状态码来说,其返回值就是一个数值,因此前面不需要加 await。所以,这里可以按照实际情况做处理,参考官方文档说明,看看其对应的返回值是怎样的类型,然后决定加不加await 就可以了。
  • 最后,定义完爬取方法之后,实际上是 main 方法调用了 fetch 方法。要运行的话,必须启用事件循环,而事件循环需要使用 asyncio库,然后调用 run_until_complete 方法来运行。

注意:在 Python 3.7 及以后的版本中,我们可以使用 asyncio.run(main())代替最后的启动操作,不需要显示声明事件循环,run 方法内部会自动启动一个事件循环。但这里为了兼容更多的Python 版本,依然显式声明了事件循环。

URL参数设置

对于 URL 参数的设置,我们可以借助 params 参数,传入一个字典即可,实例如下:

import aiohttp
import asyncioasync def main():params = {'name': 'germey', 'age': 25}async with aiohttp.ClientSession() as session:async with session.get('https://www.httpbin.org/get', params=params) as response:print(await response.text())if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())

运行结果如下:

{"args": {"age": "25", "name": "germey"}, "headers": {"Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "www.httpbin.org", "User-Agent": "Python/3.9 aiohttp/3.11.11", "X-Amzn-Trace-Id": "Root=1-677217f5-3bc6954d23f245465ccbb806"}, "origin": "111.18.92.1", "url": "https://www.httpbin.org/get?name=germey&age=25"
}

这里可以看到,实际请求的 URL 为 https://www.httpbin.org/get?name=germey&age-25,其中的参数对应于 params 的内容。

其他请求类型

aiohttp 还支持其他请求类型,如 POST、PUT、DELETE 等,这些和 requests 的使用方式有点类
似,实例如下:

session.post('http://www.httpbin.org/post',data=b'data')
session.put('http://www.httpbin.org/put', data=b'data')
session.delete('http://www.httpbin.org/delete')
session.head("http://www.httpbin.org/get')
session.options('http://www.httpbin.org/get')
session.patch('http://www.httpbin.org/patch', data=b'data')

要使用这些方法,只需要把对应的方法和参数替换一下。

POST请求

对于 POST表单提交,其对应的请求头中的Content-Type为application/x-www-form-urlencoded.我们可以用如下方式来实现:

import aiohttp
import asyncioasync def main():data = {'name': 'germey', 'age': 25}async with aiohttp.ClientSession() as session:async with session.post('https://www.httpbin.org/post', data=data) as response:print(await response.text())if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())

运行结果如下:

{"args": {}, "data": "", "files": {}, "form": {"age": "25", "name": "germey"}, "headers": {"Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "18", "Content-Type": "application/x-www-form-urlencoded", "Host": "www.httpbin.org", "User-Agent": "Python/3.9 aiohttp/3.11.11", "X-Amzn-Trace-Id": "Root=1-67721981-6891ead2152d4e70567cd9ab"}, "json": null, "origin": "111.18.92.1", "url": "https://www.httpbin.org/post"
}

对于 POST JSON 数据提交,其对应的请求头中的 Content-Type 为 application/json,我们只需要将 post 方法里的 data 参数改成 json 即可,实例代码如下:

import aiohttp
import asyncioasync def main():data = {'name': 'germey', 'age': 25}async with aiohttp.ClientSession() as session:async with session.post('https://www.httpbin.org/post', json=data) as response:print(await response.text())if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())

运行结果如下:

{"args": {}, "data": "{\"name\": \"germey\", \"age\": 25}", "files": {}, "form": {}, "headers": {"Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "29", "Content-Type": "application/json", "Host": "www.httpbin.org", "User-Agent": "Python/3.9 aiohttp/3.11.11", "X-Amzn-Trace-Id": "Root=1-67721ce2-0439155d5dce282c3803cca6"}, "json": {"age": 25, "name": "germey"}, "origin": "111.18.92.1", "url": "https://www.httpbin.org/post"
}

可以发现,其实现也和requests 非常像,不同的参数支持不同类型的请求内容。

响应

对于响应来说,我们可以用如下方法分别获取其中的状态码、响应头、响应体、响应体二进制内容、响应体 JSON 结果,实例代码如下:

import aiohttp
import asyncioasync def main():data = {'name': 'germey', 'age': 25}async with aiohttp.ClientSession() as session:async with session.post('https://www.httpbin.org/post', data=data) as response:print('status:', response.status)print('headers:', response.headers)print('body:', await response.text())print('bytes:', await response.read())print('json:', await response.json())if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())

运行结果如下:

status: 200
headers: <CIMultiDictProxy('Date': 'Mon, 30 Dec 2024 04:40:46 GMT', 'Content-Type': 'application/json', 'Content-Length': '510', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')>
body: {"args": {}, "data": "", "files": {}, "form": {"age": "25", "name": "germey"}, "headers": {"Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "18", "Content-Type": "application/x-www-form-urlencoded", "Host": "www.httpbin.org", "User-Agent": "Python/3.9 aiohttp/3.11.11", "X-Amzn-Trace-Id": "Root=1-6772244e-191cbb78229cbd5412777470"}, "json": null, "origin": "111.18.92.1", "url": "https://www.httpbin.org/post"
}bytes: b'{\n  "args": {}, \n  "data": "", \n  "files": {}, \n  "form": {\n    "age": "25", \n    "name": "germey"\n  }, \n  "headers": {\n    "Accept": "*/*", \n    "Accept-Encoding": "gzip, deflate", \n    "Content-Length": "18", \n    "Content-Type": "application/x-www-form-urlencoded", \n    "Host": "www.httpbin.org", \n    "User-Agent": "Python/3.9 aiohttp/3.11.11", \n    "X-Amzn-Trace-Id": "Root=1-6772244e-191cbb78229cbd5412777470"\n  }, \n  "json": null, \n  "origin": "111.18.92.1", \n  "url": "https://www.httpbin.org/post"\n}\n'
json: {'args': {}, 'data': '', 'files': {}, 'form': {'age': '25', 'name': 'germey'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Content-Length': '18', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'www.httpbin.org', 'User-Agent': 'Python/3.9 aiohttp/3.11.11', 'X-Amzn-Trace-Id': 'Root=1-6772244e-191cbb78229cbd5412777470'}, 'json': None, 'origin': '111.18.92.1', 'url': 'https://www.httpbin.org/post'}

可以看到,这里有些字段前面需要加 await,有些则不需要。其原则是,如果返回的是一个协程对象(如 async修饰的方法),那么前面就要加 await,具体可以看 aiohttp的API,其链接为:https://docs.aiohttp.org/en/stable/client_reference.html.

超时设置

我们可以借助 clientTimeout 对象设置超时,例如要设置1秒的超时时间,可以这么实现:

import aiohttp
import asyncioasync def main():timeout = aiohttp.ClientTimeout(total=1)async with aiohttp.ClientSession(timeout=timeout) as session:async with session.get('https://www.httpbin.org/get') as response:print('status:', response.status)if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())

如果在1秒之内成功获取响应,那么运行结果如下:

200

如果超时,则会抛出 TimeoutError 异常,其类型为 asyncio.TimeoutError,我们进行异常捕获即可。另外,声明clientTimeout对象时还有其他参数,如connect、socket、connect等,详细可以参考官方文档:https://docs.aiohttp.org/en/stable/client_quickstart.html#timeouts。

并发限制

由于 aiohttp 可以支持非常高的并发量,如几万、十万、百万都是能做到的,但面对如此高的并发量,目标网站很可能无法在短时间内响应,而且有瞬间将目标网站爬挂掉的危险,这提示我们需要控制一下爬取的并发量。

一般情况下,可以借助 asyncio 的 Semaphore 来控制并发量,实例代码如下:

import aiohttp
import asyncioCONCURRENCY = 5
URL = 'https://www.baidu.com'
semaphore = asyncio.Semaphore(CONCURRENCY)
session = Noneasync def scrape_api():async with semaphore:print('scraping', URL)async with session.get(URL) as response:await asyncio.sleep(1)return await response.text()async def main():global sessionsession = aiohttp.ClientSession()scrape_index_tasks = [asyncio.ensure_future(scrape_api()) for _ in range(10000)]await asyncio.gather(*scrape_index_tasks)if __name__ == '__main__':asyncio.get_event_loop().run_until_complete(main())

参考文献

https://docs.aiohttp.org/

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文件本地和OSS上传

这里写目录标题 前端传出文件后端本地存储阿里云OSS存储上传Demo实现上传ConfigurationProperties 前端传出文件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>上传文件</title> </head&g…

《Vue3实战教程》37:Vue3生产部署

如果您有疑问&#xff0c;请观看视频教程《Vue3实战教程》 生产部署​ 开发环境 vs. 生产环境​ 在开发过程中&#xff0c;Vue 提供了许多功能来提升开发体验&#xff1a; 对常见错误和隐患的警告对组件 props / 自定义事件的校验响应性调试钩子开发工具集成 然而&#xff…

python制作打字小游戏

import pygame # 导入游戏模块 安装pygame import sys # 导入系统指令模块 import random # 导入随机数模块 pygame.init() #初始化游戏环境 wndpygame.display.set_mode((800,565)) #指定窗口大小 pygame.mixer.music.load(素材/SurvivalGame.mp3) #素…

抖音短视频矩阵系统源码开发全流程解析

在项目开发过程中&#xff0c;调整配置文件至关重要&#xff0c;这些文件包括数据库连接、API密钥及全局参数等。通过正确配置这些信息&#xff0c;可确保应用程序的稳定性和安全性。灵活调整配置以适应具体需求有助于短视频矩阵系统项目的顺利推进。 在开发环境中&#xff0c…

前端路由layout布局处理以及菜单交互(三)

上篇介绍了前端项目部署以及基本依赖的应用&#xff0c;这次主要对于路由以及布局进行模块化处理 一、 创建layout模块 1、新建src/layout/index.vue <template><el-container class"common-layout"><!-- <el-aside class"aside">&l…

戴尔/Dell 电脑按什么快捷键可以进入 Bios 设置界面?

BIOS&#xff08;基本输入输出系统&#xff09;是计算机硬件与操作系统之间的桥梁&#xff0c;它负责初始化和测试系统硬件组件&#xff0c;并加载启动操作系统。在某些情况下&#xff0c;如调整启动顺序、更改系统时间或日期、修改硬件配置等&#xff0c;您可能需要进入BIOS进…

《从入门到精通:蓝桥杯编程大赛知识点全攻略》(一)-递归实现指数型枚举、递归实现排列型枚举

本篇博客将聚焦于通过递归来实现两种经典的枚举方法&#xff1a;指数型枚举和排列型枚举。这两种枚举方式在计算机科学和算法竞赛中都有广泛应用&#xff0c;无论是在解题中&#xff0c;还是在实际工作中都极具价值。 目录 前言 斐波那契数列递归 递归实现指数型枚举 算法思…

idea 的 springboot项目spring-boot-devtools 自动编译 配置热部署

1&#xff0c;设置一 2&#xff0c;设置二 设置二&#xff08;旧版本&#xff09; CtrlShiftAlt/ 点击弹出框中Registry... 引入&#xff08;如果报错&#xff0c;换不同的版本&#xff09; <dependency><groupId>org.springframework.boot</groupId><a…

低代码开发:开启企业数智化转型“快捷键”

一、低代码开发浪潮来袭&#xff0c;企业转型正当时 在当今数字化飞速发展的时代&#xff0c;低代码开发已如汹涌浪潮&#xff0c;席卷全球。从国际市场来看&#xff0c;诸多企业巨头纷纷布局低代码领域&#xff0c;像微软的 PowerApps、OutSystems 等平台&#xff0c;凭借强大…

C#二维数组详解

目录 1&#xff0c;什么是二维数组&#xff1f; 2&#xff0c;创建二维数组的几种方式 &#xff08;1&#xff09;使用[,]声明数组&#xff08;常见方式&#xff09; &#xff08;2&#xff09;声明数组时指定元素 &#xff08;3&#xff09;使用new创建数组 &#xff08;…

STM32--超声波模块(HC—SR04)(标准库+HAL库)

一、HC-SR04工作原理 1&#xff09;采用IO触发测距&#xff0c;给至少10us的高电平信号。 2&#xff09;模块自动发送8个40KHz的方波&#xff0c;自动检测是否有信号返回。 3&#xff09;有信号返回&#xff0c;通过IO输出一高电平&#xff0c;高电平持续时间就是超声波从发…

DDD(一)—— Authentication with JWT

文章目录 项目地址一、项目结构梳理1.1 Domain层1.1.1 Entities文件夹1.2 Contracts层1.2.1 Authentication文件夹1.3 Appliaction层1.3.1Common文件夹1. Interfaces文件夹Authentication 权限接口Persistence 数据库接口Services 常用服务接口1.3.2 Services文件夹1. Authenti…

GPU 进阶笔记(一):高性能 GPU 服务器硬件拓扑与集群组网

记录一些平时接触到的 GPU 知识。由于是笔记而非教程&#xff0c;因此内容不求连贯&#xff0c;有基础的同学可作查漏补缺之用 1 术语与基础 1.1 PCIe 交换芯片1.2 NVLink 定义演进&#xff1a;1/2/3/4 代监控1.3 NVSwitch1.4 NVLink Switch1.5 HBM (High Bandwidth Memory) 由…

自由学习记录(31)

Java连接MySQL 找到那个关键jar包然后导入选中&#xff0c;就配置好MySQL的JDBC&#xff08;Java Database Connectivity&#xff09;了 菜单--文件--项目结构 项目设置--模块--选择要附着的项目--选择依赖--选中模块源--选中加号添加jar包 解压之后在里面可以看到这个最关键…

第十四届蓝桥杯Scratch省赛中级组—智能计价器

智能计价器 背景信息&#xff1a; A城市的出租车计价&#xff1a;3公里以内13元&#xff0c;基本单价每公里2.3元(超过3公里的部分&#xff0c;不满1公里按照1公里收费&#xff09;&#xff0c;燃油附加费每运次1元。 例如&#xff1a; 3.2公里的打车费用&#xff1a;132.3…

游戏引擎学习第69天

回顾碰撞响应时我们停留的位置 从昨天的讨论开始&#xff0c;我们正准备处理碰撞响应的复杂性。具体来说&#xff0c;我们讨论的是&#xff0c;当两个实体在屏幕上发生碰撞时&#xff0c;如何回应这种情况。碰撞本身并不复杂&#xff0c;但要处理其后的反应和规则则更具挑战性…

全新免押租赁系统助力商品流通高效安全

内容概要 全新免押租赁系统的推出&#xff0c;可以说是一场商品流通领域的小革命。想象一下&#xff0c;不再为押金烦恼&#xff0c;用户只需通过一个简单的信用评估&#xff0c;就能快速租到所需商品&#xff0c;这种体验简直令人惊喜&#xff01;这个系统利用代扣支付技术&a…

【Python科研数据爬虫】基于国家标准查询平台和能源标准化信息平台的海上风电相关行业标准查询信息爬取及处理

基于国家标准查询平台和能源标准化信息平台的海上风电相关行业标准查询信息爬取及处理 1 背景2 标准检索平台2.1 能源标准化信息平台2.2 全国标准信息公共服务平台3 标准信息数据的爬取与处理3.1 能源标准化信息平台的信息爬取3.2 全国标准信息公共服务平台的信息爬取3.3 标准信…

ThinkPHP 8高效构建Web应用-控制器

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《2025新书 ThinkPHP 8高效构建Web应用 编程与应用开发丛书 夏磊 清华大学出版社教材书籍 9787302678236 ThinkPHP 8高效构建Web应用》【摘要 书评 试读】- 京东图书 使用VS Code开发ThinkPHP项目-CSDN博客 控制器无须特…

2025-1-2-sklearn学习(30)模型选择与评估-验证曲线: 绘制分数以评估模型 真珠帘卷玉楼空,天淡银河垂地。

文章目录 sklearn学习(30) 模型选择与评估-验证曲线: 绘制分数以评估模型30.1. 验证曲线30.2. 学习曲线 sklearn学习(30) 模型选择与评估-验证曲线: 绘制分数以评估模型 文章参考网站&#xff1a; https://sklearn.apachecn.org/ 和 https://scikit-learn.org/stable/ 每种估…