协程应用——aiohttp异步爬虫实战

news2024/11/16 10:23:03

aiohttp异步爬虫实战

  • 1. 案例介绍
  • 2. 准备工作
  • 3. 页面分析
  • 4. 实现思路
  • 5. 基本配置
  • 6. 爬取列表页
  • 7. 爬取详情页
  • 8. 总结

1. 案例介绍

  • 本例要爬取的网站是https://spa5.scrape.center/,数据量相对大,所以用到了异步爬虫,主要学习这种方法是如何提高效率的。网站界面如图。
    在这里插入图片描述
  • 居然有500多页,老师辛苦了。
  • 这是一个图书网站,里面包含数千条图书信息,网站数据是JavaScript渲染而得。数据可以通过Ajax接口获取,并且为了学习用,接口没有设置任何措施和加密参数。也是因为这个网站数据量大,才更适合做异步爬取。
  • 本节我们要完成的目标:
    • 🍏 使用aiohttp爬取全栈的图书数据。
    • 🍏 将数据通过异步的方式保存到MongoDB中。

2. 准备工作

  • 开始完成本节任务前,要有做如下准备。
    • 安装好了Python3.7以上。
    • 了解Ajax爬取的知识。
    • 了解异步爬虫的知识和asyncio库的用法。
    • 了解aiohttp库的用法。
    • 安装并运行了MongoDB数据库,并安装了异步爬虫库motor。

3. 页面分析

  • 前面学习了Ajax的方法,按照同样的逻辑,我们分析本站的情况。

  • 列表页的Ajax请求地址格式为https://spa5.scrape.center/api/book/?limit=18&offset={offset}。其中limit的值是每页包含的书的数量;offset的值是换页之后的增量值。计算公式为offset = limit*(page-1)。如下图。
    在这里插入图片描述

  • 在列表页Ajax接口返回的数据里,results字段包含当前页里18本图书的信息。
    在这里插入图片描述

  • 其中每本书的数据里面包含一个id字段,这个id字段就是图书本身的ID,可以用来进一步请求详情页。如下图。在这里插入图片描述

  • 因此,详情页的Ajax请求地址格式为https://spa5.scrape.center/api/book/{id}。其中的id即为详情页对应图书的ID,可以从列表页Ajax接口的返回结果中获取内容。

4. 实现思路

  • 一个完善的异步爬虫应该能够充分利用等待的时间进行最大并发量的爬取处理。
  • 这里我们相实现的相对简单,分为两部分:(1)爬取列表页(2)爬取详情页。具体如下:
  • 第一步,异步爬取所有列表页,再把这些列表页作为列表,形成一个task,再进行异步爬取。
  • 第二步,把上面所有列表页的内容解析,找到所有的详情页的信息,再把这些详情页的信息作为列表,形成一个task,再进行异步爬取,并把结果放入MongoDB数据库中。

5. 基本配置

  • 首先,先配置一些基本的变量并引入一些必需的库,代码如下:
import asyncio
import aiohttp
import logging
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(levelname)s:%(message)s')

INDEX_URL = 'https://spa5.scrape.center/api/book/?limit=18&offset={offset}'
DETAIL_URL = 'https://spa5.scrape.center/api/book/{id}'
PAGE_SIZE = 18
PAGE_NUMBER = 100
CONCURRENCY = 5
  • 这里我们导入了asyncio、aiohttp、logging三个库然后定义了logging在基本配置,接着定义了URL、当前页面中的元素PAGE_SIZE 、数量爬取页码数量PAGE_NUMBER 、并发量CONCURRENCY 等信息。

6. 爬取列表页

  • 第一阶段,先爬取列表页。
  • 先定义一个通用的爬取方法。代码如下:
""" 爬取列表页"""

# 设定一个并发限制,信号量为5
semaphore = asyncio.Semaphore(CONCURRENCY)
session = None

