python爬虫----DAY8----异步爬虫之协程

异步爬虫之协程

建议从代码部分开始阅读!!!不清楚的地方向上查看基本概念

异步编程大致流程:

  1.事件循环:理解为一个死循环(去检测并执行某些代码)
            #伪代码
        任务列表={任务1,任务2,任务3.。。。}
        while True:
            可执行的任务列表,已完成的任务列表=去任务列表中检查所有的任务,将’可执行‘和’已完					成‘的任务返回
            for 就绪任务   in  可执行的任务列表:
                执行已就绪的任务
            for 已完成的任务 in 已完成的任务列表;
                在任务列表中移除,已完成的任务
            如果 任务列表 中的任务都已完成,则终止循环

基本概念介绍

1. 特殊的函数

  • 如果一个函数的定义被async关键字修饰后,则该函数就变成了一个特殊的函数
  • 特殊之处:
    1. 该特殊的函数被调用后,内部的语句不会被立即执行
    2. 该函数被调用后返回一个协程对象

2. 协程对象

  • 一个对象,通过特殊的函数调用放回的一个协程对象。
    所以,协程 = 特殊的函数 =一组指定的操作 => 协程= 一组特殊的函数

3. 任务对象

  • 任务对象就是一个高级的协程对象(即对协程对象的进一步封装)
  • 有task和future两种。本质上无区别。但是基于loop创建的任务对象的前提是得有loop。

如何创建一个任务对象:
· asyncio.ensure_future(协程对象)—(future)
第二种方法为基于事件循环对象的创建task,在后面介绍

  • 任务对象的高级之处:
  1. 可以给任务对象绑定回调
    task.add_done_callback(task_callback)
  2. 回调函数的调用时机:
    任务被执行结束后,才可以调用回调函数
    -回调函数的参数只可以有一个;表示的就是该回调函数的调用者(任务对象)
    -使用回调函数的参数调用result()返回的就是任务对象表示的特殊函数的返回值

4. 事件循环对象

作用:
-可以将多个任务对象注册/装载到任务循环对象中
-如果开启了事件循环,则其内部注册/装载的任务对象表示的就是指定操作就是任务对象

    -创建方式:
        -loop=asyncio.get_event_loop()
    - 创建任务对象
    	- task=loop.creat_task(协程对象)
    -注册且启动方式:
        -loop.run_until_complete(task)

启动时不不能直接将task列表放入,要加asyncio.wait(tasks)修饰
wait()作用:可以将任务列表中的任务对象赋予可挂起权限。(每一个任务对象都可以被挂起)
任务对象的挂起:将当前挂起的任务对象交出CPU的使用权。只有当任务对象的CPU的使用权,
loop才可以使用CPU去执行下一个任务对象

-当loop在执行某一个任务对象时,遇到了阻塞操作,则loop会跳过阻塞操作执行下一个任务对象
-当loop在执行某一个任务对象时,前面一个任务对象的阻塞操作结束了,则loop会回头将该阻塞
结束的任务对象阻塞之后的操作进行执行

5. 注意事项!!!!!

            -在特殊函数内部不可以出现不支持异步模块对应的代码,否则会中断整个异步效果,requests不支持异步

    -await关键字
            -在特殊函数内部,凡是阻塞操作前,都必须使用await进行修饰,await就可以保证阻塞
            操作在异步执行过程中不会被跳过!!!
   
    -aiohttp
            -是一个支持异步的网络请求模块。
            -使用代码:
                    1.写一个大致的架构
                  async def get_request(url):
                         #实例化好了一个请求对象
                         with aiohttp.ClientSession() as sess:
                             #调用get发起请求,返回一个响应对象
                             #get/post(url,headers,params/data,proxy=''http://ip:port)
                             with sess.get(url=url) as response:
                                 #获取了字符串形式的响应数据
                                 page_text=response.text()
                                 return page_text
                   	2.补充细节:
                            -在阻塞操作前加await关键字,在每个with前加上async关键字(爬虫中只有发送请求和获取响应数据是阻塞操作)
                            
                    3。完整代码:
                            async def get_request(url):
                                #实例化好了一个请求对象
                                async with aiohttp.ClientSession() as sess:
                                    #调用get发起请求,返回一个响应对象
                                    #get/post(url,headers,params/data,proxy=''http://ip:port)
                                    async with await sess.get(url=url) as response:
                                        #text()获取了字符串形式的响应数据
                                        #read()获取byte类型的响应数据
                                        page_text=await response.text()
                                        return page_text

代码部分

安装模块 : pip install asyncio

1. 基本协程的使用

import asyncio

# 定义协程函数
async def request(url):
    print('正在请求:',url)
    print('请求结束',url)
#  async修饰的函数,调用后返回一个协程对象
c= request('www.baidu.com')

# 创建一个事件循环对象
loop=asyncio.get_event_loop()

# 将协程对象注册到loop中,然后启动loop
loop.run_until_complete(c)

2. 基于loop的任务对象的使用

import asyncio

async def request(url):
    print('正在请求:',url)
    print('请求结束',url)
