一、基本概念

进程:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。进程是操作系统动态执行的基本单元。

线程:一个进程中包含若干线程,当然至少有一个线程,线程可以利用进程所拥有的资源。线程是独立运行和独立调度的基本单元。

协程:协程是一种用户态的轻量级线程。协程无需线程上下文切换的开销,也无需原子操作锁定及同步的开销。

同步:不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,称这些程序单元是同步执行的。

异步:为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式,不相关的程序单元之间可以是异步的。

多进程:多进程就是利用 CPU 的多核优势,在同一时间并行地执行多个任务。多进程模式优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程,但是操作系统能同时运行的进程数是有限的。

多线程:多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。

二、异步协程

Python 中使用协程最常用的库莫过于 asyncio,然后我们还需要了解一些概念:

event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足条件发生的时候,就会调用对应的处理方法。

coroutine:协程对象类型,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。

task:任务,它是对协程对象的进一步封装,包含了任务的各个状态,比如 running、finished 等。

另外我们还需要了解两个关键字:async(定义一个协程),await(用来挂起阻塞方法的执行)。下面是一个示例:

 import asyncio

 async def show(num):
print("Number is {}".format(num)) cor = show(1)
print("Coroutine: ", cor)
print("After execute...")
task = asyncio.ensure_future(cor)
print("Task: ", task)
loop = asyncio.get_event_loop()
loop.run_until_complete(cor)
print("Task: ", task)
print("After loop...")

运行结果如下:

Coroutine: <coroutine object show at 0x0000000012ED91A8>
After execute...
Task: <Task pending coro=<show() running at E:/Python/1.py:4>>
Number is 1
Task: <Task finished coro=<show() done, defined at E:/Python/1.py:4> result=None>
After loop...

这里首先使用async定义了一个show方法,传入一个数字然后打印出来,我们调用了这个方法,但是这个方法并没有执行,而是返回了一个Coroutine协程对象。然后我们使用了asyncio的ensure_future()方法,该方法会返回一个task对象,此时task的状态是pending。然后我们使用 get_event_loop() 方法创建了一个事件循环 loop,并调用了run_until_complete() 方法将协程注册到事件循环loop中,然后启动。最后我们才看到了show() 方法打印了输出结果,此时task的状态已经是finished了。

再来看一个例子:

 import time
import asyncio async def show(num):
print("Number is {}".format(num))
await asyncio.sleep(1) # 必须加await实现协程 这里asyncio.sleep(1)是一个子协程
# time.sleep(1) # time.sleep()不能与await搭配使用 start = time.time()
tasks = [asyncio.ensure_future(show(i)) for i in [1, 2, 3, 4, 5]] loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("Cost time: ", end - start)

这里我们有多个任务组成了一个列表tasks,然后我们将tasks添加到事件循环中,等到执行完毕了打印出所花费的时间。当我们使用await asyncio.sleep(1)的时候,结果如下:

Number is 1
Number is 2
Number is 3
Number is 4
Number is 5
Cost time: 1.0040574073791504

使用time,sleep(1)的时候结果如下:

Number is 1
Number is 2
Number is 3
Number is 4
Number is 5
Cost time: 5.001286029815674

结果很明显了,前者所花费的时间更少,原因在于await会将asyncio.sleep(1)这个协程暂时挂起阻塞,第一个任务(show(1))运行到这里的时候就会挂起,然后执行下一个任务(show(2)),以此类推,等到所有的任务都执行完毕,再执行asyncio.sleep(1),所以最后花费的时间就是一秒多一点了。

三、编写爬虫

1、aiohttp

要利用协程来写网络爬虫,还需要使用一个第三方库--aiohttp,aiohttp是一个支持异步请求的库,利用它和 asyncio配合我们可以非常方便地实现异步请求操作。没有安装的可以使用pip install aiohttp进行安装,其官方文档的链接是:https://aiohttp.readthedocs.io/en/stable/,需要注意的是aiohttp支持的python版本是3.5.3+,如果运行出错的话建议先检查下你的python版本。先来看看官网上给出的例子吧:

 import aiohttp
import asyncio async def fetch(session, url):
async with session.get(url) as response:
return await response.text() async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, 'http://python.org')
print(html) loop = asyncio.get_event_loop()
loop.run_until_complete(main())

首先是导入我们需要的模块,然后定义了一个fetch方法,传入的参数是一个session和一个url,然后使用session的get()方法去请求这个链接,并返回结果。在main方法中,首先引用了aiohttp里的ClientSession类,建立 了一个session对象,然后将这个session和一个链接传入到fetch方法中,最后将fetch方法返回的结果打印出来。

2、具体步骤

这次写的爬虫实现了对崔庆才的个人博客上的文章基本信息的爬取,包括标题、链接、浏览的数目、评论的数目以及喜欢的人数,最后分别将浏览数、评论数以及喜欢数排前十的文章统计出来并绘制出图表。

首先进入崔庆才个人博客,可以看到一页有二十篇文章,把页面下拉,就会出现更多的文章,显然这是动态加载的,于是我们打开开发者工具,继续下拉页面,然后在XHR选项中看到了我们需要的内容:

不停地下拉页面,会发现最后数字会定格在35,也就是说总共有35页,每页的链接都形如https://cuiqingcai.com/page/2,这样的话我们爬取的话就简单多了。基本思路是将所有链接组成一个列表,然后利用aiohttp去请求网页并返回结果,然后我们再对结果进行解析,对于解析得到的结果,保存在MongoDB数据库中。然后再对数据进行一下简单的分析,并绘制图表,结果如下:

