zoukankan      html  css  js  c++  java
  • asyncio异步模块

    线程池实现爬取数据

    import requests
    import time
    
    from multiprocessing.dummy import Pool
    
    urls = [   "http://127.0.0.1:5000/zhou1",
        "http://127.0.0.1:5000/zhou2",
        "http://127.0.0.1:5000/zhou3"]
    
    def get_request(url):
    	response = requests.get(url).text
    	return len(response)
        
    if __name__ == '__main__':
        start = time.time()
    	pool = Pool(3) #线程的数量
    	 #使用get_request作为回调函数,需要基于异步的形式对urls列表中的每一个列表元素进行操作
        #保证回调函数必须要有一个参数和返回值
    	result_list = pool.map(get_reuqest,urls)   #(参数是执行的函数名,可迭代对象)
    	pool.close()#关闭线程池,不再接受新的进程
        pool.join()#主线程阻塞等待子线程的退出
        print(result_list)
        print(time.time()-start)
        
    补充:map方法是阻塞的方法。就是等待子线程都结束之后。主线程在结束
        
    #结果:[1002, 1002, 1002]
    2.1248600482940674   异步运行。所以是2秒,如果是同步运行那就是6S了
    

    协程基础

    概念部分

     特殊的函数
            - 如果一个函数的定义被async修饰后,则该函数就变成了一个特殊的函数
            - 特殊之处: 
                - 该特殊的函数调用后,函数内部的实现语句不会被立即执行
                - 该特殊函数被调用后会返回一个协程对象
        - 协程对象
            - 对象。通过特殊函数的调用返回一个协程对象。
            - 协程 == 特殊函数 == 一组指定的操作
            - 协程 == 一组指定的操作
        - 任务对象
            - 任务对象就是一个高级的协程对象。(任务对象就是对协程对象的进一步封装)
            - 任务 == 协程 == 特殊函数 == 一组指定操作
            - 任务 == 一组指定的操作
            - 如何创建一个任务对象:
                - asyncio.ensure_future(协程对象)
            - 任务对象的高级之处:
                - 可以给任务对象绑定回调:
                    - task.add_done_callback(task_callback)
                    - 回调函数的调用时机:
                        - 任务被执行结束后,才可以调用回调函数
                    - 回调函数的参数只可以有一个:表示的就是该回调函数的调用者(任务对象)
                    - 使用回调函数的参数调用result()返回的就是任务对象表示的特殊函数return的结果
        - 事件循环对象
            - 对象。
            - 作用:
                - 可以将多个任务对象注册/装载到事件循环对象中
                - 如果开启了事件循环后,则其内部注册/装载的任务对象表示的指定操作就会被基于异步的被执行
            - 创建方式:
                - loop = asyncio.get_event_loop()
            - 注册且启动方式:
                - loop.run_until_complete(task)
    

    代码部分

    import asyncio
    import requests
    import time
    
    async def get_request(url):
        print('正在请求的url:',url)
        time.sleep(2)
        print('请求结束:',url)
        return 'bobo'
        
    #回调函数的封装
    #参数t是该回调函数的调用者(任务对象)
    def task_callback(t):
    	print('i am task_callback(),参数t:',t)
    	#result返回的就是特殊函数的返回值
    	print("t.result()返回的是:",t.result())   #因为是回调函数所以能够获取特殊函数的返回结果,就是return值
    	
    #以下是使用了asyncio.ensure_future方法
    if __name__=="__main__":
    	#coroutine是一个协程对象
    	coroutine = get_request(“www.baidu.com”)
    	#任务对象就是对协程对象的进一步封装
    	task = asyncio.ensure_future(coroutine)
        #给task绑定一个回调函数
        task.add_done_callback(task_callback)
    	#创建事件循环对象
    	loop = asyncio.get_event_loop()
    	#将任务对象注册到事件循环中并且开启事件循环
    	loop.run_until_complete(task)
        
    #使用create_task方法类似,效果是一样的写法不一样
    if __name__=="__main__":
        #coroutine是一个协程对象
    	coroutine = get_request(“www.baidu.com”)
        #创建事件循环对象
    	loop = asyncio.get_event_loop()   #需要先创建loop循环对象
        #任务对象就是对协程对象的进一步封装
    	task = loop.create_task(coroutine)   #不同之处,loop调用的create_task方法来创建任务
        #给task绑定一个回调函数
        task.add_done_callback(task_callback)  #将回调函数名传进去
        #将任务对象注册到事件循环中并且开启事件循环
    	loop.run_until_complete(task)
       
    #结果:
    正在请求的url: www.baidu.com
    请求结束: www.baidu.com
    i am task_callback(),参数t: <Task finished coro=<get_request() done, defined at E:/爬虫练习/test/xxx.py:7> result='bobo'>
    t.result()返回的是: bobo
    

    多任务操作

    import  time
    import requests
    
    # async def get_request(url):
    #     print('正在请求的url:',url)
    #     time.sleep(2) #出现了不支持异步模块的代码  这就会导致无法异步操作
    #     print('请求结束:',url)
    #     return 'bobo'
    
    async def get_request(url):
        print('正在请求的url:',url)
        await asyncio.sleep(2) #支持异步模块的代码
        print('请求结束:',url)
        return 'bobo'
    
    urls = [
        'www.1.com',
        'www.2.com',
        'www.3.com'
    ]
    if __name__ == "__main__":
        start = time.time()
        tasks = [] #多任务列表
        #1.创建协程对象
        for url in urls:
            c = get_request(url)
            #2.创建任务对象
            task = asyncio.ensure_future(c)
            tasks.append(task)
    
        #3.创建事件循环对象
        loop = asyncio.get_event_loop()
        # loop.run_until_complete(tasks)
        #必须使用wait方法对tasks进行封装才可
        loop.run_until_complete(asyncio.wait(tasks))
        print('总耗时:',time.time()-start)
    #补充
     wait方法的作用:
            - 将任务列表中的任务对象赋予可被挂起的权限。只有任务对象被赋予了可被挂起的权限后,该
                任务对象才可以被挂起
                - 挂起:将当前的任务对象交出cpu的使用权。
        - 注意事项【重要】:
            - 在特殊函数内部不可以出现不支持异步模块对应的代码,否则会中断整个异步效果
            
    结果:
    正在请求的url: www.3.com
    正在请求的url: www.2.com
    请求结束: www.1.com
    请求结束: www.3.com
    请求结束: www.2.com
    总耗时: 2.0018727779388428
    

    多任务的异步爬虫

    import asyncio
    import time
    import aiohttp
    from lxml import etree
    urls = [
        'http://localhost:5000/bobo',
        'http://localhost:5000/tom',
        'http://localhost:5000/jay'
    ]
    # async def get_request(url):
    #     #requests是一个不支持异步的模块,所以无法实现异步请求,用aiohttp代替
    #     page_text = requests.get(url).text
    #     return page_text  
    
    async def get_request(url):
    	#实例化好一个请求对象
    	async with aiohttp.ClientSession() as sess:
    		#调用get发起请求,返回一个响应对象
    		async with await sess.get(url=url) as response:
    			#text()获取字符串形式的响应数据
    			#read()获取byte类型的响应数据
    			#json()获取json类型数据
    			page_text = await response.text()  #不管什么类型数据,都要加await
    			return page_text
    			
    #解析函数的封装
    def parse(t):
    	#获取请求的页面源码数据
    	 tree = etree.HTML(t.result)
    	 pare_text = tree.xpath('//a[@id="feng"]/text()')[0]
         #如果需要读写文件。正常写入即可
         return pare_text
         
    if __name__ == "__main__":
    	start = time.time()
    	tasks=[]
    	for url in urls:
    		coroutine = get_request(url)
    		task = syncio.ensure_future(coroutine)
    		task.add_done_callback(parse)
    		tasks.appende(task)
    	loop = syncio.get_event_loop()
    	loop.run_until_complete(asyncio.wait(tasks))
        print('总耗时:',time.time()-start)
        
     - await关键字
            - 在特殊函数内部,凡是阻塞操作前都必须使用await进行修饰。await就可以保证
            阻塞操作在异步执行的过程中不会被跳过!
    
    - aiohttp
        - 是一个支持异步的网络请求模块。
        - pip install 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关键字
            - 完整代码:
                async def get_request(url):
                    #实例化好了一个请求对象
                    with aiohttp.ClientSession() as sess:
                        #调用get发起请求,返回一个响应对象
                        #get/post(url,headers,params/data,proxy="http://ip:port")
                        with await sess.get(url=url) as response:
                            #text()获取了字符串形式的响应数据
                            #read()获取byte类型的响应数据
                            page_text = await response.text()
                            return page_text
        - 多任务爬虫的数据解析
            - 一定要使用任务对象的回调函数实现数据解析
            - why:
                - 多任务的架构中数据的爬取是封装在特殊函数中,我们一定要保证数据请求结束后,
                    在实现数据解析。
                    
        - 使用多任务的异步协程爬取数据实现套路:
            - 可以先使用requests模块将待请求数据对应的url封装到有个列表中(同步)
            - 可以使用aiohttp模式将列表中的url进行异步的请求和数据解析(异步)
    
    
  • 相关阅读:
    李晋的YIM每日签名
    C语言的基础知识
    数据库的分页方法总结
    C#操作XML的代码
    ASP文件操作的类
    spring配置文件的基本语法
    预测球队比赛成绩
    文件的读写和我的第一个网页
    python中关于turtle库的学习笔记
    python科学计算与可视化
  • 原文地址:https://www.cnblogs.com/zzsy/p/12687882.html
Copyright © 2011-2022 走看看