# 定义一个函数,引入信号量
async def scrape_api(url):
    async with semaphore:
        try:
            logging.info('scraping %s', url)
            # 调用session的get方法,返回json格式的数据
            async with session.get(url) as response:
                return await response.json()
        except aiohttp.ClientError:
            logging.error('error occurred while scraping %s',
                          url, exc_info=True)
  • 这里声明一个并发量,用来控制最大并发量。
  • 接着,定义了scrape_api方法,接收一个参数url。该方法首先使用async with语句引入信号量(并发量)作为上下文,接着调用session的get方法请求这个url,然后返回响应的JSON格式的结果。另外,这里还进行了异常处理,捕获了ClientError,如果出现错误,就会输出异常信息。
  • 然后,爬取列表页,实现代码如下:
# 定义爬取列表页的函数
async def scrape_index(page):
    url = INDEX_URL.format(offset=PAGE_SIZE * (page-1))
    return await scrape_api(url)
  • 显然,这里定义的scrape_index方法接收一个参数page,用于爬取列表页。先构造完整的url,再传给上面的scrape_api函数。这里面再次用async定义为异步方法,而且返回时调用scrape_api方法前面再加await,是因为scrape_api调用之后,会返回一个协程对象,它的作用就是暂时等待。最后的结果JSON格式,也是我们想要的。
  • 接下来,我们定义main方法。
async def main():
    global session
    session = aiohttp.ClientSession()
    scrape_index_tasks = [asyncio.ensure_future(
        scrape_index(page)) for page in range(1, PAGE_NUMBER + 1)]
    results = await asyncio.gather(*scrape_index_tasks)
    logging.info('results %s', json.dumps(
        results, ensure_ascii=False, indent=2))

if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(main())
  • 这里定义的scrape_index_tasks,是用于爬取列表页的所有task完成的JSON结果集组成的列表。然后调用asyncio的gather(聚集)方法,将这个task列表作为其参数,将结果赋值给results,那么这个results最后再由主入口程序中的loop对象的run_until_complete方法注册到事件循环,启动完成。
  • main方法中,用全局变量声明了session变量,方便使用。
  • 把上面代码全部放入一个py文件中执行,在VSCode中,发现一个问题。如图。
    在这里插入图片描述
  • 搜索了下百度,找到了答案。说是VScode调试Runtime error报错是因为解释器里面已经有loop循环事件。所以把上面
import nest_asyncio
nest_asyncio.apply()
  • 这两行加入,就解决了。
  • 运行结果如下:
PS D:\Programs\PythonProject\Practice\threading> & D:/Programs/Python/Python310/python.exe d:/Programs/PythonProject/Practice/threading/asyncio_aiohttp.py
2023-01-17 22:54:38,037 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=0
2023-01-17 22:54:38,063 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=18
2023-01-17 22:54:38,063 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=36
2023-01-17 22:54:38,064 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=54
2023-01-17 22:54:38,064 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=72
2023-01-17 22:54:38,537 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=90
2023-01-17 22:54:39,161 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=108
2023-01-17 22:54:39,177 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=126
2023-01-17 22:54:39,472 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=144
2023-01-17 22:54:40,067 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=162
2023-01-17 22:54:40,077 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=180
2023-01-17 22:54:40,668 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=198
2023-01-17 22:54:40,676 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=216
2023-01-17 22:54:41,173 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=234
2023-01-17 22:54:41,672 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=252
2023-01-17 22:54:42,064 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=270
2023-01-17 22:54:42,073 - INFO:scraping https://spa5.scrape.center/api/book/?limit=18&offset=288
………………

7. 爬取详情页

  • 第二阶段,再爬取详情页,并保存数据。
  • 在main方法里面加入解析功能的代码。
    ids = []
    for index_data in results:
        if not index_data:continue
        # 因为都是json数据,所以以键取值
        for item in index_data.get('results'):
            ids.append(item.get('id'))
  • 得到所有的id以后,就可以构造详情页的地址,从而形成对应的task,再进行异步爬取。
  • 这里再定义两个方法,用于爬取详情页。如下:
from motor.motor_asyncio import AsyncIOMotorClient

MONGO_CONNECTION_STRING = 'MongoDB://localhost:27017'
MONGO_DB_NAME = 'scrape_book'
MONGO_COLLECTION_NAME = 'books'

