python异步爬虫(协程asyncio和aiohttp)

#知识复盘

要实现异步机制的爬虫,离不开协程!

一、协程的基本概念:

协程(单线程),英文叫coroutine,又称微线程、纤程,是一种运行在用户状态的轻量级线程。它拥有自己的寄存器上下文和栈,在调度切换时,将寄存器上下文和栈保存到其他地方,等切回来时,再恢复到先前保存的寄存器上下文和栈。因此,协程能保留上一次调用时的状态,所有局部状态的一个特定组合,每次过程重入,就相当于进入上一次调用的状态。

1. event_loop:事件循环,相当于一个无限循环,我们可以把一个函数注册到这个事件循环上,当满足发生条件的时候,就调用对应的处理方法。

2. coroutine:协程,指代协程对象类型,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用async关键字来定义一个方法,这个方法再调用时不会被立即执行,而是返回一个协程对象

3. task:任务,这是协程对象的进一步封装,包含协程的各个状态

4. future:代表将来执行或者没有执行的任务的结果,实际上和task没有本质区别

二、准备工作

安装asyncio和aiphttp,相关命令如下:

pip install asyncio
pip install aiphttp

三、简单运用asyncio和aiohttp

假如爬取一个网站,同时执行10个请求,每个请求6s,若是按照顺次执行的方式,则花费总时间:大约60s左右。若是按照异步爬取,则只需大约6的时间,具体代码如下:

# 协程爬取
import asyncio
import aiohttp
import time

start = time.time()


# aiohttp的client封装
async def get(url):
    session = aiohttp.ClientSession()
    response = await session.get(url)
    await response.text()
    await session.close()
    return response


# 网络请求方法
async def request():
    url = "https://www.httpbin.org/delay/5"
    print("等待:", url)
    response = await get(url)
    print("得到返回结果:", response)


tasks = [asyncio.ensure_future(request()) for _ in range(10)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

end = time.time()
print("总时间:", end - start)
结果演示

四、aiohttp的其他使用

aiohttp是一个基于asyncio的异步http网络模块,它既提供服务端,又提供客户端。其中,我们服务端可以搭建一个支持异步处理的服务器,这个服务器就是用来处理请求并返回响应的,类似于Django、Flask、Tornado等一些web服务器。而客户端可以用来发起请求,类似于使用requests发起一个Http请求然后获得响应,但requests发起的是同步的网络请求,aiphttp则是异步的。

1. 基本实例:

import asyncio
import aiohttp


async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text(), response.status


async def main():
    async with aiohttp.ClientSession() as session:
        html, status = await fetch(session, "https://www.baidu.com")
        print(f"html:{html[:100]}")
        print(f"状态:{status}")


if __name__ == "__main__":
    # 兼容python3.7以下的版本
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    # python版本3.7及以上
    # asyncio.run(main())
注意事项:aiphttp的写法和requests的写法区别

1. 首先是导入的库,除了导入aiohttp这个库,还必须引入asyncio库,因为要实现异步爬取,需要启动线程,而协程则需要借助于asyncio里面的事件循环才能执行。

2. 异步爬取方法的定义和requests的定义不同,每个异步方法的前面都要统一加async来修饰

3. with as 语句前面同样需要加async来修饰。在python中,with as 语句用于声明一个上下文管理器,能够帮我们自动分配和释放资源。而在异步方法中,with as 前面加上async代表声明一个支持异步的上下文管理器

4. 对于一些返回协程对象的操作,前面加await来修饰

5. 在python3.7及以后的版本中,我们可以使用asyncio.run(main())代替最后的启动操作,不需要显示声明事件循环,run方法内部会自动启动一个事件循环。

2. URL参数设置

import asyncio
import aiohttp


async def main():
    param = {'name': 'germey', 'age': 23}
    async with aiohttp.ClientSession() as session:
        async with session.get("https://www.baidu.com", params=param) as response:
            print(f"html:{response.text}")
            print(f"状态:{response.status}")


if __name__ == "__main__":
    # 兼容python3.7以下的版本
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    # python版本3.7及以上
    # asyncio.run(main())

3. 其他请求类型

aiohttp还支持其他请求类型,如POST,PUT,DELETE等

post请求代码如下:

import asyncio
import aiohttp


async def main():
    data = {'name': 'germey', 'age': 23}
    async with aiohttp.ClientSession() as session:
        async with session.post("https://www.httpbin.org/post", data=data) as response:
            print(await response.text())


if __name__ == "__main__":
    # 兼容python3.7以下的版本
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    # python版本3.7及以上
    # asyncio.run(main())

4. 超时设置:

我们可以借助ClientTimeout对象设置超时,例如设置1s超时:

import asyncio
import aiohttp


async def main():
    timeout = aiohttp.ClientTimeout(total=1)  # 设置超时1秒
    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with session.post("https://www.baidu.com") as response:
            print(f"html:{response.text}")
            print(f"状态:{response.status}")


if __name__ == "__main__":
    # 兼容python3.7以下的版本
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    # python版本3.7及以上
    # asyncio.run(main())

5. 并发限制

由于aiohttp可以支持高并发量,但面对如此高的并发量,目标网站可能无法短时间内进行响应,甚者会导致网站挂掉,所以需要主动控制以下并发量。一般情况下,我们使用asyncio的Semaphore来控制并发量,如下:

import asyncio
import aiohttp

MAX_NUM = 5  # 最大并发量
URL = "https://www.baidu.com"

semaphore = asyncio.Semaphore(MAX_NUM)
session = None


async def scrape_api():
    async with semaphore:
        print("地址:", URL)
        async with session.get(URL) as response:
            await asyncio.sleep(1)
            return await response.text()

async def main():
    global session
    session = aiohttp.ClientSession()
    scrape_index_tasks = [asyncio.ensure_future(scrape_api()) for _ in range(1000)]
    await asyncio.gather(*scrape_index_tasks)


if __name__ == "__main__":
    # 兼容python3.7以下的版本
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    # python版本3.7及以上
    # asyncio.run(main())

以上就是python异步爬虫的基本使用,其他功能可在这基础上进行拓展!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值