python3 爬虫例子_如何让你写的爬虫速度像坐火箭一样快【并发请求】

开坑个新系列,主要面向新手,老司机可以忽略。
这个系列内的文章将会让你知道如何做到让你写的爬虫在运行的时候速度能像火箭一样快!
很多初学爬虫的朋友对于这方面的知识似乎是空白的,甚至还有一些在爬虫岗位上工作了一两年的人也搞不清楚在不使用爬虫框架的情况下,如何写出一个速度足够快的爬虫,而网上的文章大多是基于多进程/Gevent来写的,代码看起来就极其复杂,甚至有些人抄来抄去连多进程和多线程没搞清楚,如果是一个想学习这方面知识的人看到了这样的文章,多半会一脸懵逼。
综上所述,为了让关注我公众号的新手朋友们能快速掌握这些技巧,这个系列就这样诞生了~

话不多说,我们正式开始。在提升爬虫的速度这方面,最基础、最有效、最直接的操作是什么呢?没错,就是并发请求,如果你的爬虫整个逻辑是顺序执行的,请求的时候永远不会并发,那么你就会遇到像他这样的情况:《小白写了个壁纸的爬虫,能跑起来,但是感觉很慢,不知道怎么回事,请大佬指点》。

上面这是我昨天刷V2的时候看到的一个帖子,楼主的代码内容简单概括一下就完全是顺序执行的,每下载一个图片都需要等待当前这个图片下载完了才能继续下载下一个,这样子做当然会非常慢了!这篇文章就拿他的代码作为样例,在原来的基础上进行一些调整,从而让他写的这个爬虫的运行速度能从龟爬变成像坐火箭一样快!


首先,我们需要知道什么是并发,这里的并发指的是“并行发送请求”,意思就是一次性发出多个请求,从而达到节省时间的效果!那么并发和不并发的区别在哪呢?简单来说就是这样子的:

把爬虫比喻成工人,在不并发的情况下,一个工人一次只能做一件事情,所以必须要下载完一个图片才能继续下载下一个。

0026125f60061efefc07777d52d59701.png

而在并发的情况下,就有很多个工人一起在干活,每个工人都被分配了一件事情做,所以可以同时下载多个图片,速度自然就快了很多。

e82d1f7959f6a3a948d62d341732695f.png

当然,上面说的这个例子只是从一个宏观的角度上来看并发,实际在做的时候要让你的爬虫能并发请求的方式是分为多线程、多进程、协程三种的,并不是每一种方式在运行时的效果都像上面说的这样,这里先不做深入探讨,因为这不是本文的重点。我们现在只需要知道,只要能让爬虫并发请求,就能同时下载多个图片,让速度快得飞起,这样就够了。


那么我们要用上面说的三种方式里的哪一种来实现并发请求呢?这还用问吗?当然是选择代码最简单、改动最小,并且最容易看懂的协程啊!在Python3.4之后Python就引入了一个叫做asyncio的库,原生支持了异步IO,而在3.5之后Python又支持了asyncawait这两个语法,使得写异步代码可以像写同步代码一样简单易读。

刚刚又提到了两个词,同步和异步,这两个词的含义其实就跟上面的并发差不多,同步代码就是顺序执行的,而异步则不是,这里同样不做深入探讨,先知道有这么个东西就行了。

看到这里肯定会有人开始有疑问了,虽然前面说我们要用协程来实现并发请求,但是后面说的却是什么Python支持原生异步,那么这个异步跟协程的关系又是什么呢?

其实很简单,协程可以让你写异步代码的时候能像写同步代码一样简单,在Python3中写协程代码的核心语法就是asyncawait这两个,举个简单的例子吧:

def func():print(1)time.sleep(10)print(2)

这是一段普通的函数,它属于同步代码,里面的time.sleep是普通函数,也属于同步代码。

async def func():  # 调用协程函数的那个函数也需要是一个协程函数print(1)await asyncio.sleep(10)  # 调用协程函数的时候要在前面加awaitprint(2)

而这是一个协程函数,它属于异步代码,里面的asyncio.sleep是协程函数,也属于异步代码。

它们的区别显而易见,用协程来写异步代码,除了需要换成异步的库以外,就只是多了个asyncawait而已,是不是非常简单?


那么我们在了解了怎么写协程代码之后,就能开始优化那段慢成龟速的代码了吗?答案是否定的,那段代码中使用了requests库进行网络请求,而requests是一个同步库,不能在异步环境下使用;同样,文件操作用的openfile.write也是同步的,也不能在异步环境下使用。