client = AsyncIOMotorClient(MONGO_CONNECTION_STRING)
db = client(MONGO_DB_NAME)
collection = db[MONGO_COLLECTION_NAME]

async def save_data(data):
    logging.info('saving data %s', data)
    if data:
        return await collection.update_one({
            'id' : data.get('id')
        },{
            '$set' : data
        }, upsert=True)

async def scrape_detail(id):
    url = DETAIL_URL.format(id=id)
    data = await scrape_api(url)
    await save_data(data)
  • 这里定义了scrape_detail方法用于爬取详情页数据,并调用save_data方保存到MongoDB里面。
  • 这里用到了支持异步的MongoDB存储库motor。motor的声明和pymongo类似,不过方法是异步方法。
  • 接着,再在main方法里面加入对scrape_detail的调用,完成详情页的爬取。代码如下 。
    # 加入对scrape_detail方法的调用,用来爬取详情页,并保存到数据库
    scrape_detail_tasks = [asyncio.ensure_future(scrape_detail(id)) for id in ids]
    await asyncio.wait(scrape_detail_tasks)
    await session.close()

8. 总结

  • 结论就是,在爬取数据量大的网站时,优先使用异步爬虫。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Maven学习(三):纯手撸一个Maven项目

纯手撸一个Maven项目一、创建Maven工程目录二、Maven项目构建命令三、插件创建工程1、创建java工程2、创建web工程3、对比java工程和web工程区别一、创建Maven工程目录 按照下图所示的结构创建项目所需文件夹: 在Demo.java文件内输入以下代码: package…

数据库被勒索删除,解决方法

突然数据库被黑了,有一条勒索信息: To recover your lost Database send 0.018 Bitcoin (BTC) to our Bitcoin address: bc1qe4yefrptv2k8shawu3h84j0n8kyvxfk4wwate5 After this, contact us by email with your Server IP or Domain Name and a Proof of Payment …

JavaScript中的严格模式

一.什么是严格模式 在ECMAScript5标准中,JavaScript提出了严格模式的概念: 严格模式是一种具有限制性的JavaScript模式,从而使代码隐式脱离了“懒散(sloppy)模式”;支持严格模式的浏览器在检测到代码中有严格模式时,…

卡方检验的基本原理详解

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录一、卡方检验基本原理1. 1 χ2统计量计算公式1.2 理论频数如何计算?1.3 χ2值的结果如何理解?1.4 χ2检验的自由度如何理解?1.5 χ…

Arduino开发串口控制ESP8266 RGB LED

