大家好!我是霖hero

Python并发编程有三种方式:多线程(Threading)、多进程(Process)、协程(Coroutine),使用并发编程会大大提高程序的效率,今天我们将学习如何选择多线程、多进程和协程来提高代码的效率、如何使用异步协程,并用协程来获取同程旅行酒店的评论数据。

目录

并发编程

多线程(Threading)

多进程(Process)

协程(Coroutine)

并发编程对比

异步协程

asyncio模块

aiohttp库

aiohttp基本使用

post请求

超时

限制连接池大小

实战演练

评论数据网页分析

酒店列表页网页分析

同步请求获取酒店id

获取酒店评论

数据保存

绘制词云


并发编程

多线程(Threading)

多线程(Threading):从软件或者硬件上实现多个线程并发执行的技术。能够在同一时间执行多于一个线程,进而提升整体处理性能,利用CPU和IO可以同时执行的原理,让CPU不会干巴巴地等待IO完成。

工作方式:多线程由三个部分组成:新任务、任务队列、线程。由于新建线程系统是需要分配资源的,终止线程系统是需要回收资源的,为了重用线程,把线程放在一个线程池中,如下图所示:

  • 线程池:里面提前建好N个线程,这些都会被重复利用;

  • 任务队列:当有新任务的时候,会把任务放在任务队列中。

当任务队列里有任务时,线程池的线程会从任务队列中取出任务并执行,执行完任务后,线程会执行下一个任务,直到没有任务执行后,线程会回到线程池中等待任务。

多进程(Process)

多进程:一个程序的执行实例就是一个进程,每一个进程提供执行程序所需的所有资源,利用多核CPU的能力,真正的并行执行任务。

工作方式:通过CPU调度器来并发执行进程,如下图所示:

协程(Coroutine)

协程:一种比多线程高效得多的并发模型,是无序的,为了完成某个任务,在执行的过程中,不同程序单元之间过程中无需通信协调,也能完成任务的方式,在单线程利用CPU和IO同时执行的原理,实现函数异步执行。

工作方式:如下图所示:

当请求程序发送网络请求1并收到某个站点的响应后,开始执行程序中的下载程序,由于下载需要时间或者其他原因使处于阻塞状态,请求程序和下载程序是不相关的程序单元,所以请求程序发送下一个网络请求。

并发编程对比

简单了解了并发编程的三种方式,接下来将对比这三种方式的优缺点并根据任务来选择对应的技术。

多进程Process

  • 优点:可以利用多核CPU并行运算;

  • 缺点:占用资源最多、可启动数目比线程少;

  • 适用于:CPU密集型计算。

多线程threading

  • 优点:比进程更轻量级,占用资源少;

  • 缺点:只能并发执行,不能利用多CPU,启动数目有限,占用内存资源,有线程切换开销;

  • 适用于:IO密集型计算、同时运行的任务数目要求不多。

协程Corutine

  • 优点:内存开销最少,启动协程数量最多;

  • 缺点:之前的库有限制、代码实现复杂;

  • 适用于:IO密集型计算、需要超多任务运行。

有人可能不知道什么是CPU、IO密集型计算,我们来简单讲解一下:

  • CPU密集型:指I/O在很短的时间就可以完成,CPU需要大量的计算机和处理,CPU占用率很高,压缩解压、加密解密、正则表达式搜索等都属于CPU密集型计算。

  • IO密集型:指I/O(硬盘/内存)的读写操作,CPU占用率较低,文件处理程序、网络爬虫、读写数据库都属于IO密集型计算。

好了,最后通过一张图来总结如何选择并发编程的方式:

异步协程

asyncio模块

asyncio模块是Python中实现协程的模块之一,其语法格式如下:

import asyncio
#定义协程
async def myfunc(url):await 响应的数据或调用下一个方法等等
#获取事件循环
loop=asyncio.get_event_loop()
#创建task列表
tasks=[loop.create_task(myfunc(url))]
#执行爬虫事件列表
loop.run_until_complete(asyncio.wait(tasks))

其中:

  • async:声明该方法是异步协程方法;

  • await:声明为异步协程可等待对象;

  • create_task:创建线程任务;

