python异步爬虫协程的使用学习笔记

最近学异步的时候对于协程的理解比较困难,感觉对于之前的多线程也有也陌生了(下个文章写一下多线程巩固一下)

目录

先来说明一下协程的定义:

asynico库使用

一 . event_loop事件循环

二 . await对象

三 . task对象

四 . future对象

回调方法


先来说明一下协程的定义:

协程, 我们又称为微线程,协程它不像线程和进程那样,需要进行系统内核上的上下文切换,协程的上下文切换是由开发人员决定的。

协程是一种用户级的轻量级线程。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。

我实话定义我一般没看懂过,因此用我简单用大白话来概括一下

所谓的协程就是在不同的任务中按照程序员的要求来反复横跳

首先我用yield来演示一下协程的工作流程

def func1():
    yield 1
    yield from func2()
    yield 2
def func2():
    yield 3
    yield 4
a=func1()
for item in a:
    print(item)

众所周知yield是个生成器函数,在这里我们在func1函数中使用了yield from意思是返回func2函数中,调用了func1函数之后先返回数字1,然后程序跳到func2中返回了数字3和4,在完成了yield from func2()后程序在返回到func1中返回了最后的数字4

因此最后输出结果就是

对应到爬虫上就是因为网站的反应有快有慢,当网站还未反应时程序只能干等着,如果加上协程就能在网页还未响应时去进行其他的操作,这样就能提高效率。

当前python的asynico库可以实现协程,不必安装python自带(需要python3.5以上)

asynico库使用

先交代一下基本的概念;

event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足发生条件的时候,就调用对应的处理方法。
coroutine:中文翻译叫协程,在Python中常指代协程对象类型,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用async关键字来定义一个方法,这个方法在调用时不会立即被执行,而是会返回一个协程对象。
task:任务,这是对协程对象的进一步封装,包含协程对象的各个状态。
future:代表将来执行或者没有执行的任务的结果,实际上和task没有本质区别。

一 . event_loop事件循环

用大白话来说事件循环就是一个死循环,用来检测那些任务可以运行,那些任务在休眠,并调用对应的处理方法。

先写个例子:

import asyncio
async def func():
    print("hello world")
loop=asyncio.get_event_loop()
loop.run_until_complete(func())

首先我们先理清一个概念,async开头的函数叫做协程函数,它与普通函数不一样,调用协程函数要先声明一个事件循环并传入协程函数才能调用

补充一点:在python3.7以上

loop=asyncio.get_event_loop()
loop.run_until_complete(func())的方法被优化为asyncio.run(func())但原理都一样

二 . await对象

这里就不引什么定义,直接词面意思理解,就是等await后的任务完成,一般当作调用就这么简单

举个栗子

import asyncio
async def func():
    print("4")
    await asyncio.sleep(2)
    print("5")
    return 6
async def main():
    print("1")
    print("2")
    reason=await func()
    print(reason)
loop=asyncio.get_event_loop()
loop.run_until_complete(main())

首先我们创立了两个协程函数,但只调用了第二个main()函数,注意这里我用了两个await方法,main函数里的reason=await func()是在打印出2后程序转到第一个函数运行等函数运行结束后接受返回的一个值赋值到reason,func()里的await asyncio.sleep(2)表示程序休眠两秒,最后返回一个6然后回到main()函数里完成剩下的。

所以结果就是

就是这么的丝滑流畅

三 . task对象

task对象可以这么理解,就是添加一个任务到事件循环中

强调一点:Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用 asyncio.create_task()函数以外,还可以用低层级的loop.create_task0或ensure_future0函数。不建议手动实例化Task 对象。注意:asyncio.create_task0 函数在 Python 3.7中被加入。在 Python 3.7之前,可以改用低层级的asyncio.ensure_future()函数

因为我的python是3.6版本所以低层次的函数

继续举一个栗子

import asyncio
async def func():
    print("4")
    await asyncio.sleep(2)
    print("5")
    return 6
async def main():
    print("1")
    print("2")
    task1=asyncio.ensure_future(func())
    task2=asyncio.ensure_future(func())
    await task1
    await task2
    print(task1)
    print(task2)
loop=asyncio.get_event_loop()
loop.run_until_complete(main())

首先通过asyncio.ensure_future()/asyncio.create_task()创建task对象,在由await方法调用添加到事件循环,跟await func()差不多。

但一般对于多个任务不这样用,一般都是建立一个任务列表

第二个栗子

import asyncio
async def func():
    print("4")
    await asyncio.sleep(2)
    print("5")
    return 6
async def main():
    print("1")
    print("2")
    task_list=[asyncio.ensure_future(func()),asyncio.ensure_future(func())]
    done,pending=await asyncio.wait(task_list)
    print(done)
loop=asyncio.get_event_loop()
loop.run_until_complete(main())

注意一点调用任务列表是不能用await+列表,必须要asyncio.wait()

还有done,pending=await asyncio.wait(task_list)这里的done,pending我解释一下done就是返回的集合,而pending啥意思呢?

done,pending=await asyncio.wait(task_list,timeout=2)后面可以加个timeout=2表明只给你2秒的反应时间,那问题来了如果反应不过来咋办?那就把已经处理好的放在pending里

四 . future对象

Task 继承Future,Task对象内部await结果的处理基于Future对象来的。

了解一下就行,并不需要过多理解

import asyncio

async def main():
    # 获取当前事件循环
    loop = async.get_running_loop()

    # 创建一个任务(Future对象),这个任务什么都不做
    fut = loop.create_future()

    # 等待任务最终结果(Future对象),没有结果则会一直等下去

    await fut
asynio.run(main())

回调方法

再写一个方法

直接看例子

import requests
async def func():
    url="https://www.baidu.com"
    response=requests.get(url)
    return response
def callback(response):
    print("status:",response.result())
task=asyncio.ensure_future(func())
task.add_done_callback(callback)
print("task:",task)
loop=asyncio.get_event_loop()
loop.run_until_complete(task)
print("task:",task)

这里我们定义了request方法,在这个方法里请求了百度,并获取了其状态码,但是没有编写任何print语句。随后我们定义了callback方法,这个方法接收一个参数,参数是task对象,在这个方法中调用print方法打印出了task对象的结果。这样就定义好了一个协程对象和一个回调方法。我现在希望达到的效果是,当协程对象执行完毕之后,就去执行声明的callback方法。
那么两者怎样关联起来呢?很简单,只要调用 add_done_callback 方法就行。我们将 callback 方传递给封装好的task对象,这样当task执行完毕之后,就可以调用callback方法了。同时task对还会作为参数传递给callback方法,调用task对象的result方法就可以获取返回结果了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值