所以在开始之前我们还需要了解两个库,分别是aiohttp和aiofiles,aiohttp是一个异步网络请求库,而aiofiles是一个异步文件操作库。(aiofiles是基于线程池实现的,并不是真正的原生异步,但问题不大,不影响使用)

切记,异步代码不能与同步代码混用,否则如果同步代码耗时过长,异步代码就会被阻塞,失去异步的效果。而网络请求和文件操作是整个流程中最耗时的部分,所以我们必须使用异步的库来进行操作!否则就白搞了!

好了,先来看看aiohttp的用法吧,官方文档上的示例大致如下:

async with aiohttp.ClientSession() as session:async with session.get(url) as resp:result = await resp.text()

是不是觉得很麻烦,不像requests库那么方便?还觉得两层async with很丑?有没有办法让它像requests库一样方便呢?

答案是有的,有一个叫作aiohttp-requests的库,它能让上面的这段代码变成这样:

resp = await requests.get(url)
result = await resp.text()

清爽多了对吧?我们等下就用它了!记得装这个库的前提是要先装aiohttp哦!

然后我们来看看aiofiles的用法,官方文档上的示例如下:

async with aiofiles.open('filename', mode='r') as f:contents = await f.read()
print(contents)

嗯,这个用起来就和用同步代码操作文件差不多了,没啥可挑剔的,直接用就完事了。

提示:aiohttp-requests默认是创建并使用了session的,对于一些需要不保留Cookie进行请求的场景需要自己实例化一个Requests类,并指定cookie_jar为aiohttp.DummyCookieJar


了解完了要用的库之后我们就可以开始对贴子中的代码进行魔改了,如果你用的不是Python3.5以上版本的话需要先准备一下环境。除了版本号大于等于3.5的Python以外,你还需要安装以下几个库:

  • aiohttp(异步网络请求库)
  • aiohttp-requests(让aiohttp用起来更方便的库)
  • aiofiles(异步文件操作库)
  • pillow(其实就是PIL库,代码中的图片操作有用到)

执行一下pip install aiohttp aiohttp-requests aiofiles pillow一次性装完,如果存在多个不同版本的Python环境记得区分好。


然后我们打开编辑器,开始改代码,首先调整一下导包的部分,将里面的requests替换成aiohttp-requests,像这样:

78fbbada73c5c90c6d4d765f88ad98bf.png

然后搜索一下requests,看看哪些地方用到了它。

5012e9b573de3c1cc28df1e786727726.png

接着把所有搜到的部分都给改成异步请求的。

c67212225076f98e6c731de59b35a025.png

同时不要忘了将所有调用过requests.get的函数都变成协程函数。

3597467bafb125768e0203c24b936f05.png

然后我们把文件操作的部分也换成异步的,使用aiofiles.open代替open

a0c18dd31b840dcbc2c4ee7690caf4c7.png

最主要的部分都换好了,接着我们将原先在if __name__ == '__main__':下的代码移到一个新写的协程函数run中,并且将调用前面协程函数的部分都加上await

21074b141d95f3bf27f0dd262632e911.png

再导入一下asyncio库,然后在if __name__ == '__main__':下写出这样的代码:

63c797fa1a865bccf81406406f8ae79e.png

上面这个是Python3.7之后才能用的写法,低于Python3.7要这样写:

b5c69cae1c4b68e200f57700449c9426.png

现在我们就可以运行一下看看修改后的代码能不能跑通了。

16942cfeb338936747ef7c142a6dbecb.png

这里报了个错,从错误堆栈中可以看出问题是出在response = await requests.get(url=url, headers=headers)这里的,原因是self.session._request方法没有key为url的参数。这个问题很好解决,只需要将url=url变成url就好了(本来也就没必要这么指定参数写)。将代码中所有用到requests.get并且存在url=url这种写法的都做一下调整:

706e47c3f06e46622f8b33cc7ef09c6f.png

调整完之后再运行一次就正常了,效果和原先的代码相同。

01d7ef756b9abfe6c1c90f164c7385b7.png

注意!仅仅是这样并不会让速度发生很大的变化!我们最后还需要将这一堆代码中最耗时且是顺序执行、没有并发请求的部分单独放到一个协程函数中,并且用asyncio.gather来并发调用(由于原本的逻辑较为混乱,这里除了并发请求以外还进行了一些其他的微调,主要是计数和文件路径的部分,无关紧要)。

f8907746732117228f862758f892692d.png

运行一下看看效果,刚运行起来一瞬间就刷了一排的下载完成,跟修改之前比起来简直是天差地别。