注意:异步协程操作不能出现同步操作,否则异步操作将失效或报错。

aiohttp库

requests请求库是不支持异步协程的,所以我们使用aiohttp请求库,这个请求库只能发送异步请求,下篇文章我们学习更强大的异步请求库httpx,httpx请求库既可以发送同步请求,也可以发送异步请求。

aiohttp基本使用

使用语法为:

import aiohttp
import asyncio
async def main():async with aiohttp.ClientSession() as session:async with session.get(url) as response:print(await 响应的数据或调用下一个方法等等)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

先导入aiohttp模块和asyncio模块,aiohttp.ClientSession() 相当于把aiohttp的功能传递给session,也就是说语法中的ClientSession()相当于aiohttp,接着我们就可以使用client来调用get请求调用asyncio.get_event_loop()方法进入事件循环,再调用loop.run_until_complete(main())方法运行事件循环,直到main运行结束。

post请求

网页请求中有两种类型,一种是Form Data类型和Request Playload类型

当我们发送post请求中,需要根据请求类型来传递是否使用data参数还是使用json参数

params = [('key', 'value1'), ('key', 'value2')]
async with aiohttp.ClientSession() as session:#data参数async with session.post(url,data=params) as resp:...#json参数async with session.post(url,json=params) as resp:

有时候,我们URL参数构造正确却获取不到数据,有可能是因为post请求中的参数选择不正确,

那么什么时候使用data参数,什么时候json参数呢?

判断方法很简单,当出现如下图的情况时,该网页请求是Form Data类型,要使用data参数:

当出现如下图情况时,该网页请求是Request Playload类型,要使用json参数:

超时

在某些情况下,我们调用第三方接口时,响应时间无法估计,这时可以指定超时时间,如果超时未处理成功,则直接跳过它,继续向下执行。aiohttp提供了ClientTimeout方法来设置超时,其语法格式如下:

timeout=aiohttp.ClientTimeout(total=5*60, connect=None,sock_connect=None, sock_read=None)
async with aiohttp.ClientSession(timeout=timeout) as session:

默认情况下,aiohttp使用总共300 秒(5 分钟)的超时时间,这意味着整个操作应该在 5 分钟内完成。

其中:

  • total:整个操作的最大秒数,包括连接建立、请求发送和响应读取;

  • connect:连接建立新连接或在超过池连接限制时等待来自池的空闲连接的最大秒数;

  • sock_connect:连接到新连接的对等点的最大秒数;

  • sock_read:从对等方读取新数据部分之间允许的最大秒数。

限制连接池大小

当我们要获取的数据量很大时,为了提高程序效率或防止访问网页过多造成对方网页瘫痪,通常情况下要设置连接池的大小,aiohttp提供了TCPConnector方法来限制连接池的大小,其语法格式为:

conn = aiohttp.TCPConnector(limit=30)
async with aiohttp.ClientSession(connector=conn) as session:...

上面的设置是并行连接总数限制为30,默认情况下限制数是100,当我们不想设置限制数目时,可以把limit参数中的值改为0即可。

好了,aiohttp请求库讲解到这里,接下来我们正式开始爬取同程旅行酒店评论。

实战演练

这次的爬虫过程是:

  1. 评论数据网页分析;

  2. 酒店列表网页分析;

  3. 同步请求获取酒店id

  4. 异步请求获取酒店评论;

  5. 保存数据到MongoDB中;

  6. 绘制词云图。

评论数据网页分析

随意进入一个酒店的详情页并打开开发者工具,如下图所示:

经过查找,我们发现评论数据保存在getCommentList数据包中,如下图所示:

那么我们观察一下getCommentList数据包的headers信息,如下图所示:

请求方式是post请求,而且是Request payload类型,所以我们在使用json参数。

经过观察,我们发现objectId是酒店的id,pageIndex是翻页参数,pageSize是每个url存放的数量,其他的参数要么是空,要么是定值,所以我们只要知道酒店的id就可以构造url来获取酒店的评论数据了。

酒店列表页网页分析

进入同程旅行酒店列表网页并打开开发者工具,如下图所示:

