zoukankan      html  css  js  c++  java
  • 网络爬虫之异步协程

    引言:异步协程本质就是一条线程中多个任务遇到阻塞操作就自动挂起并继续执行下一个任务,等待阻塞操作完成之后再回去执行完剩余的操作。涉及的模块:aiohttp,asyncio。

    协程的作用:

        减轻了操作系统的负担

        用来规避IO操作,就达到了我们将一条线程中的io操作降到最低的目的

        一条线程如果开了多个协程,那么给操作系统的印象是线程很忙,这样能多争取一些时间片时间来被CPU执行,程序的效率就提高了

    下面来看一个例子:

    import aiohttp
    import asyncio
    import time
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
    }
    
    urls = ['https://www.baidu.com/',
            'https://www.hao123.com/‘,
            'https://www.sohu.com/‘]
    
    async def get_response(url):#声明一个协程函数
        conn = aiohttp.TCPConnector(verify_ssl=False) #跳过证书认证
        async with aiohttp.ClientSession(connector=conn) as sess:
            async with await sess.get(url=url,headers=headers) as response:
                # await asyncio.sleep(2)
                page_text = await response.text()
                return page_text
    
    
    def parse(t):#回调函数
        page_text = t.result()
        print(page_text)
    
    start_time = time.time()
    tasks = [] #创建一个任务列表
    
    for url in urls:
        c = get_response(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(f'总耗时:{time.time()-start_time}')

    这里async的作用是声明一个协程函数,为什么用到with呢?with可以自动关闭。协程函数内的阻塞操作前面一点需要加上await表示阻塞之后自动挂起让出cup去执行另一个任务,协程函数内用到的方法必须是基于异步的而python原生的requests模块不支持所以这里使用了aiohttp一个基于异步的请求模块。

    下面来看一个实例:

    import aiohttp
    import asyncio
    from lxml import etree
    import time
    
    urls_list = [f'https://www.qiushibaike.com/text/page/{i}/' for i in range(1,101)]
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
    }
    
    async def get_page(url):
        conn = aiohttp.TCPConnector(verify_ssl=False)
        async with aiohttp.ClientSession(connector=conn) as session:
            async with session.get(url=url,headers=headers) as response:
                page = await response.text()
                await asyncio.sleep(2)
                return page
                #
    
    def parms(t): #回调函数
        page = t.result()
        tree = etree.HTML(page)
        div_list = tree.xpath('//*[@id="content"]/div/div[2]/div')
        for div in div_list:
            content = div.xpath('./a[1]/div[1]/span//text()')[0]
            print(content)
    
    
    takes = []
    
    start_time = time.time()
    
    for url in urls_list:
        c = get_page(url)
        take = asyncio.ensure_future(c)
        take.add_done_callback(parms) #绑定回调函数
        takes.append(take)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(takes))
    
    print(f'总耗时:{time.time()-start_time}')

    目标网站是糗事百科,首先用列表推倒式构建前100页的url列表,然后声明一个协程函数。之后创建事件循环并提交任务。

    结果如下:

    可见速度还是非常快的,为什么这里不使用多线程的方式?因为GIL锁导致了在CPython解释器下只有一个线程真正的被cpu调度,使用多线程就是让每一个线程轮流执行一个时间片然后让出cpu给下一个线程并没有规避掉IO。而协程是遇到IO就自动挂起然后让出cpu,之后等待阻塞操作完成之后再继续回去执行完剩余操作。这样大大规避了IO操作,节省了时间。

  • 相关阅读:
    easyui学习笔记3—在展开行内的增删改操作
    easyui学习笔记2—在行内进行表格的增删改操作
    4星|《贫穷的本质》:小额贷款对穷人帮助有限,助推政策也许是更好的
    科技类好书12本
    咨询公司等专业服务公司的权力结构:3星|《哈佛商业评论》第4期
    投资、投机、经济周期相关20本书,其中好书8本半
    3星|《进化:中国互联网生存法则》:点评十年来互联网公开大事
    黑洞有毛 or 黑洞无毛:4星|《环球科学》2019年03月号
    3星|《给产品经理讲技术》:APP开发技术介绍,没有技术背景的话恐怕只能看懂书中的比喻和结论
    2星|《为什么我们总是在逃避》:尝试用精神分析理论解释常见负面情绪
  • 原文地址:https://www.cnblogs.com/Pynu/p/14683030.html
Copyright © 2011-2022 走看看