完整代码已上传到GitHub

【Python3爬虫】使用异步协程编写爬虫的更多相关文章

  1. python爬虫---单线程+多任务的异步协程,selenium爬虫模块的使用

    python爬虫---单线程+多任务的异步协程,selenium爬虫模块的使用 一丶单线程+多任务的异步协程 特殊函数 # 如果一个函数的定义被async修饰后,则该函数就是一个特殊的函数 async ...

  2. Python爬虫进阶 | 异步协程

    一.背景 之前爬虫使用的是requests+多线程/多进程,后来随着前几天的深入了解,才发现,对于爬虫来说,真正的瓶颈并不是CPU的处理速度,而是对于网页抓取时候的往返时间,因为如果采用request ...

  3. python爬虫--多任务异步协程, 快点,在快点......

    多任务异步协程asyncio 特殊函数: - 就是async关键字修饰的一个函数的定义 - 特殊之处: - 特殊函数被调用后会返回一个协程对象 - 特殊函数调用后内部的程序语句没有被立即执行 - 协程 ...

  4. 小爬爬4.协程基本用法&&多任务异步协程爬虫示例(大数据量)

    1.测试学习 (2)单线程: from time import sleep import time def request(url): print('正在请求:',url) sleep() print ...

  5. 爬虫必知必会(4)_异步协程-selenium_模拟登陆

    一.单线程+多任务异步协程(推荐) 协程:对象.可以把协程当做是一个特殊的函数.如果一个函数的定义被async关键字所修饰.该特殊的函数被调用后函数内部的程序语句不会被立即执行,而是会返回一个协程对象 ...

  6. 协程实现爬虫的例子主要优势在于充分利用IO时间去请求其他的url

    # 分别使用urlopen和requests两个模块进行演示 # import requests # 需要安装的 # from urllib.request import urlopen # # ur ...

  7. 异步协程asyncio+aiohttp

    aiohttp中文文档 1. 前言 在执行一些 IO 密集型任务的时候,程序常常会因为等待 IO 而阻塞.比如在网络爬虫中,如果我们使用 requests 库来进行请求的话,如果网站响应速度过慢,程序 ...

  8. asyncio模块实现单线程-多任务的异步协程

    本篇介绍基于asyncio模块,实现单线程-多任务的异步协程 基本概念 协程函数 协程函数: 定义形式为 async def 的函数; aysnc 在Python3.5+版本新增了aysnc和awai ...

  9. Python中异步协程的使用方法介绍

    1. 前言 在执行一些 IO 密集型任务的时候,程序常常会因为等待 IO 而阻塞.比如在网络爬虫中,如果我们使用 requests 库来进行请求的话,如果网站响应速度过慢,程序一直在等待网站响应,最后 ...

随机推荐

  1. 【译】Flink + Kafka 0.11端到端精确一次处理语义的实现

    本文是翻译作品,作者是Piotr Nowojski和Michael Winters.前者是该方案的实现者. 原文地址是https://data-artisans.com/blog/end-to-end ...

  2. 学习Javascript数据结构与算法(第2版)笔记(1)

    第 1 章 JavaScript简介 使用 Node.js 搭建 Web 服务器 npm install http-server -g http-server JavaScript 的类型有数字.字符 ...

  3. C++中虚拟继承

    多重继承 在多重继承中,基类的构造函数的调用次序即不受派生类构造函数初始化列表中出现的基类构造函数的影响,也不受基类在构造函数初始化列表中的出现次序的影响,它按照基类在类派生列表中的出现次序依次调用相 ...

  4. laravel 查询数据返回的结果

    laravel查询数据返回的结果 在插入数据库的时候,发现查询数据返回的结果是一个对象;即使是空数据 返回的不是true或者false 那么要判断该结果是否查询有结果 该如果呢? 学习源头: http ...

  5. Java基础-抽象类和接口

    抽象类与接口是Java语言中对抽象概念进行定义的两种机制,正是由于他们的存在才赋予java强大的面向对象的能力.他们两者之间对抽象概念的支持有很大的相似,甚至可以互换,但是也有区别. 抽象定义: 抽象 ...

  6. openoffice excel word 转换pdf 支持本地调用和远程调用

    OpenOffice.org 是一套跨平台的办公室软件套件,能在Windows.Linux.MacOS X (X11)和 Solaris 等操作系统上执行.它与各个主要的办公室软件套件兼容.OpenO ...

  7. H5移动端项目案例、web手机微商城实战开发

    自微信生态圈一步步强大后,关于移动端购物的趋势,逐渐成为大众关心的内容,目前市场上关于移动商城的制定就有大量版本,比如.微商城.移动商城.移动webAPP.微信商城各等各种定义层出不穷,这就对于移动端 ...

  8. SPPNET

    SPPNet Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition 文章地址:https://ar ...

  9. java提高(15)---java深浅拷贝

    #java深浅拷贝 一.前言 为什么会有深浅拷贝这个概念? 我觉得主要跟JVM内存分配有关,对于基本数据类型,只存在栈内存,所以它的拷贝不存在深浅拷贝这个概念.而对于对象而言,一个对象的创建会在内存中 ...

  10. HTML/CSS初步了解

    一.CSS是什么? 它是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言.CSS为HTML标记语言提供了一种样式描述,定义了其中元素的显示 ...