经过查找,发现酒店基本数据(酒店id等)存放在上图红框的数据包中,其URL链接为:

https://www.ly.com/hotelapi/v2/list?pageSize=20&t=1634638146507&city=80&inDate=2021-10-19&outDate=2021-10-20&filterList=8888_1&pageIndex=0&sugActInfo=

观察URL链接,可以推测pageSize每一页的酒店数据量,t是时间戳,city是城市编号,inDate、outDate为入住和离店的时间,filterList为定值,pageIndex是翻页。

好了,网页分析就到这里了,接下来正式开始编写代码来爬取评论数据。

同步请求获取酒店id

由于我们请求的酒店列表页的URL只要三四个,那么使用同步请求即可,主要代码如下图所示:

async def get_link(url):response=requests.get(url, headers=headers)json = response.json()data = json.get('data').get('hotelList')hotelName_list = []task=[]for i in data:hotelId = i.get('hotelId')hotelName=i.get('hotelName')task.append(get_commtent(hotelId,hotelName))hotelName_list.append(hotelName)await asyncio.wait(task)

首先定义协程方法get_link(),发送get网络请求,返回的数据类型为json格式,再通过get()方法来获取酒店名及酒店id,创建一个任务列表task,并将自定义的get_commtent()方法放在task任务列表中。

获取酒店评论

酒店id、酒店名已经获取了,接下来构造参数,主要代码如下所示:

#构造参数
data = {'keyword': '','objectId': hotelId,'pageIndex': i,'pageSize': '20','searchFeatures': [],'sortingInfo': {'sortingDirection': '1','sortingMethod': '0'}
}

发送异步请求,主要代码如下所示:

async with aiohttp.ClientSession()as session:async with session.post(url,json=data,headers=headers)as response:Json = await response.json()data = Json.get('data').get('comments')if data != None:for i in data:commtent_list = {'hotelName': hotelName,'content': i.get('content').replace(' ', ''),'commentScore': i.get('commentScore'),'createTime': i.get('createTime')}await saving_data(commtent_list)elif data == None:commtent_list = {'hotelName': hotelName,'content': '0','commentScore': '0','createTime': '0'}await saving_data(commtent_list)

首先通过async语法来声明为异步协程操作,再通过post()来发送网络请求,使用await方法来声明响应内容为异步协程可等待对象,再通过get()方法来提取数据并把数据传递到自定义方法saving_data()中。

注意:有些酒店可能是新开业的原因,没有评论,所以我们使用if-elif语句来防止因为没有评论而报错。

数据保存

数据已经获取了,接下来把数据保存在MongoDB数据库中,首先创建MongoDB数据库和数据集合,主要代码如下所示:

async def create_db(hotelName):#连接数据库client = pymongo.MongoClient(host='localhost', port=27017)#创造数据库db = client['comment_data']#创建数据表colist=db.list_collection_names()for i in hotelName:if i not in colist:db.create_collection(i)

创建数据库后,接下来将保存数据,主要代码如下图所示:

async def saving_data(commtent_list):#连接数据库client=pymongo.MongoClient(host='localhost',port=27017)db=client['comment_data']collection=db[commtent_list['hotelName']]#插入数据到数据库中result=collection.insert_one(commtent_list)

由于代码比较简单,我就直接在代码里面写注释。

最后调用asyncio.get_event_loop()方法进入事件循环,再调用loop.run_until_complete(get_link())方法运行事件循环,直到function运行结束。主要代码如下所示:

for i in range(0,3):t=time.time()url = f'https://www.ly.com/hotelapi/v2/list?pageSize=20&t={t}&city=80&inDate=2021-10-17&outDate=2021-10-18&filterList=8888_1&pageIndex={i}&sugActInfo='loop=asyncio.get_event_loop()loop.run_until_complete(get_link(url))

运行结果如下图所示:

好了,aiohttp异步协程爬取同程旅行酒店评论就讲到这里了,感谢观看!!!

