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进行异步的请求和数据解析(异步)

おすすめ

転載: www.cnblogs.com/zzsy/p/12687882.html