• 异步爬虫(五)----aiohttp


    开始学着利用协程做异步爬虫,直接上代码:

    先自己弄一个服务器,别问我代码什么意思,我自己都不知道,视频copy过来的。。

    from flask import Flask
    import time
    app  = Flask(__name__)
    @app.route('/tom')
    def index_tom():
        time.sleep(1)
        return 'hello tom'
    if __name__ == '__main__':
        app.run(threaded = True)

    访问http://127.0.0.1:5000/tom,返回一个‘hello tom’,中间等待1s。

    import requests
    print(requests.get(url='http://127.0.0.1:5000/tom').text)

    输出:

    hello tom

    来一个利用协程爬虫的代码:

    import aiohttp
    import asyncio
    import time
    import requests
    urls = ['http://127.0.0.1:5000/tom',
           'http://127.0.0.1:5000/tom',
           'http://127.0.0.1:5000/tom'
           ]
    async def request(url):
        responses = requests.get(url=url)
        print(responses.text)
    
    async def main():
        tasks = []
        for u in urls:
            task = asyncio.create_task(request(u))
            tasks.append(task)
    if __name__ == '__main__':
           start_time = time.time()
           asyncio.run(main())
           end_time = time.time()
           print(end_time-start_time)

    输出 :

    hello tom
    hello tom
    hello tom
    3.0251729488372803

    很明显,利用协程做了,但是没有实现并发,时间还是3s多一点。

    问题在哪呢?在requests模块是不支持异步的,异步要用到aiohttp。同样cmd,pip install aiohttp下载该模块。

    import aiohttp
    import asyncio
    import time
    import requests
    urls = ['http://127.0.0.1:5000/tom',
           'http://127.0.0.1:5000/tom',
           'http://127.0.0.1:5000/tom'
           ]
    async def request(url):
           async with aiohttp.ClientSession() as session:
                  async with  session.get(url = url) as response:
                         ret = await response.text()
           return ret
    async def main():
           tasks = []
           for u in urls:
                  task = asyncio.create_task(request(u))
                  tasks.append(task)
           done,pending = await asyncio.wait(tasks)
           for i in done:
                  print(i.result())
    if __name__ == '__main__':
           start_time = time.time()
           asyncio.run(main())
           end_time = time.time()
           print(end_time-start_time)

    输出:

    hello tom
    hello tom
    hello tom
    1.0150578022003174

    下边解释下代码:

    async def request(url):
    async with aiohttp.ClientSession() as session:
    async with session.get(url = url) as response:
    ret = await response.text()
    return ret

    这个协程函数的功能是创建协程对象,访问一个形参url,返回的是响应结果。

    async with aiohttp.ClientSession() as session:

    async with 固定语法,用于异步协程,with…as……是一个上下文切换器,具体的可以百度相关资料。aiohttp.ClientSession()  创建一个session对象,调用session的方法get可以访问url。如果是get请求,就是调用get方法,如果是post请求,就调用post方法,和之前的requests模块一样。get、post方法中参数可以有headers、params/data和requests模块一样,代理用proxy = ‘http:// + IP:端口号’,传入的是字符串,和requests中proxy={},传入字典不一样。

    获取响应结果与requests模块略有不同:

     

    aiohttp

    requests

    返回字符串

    res.text()

    res.text

    返回二进制数据(图像、音频)

    Res.read()

    res.content

    Json

    res.json()
    res.json()

    这个函数可以分为两步理解,第一步是申请访问,第二步是返回,返回的过程是一个IO过程,是比较耗时的,我们利用协程做爬虫的目的是利用这个返回的时间,对其他的url申请访问。所以返回时一个阻塞操作,应该用await关键字挂起他,这时候程序会切换到其他协程对象执行。

    async def main():
    tasks = []
    for u in urls:
    task = asyncio.create_task(request(u))
    tasks.append(task)
    done,pending = await asyncio.wait(tasks)
    for i in done:
    print(i.result())

    这个函数的功能是为协程对象创建task对象,并添加到循环事件中,并执行这个循环事件。tasks = []tasks是一个列表,每一个元素是创建的task对象。asyncio.create_task()前边提到过,是创建task对象。done,pending = await asyncio.wait(tasks),这个就是执行这个循环事件,返回的结果以集合形式在done中,所以后边的for循环就是获取每个task的结果。也可以使用回调机制,具体可参考 :  https://www.jianshu.com/p/b5e347b3a17c

    所以我们可以在for循环这里中持久化存储,也就是将返回结果保存在本地。这个过程是一个CPU繁忙型任务,所以用协程提高效率的意义不大,直接用串行就可以。

    if __name__ == '__main__':
    start_time = time.time()
    asyncio.run(main())
    end_time = time.time()
    print(end_time-start_time)

    这个主函数就不用说了,关键是asyncio.run(main())运行mian()协程函数,前边的main()函数定义的也是协程函数,记住就可以。

    有一个问题容易被忽略,我们接受到的响应数据在没有持久化存储之前去哪了?答案是在内存中,所以说如果下载的是视频、音频这些大数据时,内存会很吃劲,尤其是并发数据量非常大时,那怎么办呢?可以根据实际电脑配置限制并发的协程数量。

    例子:

    import aiohttp
    import asyncio
    import time
    import requests
    urls = ['http://127.0.0.1:5000/tom',
           'http://127.0.0.1:5000/tom',
           'http://127.0.0.1:5000/tom',
           'http://127.0.0.1:5000/tom'
           ]
    async def request(url,semaphore):
           async with semaphore:
                  async with aiohttp.ClientSession() as session:
                         async with  session.get(url = url) as response:
                                ret = await response.text()
           return ret
    async def main():
           semaphore = asyncio.Semaphore(2) 
           tasks = []
           for u in urls:
                  task = asyncio.create_task(request(u,semaphore))
                  tasks.append(task)
           done,pending = await asyncio.wait(tasks)
           for i in done:
                  print(i.result())
    if __name__ == '__main__':
           start_time = time.time()
           asyncio.run(main())
           end_time = time.time()
           print(end_time-start_time)

    添加的部分就是红色字体部分,不需要管啥意思,一个固定用法而已,参考:  https://www.cnblogs.com/shenh/p/9090586.html

    输出:

    hello tom
    hello tom
    hello tom
    hello tom
    2.061117649078369

    结果很明显了,4个协程对象,2个2个并发的。

    分享一个写的非常好,非常全的博客:  https://www.cnblogs.com/ssyfj/p/9222342.html

    this's all   下一步,做一个爬取糗百或者百思不得姐的视频,那种大批量的。

    end_time
  • 相关阅读:
    ext之Windows
    多个DIV显示/隐藏
    asp.net 得到上一页地址
    人们的文化和素质
    gridview 删除问题
    RSS of the Day: 新闻类Feeds
    每日英语:Taiwan Tech Firms Bring Jobs Home Quietly
    每日英语:Freedom and America's gun culture
    matlab学习:图像频域分析之Gabor滤波
    每日英语:The End Of The World Is (Not) Nigh
  • 原文地址:https://www.cnblogs.com/lgwdx/p/14310617.html
Copyright © 2020-2023  润新知