根据板卡原理RGB三色LED对应引脚:int LEDR12、int LEDG14、int LEDB13;设置串口波特率为115200Serial.begin(115200);源代码如下所示:/*名称:串口控制RGB亮灭实验功能:通过串口输入R、G、B三个字母来点亮对应的LED灯,关…

Java集合进阶——Map

一、Java Map集合详解 Map集合概述和特点 概述: 将键映射到值的对象 一个映射不能包含重复的键 每个键最多只能映射到一个值 Map接口和Collection接口的不同 Map是双列的,Collection是单列的 Map的键唯一,Collection的子体系Set是唯一的 Map集合的数据结构针对键有…

放假第三天

假期 # 生活 # 水文 咱们继续假期第三天的日常更文,没看上篇的铁子们我把地址贴在下面。 点我 虽然是假期,但我规划已久的睡懒觉流程却是一直执行不下去。这不今天早上八点我就起床了,当然起的早不是为了“卷”,而是吃早餐。说出…

Python操作 JWT(python-jose包)、哈希(passlib包)、用户验证完整流程

一、JWT简介 JWT是什么? JWT 即JSON 网络令牌(JSON Web Tokens)。 JWT(JSON Web Token) 是一种用于在身份提供者和服务提供者之间传递身份验证和授权数据的开放标准。JWT是一个JSON对象,其中包含了被签名的声明。这些声明可以是…

电脑开机出现绿屏错误无法启动怎么办?

电脑开机出现绿屏错误无法启动怎么办?有用户电脑开机的时候,突然出现了屏幕变成绿色的情况,而且上面有很多的错误代码。然后卡在页面上一直无法进入到桌面,重启电脑后依然无效。那么如何去解决这个问题呢?来看看具体的…

Java---Spring---SpringCache

SpringCache入门学习SpringCache介绍SpringCatch常用注解SpringCatch使用1.导入maven坐标2.配置application.yml3.在启动类上加入EnableCaching注解,开启缓存注解功能4.在controller的方法上加入Cacheable,CacheEvict等注解,进行缓存操作缓存穿透定义解决…

【Nginx】入门看这一篇就够啦,nginx 简介、安装、工作原理、工作方式、详解配置文件

目录 1、nginx 简介 2、nginx的工作原理 3、nginx 工作方式 4、nginx 安装 命令行安装 卸载命令 从源码构建 查看版本 测试启动 5、详解nginx配置文件 第一部分:全局块 第二部分:events块 第三部分:http 6、hosts 文件简介 1、…

解析Activity启动-窗口篇

解析Activity启动-窗口篇 在 解析Activity启动 前两篇文章中,我们分别专注于 堆栈 和 生命周期角度大致的过了一遍启动流程,而本篇会着重窗口的创建和显示流程,继续梳理Activity的启动流程 顺着前两篇文章的分析流程,我们知道和 …

DBCO高分子PEG_DBCO-PEG-Lipoic COOH_二苯并环辛炔-聚乙二醇-硫辛酸

DBCO-PEG-Lipoic acid“点击化学"一般由叠氮化物(azide)和炔烃(alkyne)作用形共价键,具有高效稳定,高特异性等优点。反应不受PH影响,能在常温条件下的水中进行,甚至能在活细胞中进行。DBCO…

第十三届蓝桥杯省赛 JAVA A组 - 矩形拼接

✍个人博客:https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 📚专栏地址:蓝桥杯题解集合 📝原题地址:付账问题 📣专栏定位:为想参加蓝桥别的小伙伴整理常考算法题解,祝大家…

Python学习中的六个技巧小结

1. 引言 “Beautiful is better than ugly.” 上述为著名的The Zen of Python的第一句话,也是有追求的python开发人员的信条之一。 所以我们的问题来了: 如何编写漂亮的Python代码? 本文重点通过九个示例向大家展示Python中的六个小技巧,以帮…

java后端-servlet超详细入门

java后端介绍今天我正式开始了一个新话题,那就是 Web。目前我主要会介绍后端。作为后端的老大哥 java,也有很多后端框架,比如大家耳熟能详的 spring 等。今天来带大家入门 servlet,不管是学生,刚毕业或是已经工作自学编…

【倍增】魔力小球

今天最后一篇,该睡了,怕猝死QwQ学校OJ上的一道模板题,去年不会做,今年还是不会做嘻嘻,还好最后调出来了,错的原因竟然是题目有歧义这个小球i的i是他喵的小球编号,不是id!出题人是懂出…

Win11的两个实用技巧系列之电脑system占用高的解决办法

Win11 system占用cpu过高是什么原因? Win11电脑system占用高的解决办法Win11 system占用cpu过高是什么原因?Win11系统遇到system占用cpu很高,该怎么解决呢?下面我们就来看看Win11电脑system占用高的解决办法System占用cpu过高导致电脑卡顿&a…

2023年中职网络安全技能竞赛网页渗透(注入版)

竞赛任务书内容 (一)拓扑图 网页渗透 任务环境说明: 服务器场景:Server2121 服务器场景操作系统:未知(封闭靶机) 用户名:未知 密码:未知 1.访问服务器网站目录1,根据页面信息完成条件,将获取到的flag提交; 2.访问服务器网站目录2,根据页面信息完成条件,将获…

学生写字台灯用什么牌子的好?高品质学生台灯品牌推荐

学生写字台灯,很明显就是为学生而设计的,针对学生长时间学习,用眼强度大的特点,这种学生台灯在设计上对灯光的亮度、样式、护眼技术都是很有讲究的,为的就是保护学生眼睛,在一定程度上缓解眼部疲劳的作用。…