zoukankan      html  css  js  c++  java
  • 单线程实现了多任务异步协程

    单线程+多任务异步协程:


    意义:提升爬取数据的效率,我们也可以使用线程池,

    异步爬虫方式:

    • 多线程/多进程(电脑吃不消,没办法无节制开启)不建议
    • 池:池中的线程或进程也是无法任意开启.
    • 单线程+多任务异步协程(推荐)(500个协程,最优)

    概念:

    协程:本质就是一个对象,协程对象,怎么去获取?可以使用asynic该关键字去修饰一个函数定义,此时的函数就叫它特殊函数,当该特殊函数被调用之后,就可以返回一个协程对象,特殊之处不仅仅是返回一个协程对象,当函数内部实现的语句不会被立即执行(时间循环开启后执行),导入模块import asycio

    import asyncio
    #特殊函数:
    async  def test(num):
    		print(num)
    c = test(10)
    print(c)
    #内部函数语句没有被执行,返回的是一个协程对象,一个地址,仅仅一个协程对象没有意义
    
    
    

    1565053666398

    任务对象:

    • 本质上就是对协程对象的进一步封装,封装之后也是一个协程对象,任务对象变相等于一个特殊函数.
    • 给任务对象绑定一个回调:add_done_callback(callback)时间循环执行完成后再执行回调函数.
    import asyncio
    #特殊函数:
    async  def test(num):
    		print(num)
    c = test(10)
    #内部函数语句没有被执行,返回的是一个协程对象,一个地址,仅仅一个协程对象没有意义
    
    #进行封装,根据一个协程对象封装一个任务对象
    task = asyncio.ensure_future(c)
    
    

    1565053977269

    绑定回调:

    import asyncio
    import time
    
    async def request(url):
              print('正在请求:',url)
              time.sleep(2)
              print('请求完成':url)
              return url
    
            
    #定义一个任务对象的回调函数:
    #task参数就是该函数被绑定的任务对象,必须要有.
    def task_callback(task):
        print('a callback')
        print(task)
        print(task.result())
    #task.result()返回的是特殊函数内部的返回值.
    
    
    
    c1 = request('www.qq.com')#协程对象
    c2 = request('www.qq.com')
    c3 = request('www.qq.com')
    task_A = asyncio.ensure_future(c1)
    task_B = asyncio.ensure_future(c2)
    task_C = asyncio.ensure_future(c3)#任务对象
    
    #绑定回调:函数请求完毕后调用
    #只传回调函数的名字,没有传递参数,task就是参数,
    task.add_done_callback(task_callback)
    
    
    #创建时间循环对象
    loop = asyncio.get_event_loop()
    #将任务对象注册到事件循环对象中并且开启事件循环
    loop.run_until_complete(task_A)#放协程对象会是可以的,肯定不会把协程对象直接注册到事件循环
    

    事件循环:(eventloop)我们必须将任务对象注册到事件循环对象中,开启事件循环对象.无限的循环对象(无法确定执行次数是多少次,遇到多少次阻塞).

    事件循环开启之后,a,b,c都开始循环执行,都会有阻塞,如果单线程的话,会耗时,事件循环是异步核心,事件循环对象执行任务对象是基于异步的,执行a对象的发现阻塞时,挂起阻塞对象,开始同时执行b,b遇到阻塞时也会执行c,但是此时当a阻塞完毕时,他会直接去执行a,等a处理完成后再去执行c.(a被执行了两次,所以跟任务对象个数无关)

    import asyncio
    import time
    
    async def request(url):
              print('正在请求:',url)
              time.sleep(2)
              print('请求完成':url)
    
    c1 = request('www.qq.com')#协程对象
    c2 = request('www.qq.com')
    c3 = request('www.qq.com')
    task_A = asyncio.ensure_future(c1)
    task_B = asyncio.ensure_future(c2)
    task_C = asyncio.ensure_future(c3)#任务对象
    
    #创建事件循环对象
    loop = asyncio.get_event_loop()
    #将任务对象注册到时间循环对象中并且开启事件循环
    loop.run_until_complete(task_A)#放协程对象会是可以的,肯定不会把协程对象直接注册到事件循环
    

    关键字:

    await:

    当前阻塞时,交出cpu的控制权,就挂起.

    注意事项:

    • 特殊函数内部不可以出现不支持异步的模块代码
    • 特殊函数内部遇到阻塞操作时,必须使用await对其进行手动挂起.
    • 如果想要将多个任务注册到事件循环中,必须将多个任务对象装进一个列表中,必须使用wait方法将列表中的任务进行挂起.

    多任务异步协程对象:

    import asyncio
    import time
    
    #在特殊函数内部不可以出现不支持异步模块相关的代码.time模块不支持异步,
    async def request(url):
              print('正在请求:',url)
              #time.sleep(2)
              #阻塞之后挂起,手动的设置
              await asyncio.sleep(2)
              print('请求完成':url)
              return url
    urls = [
    	'www.goo.com',
        'www.sss.com',
        'www.sss.com',
    	'www.sss.com',
    ]
    
    def task_callback(task):
        print(task.result())
        
    tasks = []
    for url in urls:
        c = request(url)
        task = asyncio.ensure_future(cc)
        task.add_done_callback(task_callback)
        task.append(task)#装进一个列表内
        
        
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))#将任务列表中的任务对象进行挂起操作
        
    

    requests模块不支持异步,从而我们引入支持异步的模块aiohttp

    aiohttp模块:支持异步网络请求模块,pip install aiohttp

    import asyncio
    import time
    import aiohttp
    start = time.time()
    
    
    #在特殊函数内部不可以出现不支持异步模块相关的代码
    #简单的框架:在此基础上补充细节
    async def request(url):
        	with aiohttp.ClientSession() as s:
                #s.get/post和requests中的get/post用法几乎一样:url,headers,data/params
                #在s.get中如果使用代理操作:proxy = 'http://ip:port'
                with s.get(url) as response:
                    #获取字符串类型的响应数据:response.text()
                    #获取byte类型:response.read()
                    page_text = response.text()
                    return page_text
    #细节1:在每一个with前加上async关键字
    #细节2:在get方法前和response.text()前加上await关键字进行手动挂起操作
    
    #真实代码:
    async def request(url):
        async with aiohttp.ClientSession() as s:
           async with await s.get(url) as response:
            page_text = await response.text()
    	    return page_text
    urls = []
    for i in range(500):
        urls.append('https://127.0.0.1:5000/bobo')
    
    def parse(task):
        page_text = task.result()
        print(page_text+',请求数据')
        
    tasks =[]
    for url in urls:
        c = request(url)
        task = asyncio.ensure_future(c)
        task.add_done_callback(parse)
        tasks.append(task)
        
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    
    print(time.time()-start)
        
    

    aiohttp案例:

    import aiohttp
    import asyncio
    from lxml import etree
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
    }
    
    async def request(url):
        async with aiohttp.ClientSession() as s:
            async with await s.get(url,headers=headers) as response:
                page_text = await response.text()
                return page_text
    
    urls = []
    all_titles = []
    url = 'http://wz.sun0769.com/index.php/question/reply?page=%d'
    for page in range(10):
        u_page = page*30
        new_url = format(url%u_page)
        urls.append(new_url)
    
    def parse(task):
        page_text = task.result()#page_text
        tree = etree.HTML(page_text)
        tr_list = tree.xpath('/html/body/div[4]/table[2]//tr')
        for tr in tr_list:
            title = tr.xpath('./td[2]/a[1]/text()')[0]
        	print(title)
            all_titles.append(title)
        
    tasks = []
    for url in urls:
        c = request(url)#协程对象
        task = asyncio.ensure_future(c)#封装成任务对象
        task.add_done_callback(parse)
        tasks.append(task)
        
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
        
        
    

    async:

    selenium基本使用:

    概念:基于浏览器自动化的模块.

    selenium和爬虫之间的关联:很便捷的捕获动态加载的数据,实现模拟登陆,登陆成功之后可以将页面的爬取.

    使用环境:

    pip install selenium

    下载一个浏览器的驱动程序

    创建一个浏览器对象

    from  selenium import webdriver
    
    
    bro = webdriver.Chrome(executable_path = 'chromedriver.exe')#驱动程序路径
    
    #发起指定url请求
    bro.get('http:www.baidu.com')
    
    #再搜索框中搜索
    
    #可以使用find系列方法标签定位
    bro.find_element_by_xpath('//*[@id="key"]')
    
    #向搜索框中写入商品名称
    search_input.send_keys('iphone')
    sleep(2)
    btn = bro.find_element_by_xpath('//*[@id="search"]/div/div[2]/button')
    
    btn.click()
    sleep(2)
    
    bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
    sleep(2)
    bro.execute_script('window.scrollTo(0,-document.body.scrollHeight)')
    page_text = bro.page_source
    with open('./jd.html','w',encoding='utf-8')as p:
        p.write(page_text)
    #关闭浏览器
    bro.quit()
    
    
    

    动作链:

    如果想要触发一系列的连续的行为动作.actionchains

    from selenium import webdriver
    from selenium.webdriver import ActionChains #动作连
    from time import sleep
    
    bro = webdriver.Chrome(executable_path='chromedriver.exe')
    bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
    
    #定位要拖动的标签
    #定位的标签是存在于iframe的子页面中,如果直接使用find做定位,是定位不到的
    # target_ele = bro.find_element_by_id('draggable')
    
    #像定位iframe中子页面中的标签必须进行如下操作
    bro.switch_to.frame('iframeResult')
    target_ele = bro.find_element_by_id('draggable')
    
    #基于动作连实现滑动操作
    action = ActionChains(bro)
    #点击且长按
    action.click_and_hold(target_ele)
    
    for i in range(5):
        #perform()表示立即执行动作连指定好的动作
        action.move_by_offset(17,0).perform()
        sleep(0.5)
    
    action.release()
    
    sleep(4)
    
    bro.quit()
    
  • 相关阅读:
    基础学习笔记之opencv(9):Mat图像扫描
    Android开发历程_7(ListView和ProgressBar控件的学习)
    基础学习笔记之opencv(13):基本绘图
    Qt学习之路_5(Qt TCP的初步使用)
    基础学习笔记之opencv(7):ubuntu下opencv在Qt中的使用
    EM算法学习笔记_1(对EM算法的简单理解)
    Android开发历程_1(从1个activity跳转到另一个activity)
    Matlab成长之路_1(图片,视频,摄像头的读取和显示)
    深入理解JavaScript系列(41):设计模式之模板方法
    深入理解JavaScript系列(44):设计模式之桥接模式
  • 原文地址:https://www.cnblogs.com/Zhao159461/p/11310834.html
Copyright © 2011-2022 走看看