4264244a2e3516dd6b7845c266f8c4ab.png

这就是并发请求的威力!我们仅仅是对他原本的代码进行了一些微调,把最耗时的下载图片部分简单粗暴地使用asyncio.gather并发执行了一下,速度就从龟爬变成了像坐火箭一样快!(其实代码中还有很多可以优化的点,这里就不一一拿出来讲了)


最后给大家提个醒:

虽然并发请求非常牛逼,可以让你的爬虫变得飞快,但它也不是不存在任何问题的!

如果你的并发请求数量过大(又称并发数过高),你的爬虫就相当于是在对他人的服务器进行Dos攻击(拒绝服务攻击)了!

举个例子,你在爬一个小网站的时候为了自己爬的速度更快,对并发请求的数量毫无限制,使得你的爬虫一次性发出了几百、上千个请求,但一般的小网站根本扛不住这么高的并发!几乎会在一瞬间就被你的爬虫给打爆掉!试想一下,如果你是站长,看到这样的情形你会怎么想?

如果你不能理解这个例子所产生的效果是什么样的,可以自己搭建一个Web服务,只放一个简单的页面,然后开个几百并发去请求这个页面,这样你就能切身地体会到别人是什么感受了。

所以记住,一定要合理控制并发请求的数量,不要对对方网站造成过大的压力!你给别人留活路,别人才会给你留活路!

最后再留个小作业吧,如何对这个修改后的代码增加一道并发数的限制?在留言区给出你的答案。(提示:可通过搜索引擎查找【aiohttp并发连接数限制】和【python 列表切割】相关的内容)


这个时代各种东西变化太快,而网络上的垃圾信息又很多,你需要有一个良好的知识获取渠道,很多时候早就是一种优势,还不赶紧关注我的公众号并置顶/星标一波~

发送消息“爬虫速度提升之并发请求”到我的公众号【小周码字】即可获得本文代码下载地址~

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

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

相关文章

二叉排序树的删除+图解