Python爬虫——aiohttp异步协程爬取同程旅行酒店评论相关推荐

  1. Python 爬虫实战(二)爬取携程(国际)机票

    github项目:https://github.com/wzyblowfire/flightsmonitor 页面分析 首先进入携程网的国际机票网页分析,可以看出该网页是一个动态页面,也就是说单一的请 ...

  2. 【Python爬虫实战】使用Selenium爬取QQ音乐歌曲及评论信息

    本文对使用到的技术仅做简单的介绍,若想了解更多,请前往相应的官网网站进行学习. 本文适合对爬虫相关知识接触不多的新手,主要是普及Selenium如何做爬虫,大佬请跳过. 1.Selenium简单介绍 ...

  3. 送书 | aiohttp异步协程爬取同程旅行酒店评论并作词云图

    大家好!我是啃书君! Python并发编程有三种方式:多线程(Threading).多进程(Process).协程(Coroutine),使用并发编程会大大提高程序的效率,今天我们将学习如何选择多线程 ...

  4. 从入门到入土:Python爬虫学习|实例练手|爬取猫眼榜单|Xpath定位标签爬取|代码

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

  5. 从入门到入土:Python爬虫学习|实例练手|爬取百度翻译|Selenium出击|绕过反爬机制|

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

  6. 从入门到入土:Python爬虫学习|实例练手|爬取新浪新闻搜索指定内容|Xpath定位标签爬取|代码注释详解

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

  7. 从入门到入土:Python爬虫学习|实例练手|爬取百度产品列表|Xpath定位标签爬取|代码注释详解

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

  8. Python 爬虫实战,模拟登陆爬取数据

    Python 爬虫实战,模拟登陆爬取数据 从0记录爬取某网站上的资源连接: 模拟登陆 爬取数据 保存到本地 结果演示: 源网站展示: 爬到的本地文件展示: 环境准备: python环境安装 略 安装r ...

  9. Python爬虫利用18行代码爬取虎牙上百张小姐姐图片

    Python爬虫利用18行代码爬取虎牙上百张小姐姐图片 下面开始上代码 需要用到的库 import request #页面请求 import time #用于时间延迟 import re #正则表达式 ...

最新文章

  1. 关于 Git 提交这些规范,你都遵守了吗?
  2. basestring与str的区别
  3. 线程的属性 —— 分离的状态(detached state)、栈地址(stack address)、栈大小(stack size)
  4. 云计算及应用课程知识整理
  5. ArcGIS API for Silverlight地图加载众多点时,使用Clusterer解决重叠问题
  6. python学习笔记(二十)初识面向对象
  7. dedecms 在模板里引入php文件夹,dedecms如何添加并引入php文件
  8. 信息学奥赛一本通(1050:骑车与走路)
  9. java绘制一个饼图_一个简单的绘制饼图的 Java Bean 实例
  10. 计算机专业简述,简述计算机专业毕业论文完整版.doc
  11. 高频功率放大器的设计实现
  12. IIS-网站报500.19错误代码0x8007000d问题解决
  13. Java中浏览量怎么实现_Java刷视频浏览量点赞量的实现代码
  14. python正负数转换_python – 将正/负数舍入到最接近的“整数”
  15. 爬取起点小说网免费小说
  16. 广州科二化龙考场_广州化龙科目二?
  17. 技巧分享:视频配音怎么制作?(内附3种配音教程)
  18. java 通联支付接口_allinpay 通联支付接口实例
  19. [原]Dropship的流程
  20. 远程网络教学系统功能

热门文章

  1. 独家专访阿里高级技术专家北纬:Dubbo开源重启半年来的快意江湖
  2. 【Office】excel当前日期,下月日期
  3. 计算机语言学专业排名,2019QS世界大学学科排名,澳洲语言学专业排名Top200
  4. 为什么有的人戒烟,说戒就能戒掉,而大部分人戒了多次戒不掉?
  5. C语言100题打卡—第7题
  6. meterpreter使用详解
  7. 新能源储能仪表推荐-ACR10R-D16TE4 防逆流仪表通讯接线及Modbus-RTU协议通讯表说明
  8. 当前电子鼻系统数据处理中常用的模式识别技术
  9. 操作系统之核心态和用户态
  10. 特斯拉电动汽车接连发生两起致命车祸 美国监管机构已介入调查