#  async修饰的函数,调用后返回一个协程对象
c= request('www.baidu.com')

# task的使用

#创建事件循环对象
loop=asyncio.get_event_loop()

# 基于loop创建的task对象
# 通过creat_task创建任务对象,将协程对象封装进去
task=loop.create_task(c)
print(task)
# 注册并执行任务
loop.run_until_complete(task)
print(task)

在这里插入图片描述

3. 任务对象future的使用

import asyncio

async def request(url):
    print('正在请求:',url)
    print('请求结束',url)
#  async修饰的函数,调用后返回一个协程对象
c= request('www.baidu.com')

# future的使用

# 创建事件循环对象
loop=asyncio.get_event_loop()
# 创建future
future=asyncio.ensure_future(c)
print(future)
#注册并启动
loop.run_until_complete(future)
print(future)

状态与task相同

4. 回调函数的绑定和使用

import asyncio

async def request(url):
    print('正在请求:',url)
    print('请求结束',url)
    # 返回,回调函数可以通过.result获取返回值
    return url
#  async修饰的函数,调用后返回一个协程对象
c= request('www.baidu.com')

#绑定回调

# 回调函数的创建无需关键字async,参数只能有一个,即回调函数的调用者,task对象
def callback_func(task):
    print(task.result())
#创建事件循环对象
loop=asyncio.get_event_loop()
task=asyncio.ensure_future(c)
#将回调函数绑定到任务对象中
task.add_done_callback(callback_func)
#注册并启动任务
loop.run_until_complete(task)

在这里插入图片描述

5. 多任务协程

import asyncio
import time

#模拟请求发送
async def request(url):
    print('正在请求',url)
    time.sleep(2)
    print("下载完毕",url)
# 请求列表
urls=['www.baidu.com','www.sougou.com','www.goubanjian.com']

# 任务列表:存放多个任务对象
tasks=[]
# 计时
start_time=time.time()
for url in urls:
    c=request(url)
    task=asyncio.ensure_future(c)
    # 将创建的任务对象加入到任务列表中
    tasks.append(task)
# 创建任务循环对象
loop=asyncio.get_event_loop()
#注册和启动时不能直接放入列表,具体详情看基本概念 4
loop.run_until_complete(asyncio.wait(tasks))
print(time.time()-start_time)

在这里插入图片描述
原因:time是同步模块,在特殊函数内部不可以出现不支持异步模块对应的代码,否则会中断整个异步效果。 详情看注意事项

解决办法: 改用异步模块,await asyncio.sleep(2)

import asyncio
import time

#模拟请求发送
async def request(url):
    print('正在请求',url)
    # time.sleep(2)
    # 使用异步模块,
    await asyncio.sleep(2)
    print("下载完毕",url)
# 请求列表
urls=['www.baidu.com','www.sougou.com','www.goubanjian.com']

# 任务列表:存放多个任务对象
tasks=[]
# 计时
start_time=time.time()
for url in urls:
    c=request(url)
    task=asyncio.ensure_future(c)
    # 将创建的任务对象加入到任务列表中
    tasks.append(task)
# 创建任务循环对象
loop=asyncio.get_event_loop()
#注册和启动时不能直接放入列表,具体详情看基本概念 4
loop.run_until_complete(asyncio.wait(tasks))
print(time.time()-start_time)

在这里插入图片描述

6. aiohttp的使用—实战

详情可看注意事项

import asyncio
import time
import aiohttp #使用该模块中的ClientSession进行网络请求发送
import requests
from lxml import etree

# 请求列表
urls=['https://www.baidu.com/s?ie=UTF-8&wd=%E6%9D%8E%E7%99%BD'
,'https://www.baidu.com/s?ie=UTF-8&wd=%E6%9D%8E%E7%99%BD',
      'https://www.baidu.com/s?ie=UTF-8&wd=%E6%9D%8E%E7%99%BD'
      ]
headers={
        "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.67',
}
#请求发送
async def get_request(url):
    #实例化一个session对象发送请求
    async with aiohttp.ClientSession() as sess:
        # 请求发送,手动挂起
        async with await sess.get(url=url,headers=headers) as response:
            #text()方法可以返回字符串形式的响应数据
            #read()方法可以返回二进制形式的数据
            #json()方法返回的就是json对象
            # 注意在获取响应数据操作之前要使用await进行手动挂起
            page_text=await response.text()
            return page_text

# 解析响应数据
def parse(task):
    page_text=task.result()
    tree=etree.HTML(page_text)
    title=tree.xpath('/html/head/title/text()')[0]
    print(title)
# 任务列表:存放多个任务对象
tasks=[]
# 计时
start_time=time.time()
for url in urls:
    c=get_request(url)
    task=asyncio.ensure_future(c)
    #绑定回调
    task.add_done_callback(parse)
    # 将创建的任务对象加入到任务列表中
    tasks.append(task)
# 创建任务循环对象
loop=asyncio.get_event_loop()
#注册和启动时不能直接放入列表,具体详情看基本概念 4
loop.run_until_complete(asyncio.wait(tasks))
print(time.time()-start_time)


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

传说中的懿痕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值