图解 第一种情况 第二种情况 第三种情况 代码实现 package com.atguigu.binarysorttree;import com.sun.javafx.sg.prism.NGImageView; import javafx.scene.transform.Rotate;import java.io.InputStream; import java.util.Timer;/*** 创建人 wdl* 创建时间 2021/3/29* 描述…

如何一秒钟从头构建一个 ASP.NET Core 中间件

前言 其实地上本没有路,走的人多了,也便成了路。 -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最佳实践的方法&…

ssm(Spring+Spring mvc+mybatis)

在不久的之前我写了一篇ssh(SpringSpring mvchibernate)的文章,感兴趣的可以去这里看看~ 今天我来写一下ssm的增删改欧查案例: 数据库:mysql 浏览器:火狐浏览器 jdk:1.7 开发编辑器:myeclipse10.5 首…

C# winform程序免安装.net framework在XP/win7/win10环境运行

前文: 首先感谢群里的大神宇内流云 提供的anyexec for windows版本。 经过本人搭建虚拟机在xp环境 使用anyexec运行winfrom程序后,测试通过,如下是用的xp运行winfrom程序的部分截图 下面是n年前入坑C#写的winform。。。玩毒奶粉用的,勿喷。 .…

AVL树双旋转+图解

图解 代码实现 package com.atguigu.avl; /*** 创建人 wdl* 创建时间 2021/3/30* 描述*/ public class AVLTreeDemo {public static void main(String[] args) { // int[] arr{4,3,6,5,7,8};//创建一个AVLTree对象 // int arr[]{10,12,8,9,7,6};int[] arr{10,11…

[SSCore] 开源dotnet core 版本 SuperSocket

前言碎语 最近一直在做旧版本dotnet 程序迁移至dotnet core的工作, 非常欣慰dotnet社区的蓬勃发展, 目前大部分的第三方类库或开源代码都有了dotnet core版本 或者可以方便的找到替代方案. 这其中我唯一觉得遗憾的是dotnet 社区大名鼎鼎的socket 通讯框架SuperSocket SuperSock…

asp.net core mvc剖析:KestrelServer

KestrelServer是基于Libuv开发的高性能web服务器,那我们现在就来看一下它是如何工作的。在上一篇文章中提到了Program的Main方法,在这个方法里Build了一个WebHost,我们再来看一下代码: public static void Main( string [] args) …

win 7 mysql 1067_win7系统登陆MySQL服务出现1067错误的解决方法

很多小伙伴都遇到过win7系统登陆MySQL服务出现1067错误的困惑吧,一些朋友看过网上零散的win7系统登陆MySQL服务出现1067错误的处理方法,并没有完完全全明白win7系统登陆MySQL服务出现1067错误是如何解决的,今天小编准备了简单的解决办法&…

图的快速入门

快速入门案例 代码实现 package com.atguigu.graph;import java.util.ArrayList; import java.util.Arrays;/*** 创建人 wdl* 创建时间 2021/4/2* 描述*/ public class Graph {private ArrayList<String> vertexList;//存储顶点集合private int[][] edges;//存储图对应的…

.net core依赖注入的封装

现在流行的系统一般都采用依赖注入的实现方式&#xff0c;利用DI容器来直接获取所用到的类/接口的实例。.net core也一样采用DI的方式&#xff0c;提供了DI容器的接口IServiceCollection&#xff0c;并提供了基于该接口的缺省实现ServiceCollection。 这样我们就可以不再像以前…

图的深度优先遍历+图解

图解 代码实现 package com.atguigu.graph;import java.util.ArrayList; import java.util.Arrays;/*** 创建人 wdl* 创建时间 2021/4/2* 描述*/ public class Graph {private ArrayList<String> vertexList;//存储顶点集合private int[][] edges;//存储图对应的邻接矩阵…

Azure SQL的DTU和eDTU到底是个什么鬼

Azure SQL 使用了数据库事务单位 (DTU) 和弹性数据库事务单位 (eDTU)来作为一个计量单位。 但是DTU和eDTU是什么鬼啊? 官方文档这样解释 DTU 是一个资源度量单位&#xff0c;表示保证可用于单一数据库服务层内特定性能级别的单个 Azure SQL 数据库的资源。 DTU是一定比例的 C…

2015蓝桥杯省赛---java---B---1(三角形面积)

题目 三角形面积 解法 数学方法&#xff0c;直接求三角形的面积 88 - (82)/2 - (46)/2 - (84)/2 64 - (81216) 64 - 36 28 答案 28

深入理解Async/Await

C# 5 Async/Await 语法特性&#xff0c;极大地简化了异步编程&#xff0c;但我们知道&#xff0c;异步编程的基本原理并没有发生根本改变。也就是说&#xff0c;当一些复杂的东西看起来很简单时&#xff0c;它通常意味着有一些有趣的事情在背后发生。在计算机程序设计语言领域&…

2015蓝桥杯省赛---java---B---2(立方变自身)

题目 立方变自身 分析 简单枚举 i^3 99之后&#xff0c;数字越大&#xff0c;数字之和越不可能等于其自身。 代码 package com.atguigu.TEST;public class Demo01 {private static int ans;public static void main(String[] args) { // 6for (int i 1; i < 99; i) {…

【南京】.Net 开源基础服务线下技术交流会

南京地区的.net开发人员对基础服务这块感兴趣的&#xff0c;欢迎大家参加及会后继续交流&#xff0c;踊跃参与&#xff01;若对基础服务相关有深度技术交流的&#xff0c;后续交换联系方式&#xff0c;可一起深度合作。 .NET技术行业落地分享交流会 邀请南京地区.NET技术专家和…

mysql 语句块语法_MySQL ------ MySQL常用语句的语法 (三十四)

MySQL常用的语句语法注意&#xff1a;1、 | 符号用来指出几个选中中的一个&#xff0c;因此NULL | NOT NULL 表示给出null 或 not null2、包含在方括号中的关键字或子句是可选的(如 [like this])3、既没有列出所有的MySQL语句&#xff0c;也没有列出每一条子句和选项4、大写的表…

图的广度优先算法+遍历

图解 代码实现 package com.atguigu.graph;import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList;/*** 创建人 wdl* 创建时间 2021/4/2* 描述*/ public class Graph {private ArrayList<String> vertexList;//存储顶点集合private int[][…

.Net异步编程知多少

1. 引言 最近在学习Abp框架&#xff0c;发现Abp框架的很多Api都提供了同步异步两种写法。异步编程说起来&#xff0c;大家可能都会说异步编程性能好。但好在哪里&#xff0c;引入了什么问题&#xff0c;以及如何使用&#xff0c;想必也未必能答的上来。 自己对异步编程也不是很…

指纹识别开发1.0

在不久之前&#xff0c;用java和C#分别开发了个人脸识别&#xff0c;感觉挺不错的&#xff0c;于是脑袋一发热&#xff0c;想了想能不能搞个指纹识别&#xff0c;答案当然是能&#xff0c;那么问题来了&#xff0c;在人脸识别的时候可以借助自带摄像头提取你的face&#xff0c;…