Python3 基于线程、协程并发请求、大并发爬虫方法之一

from gevent import monkey; monkey.patch_all()
import asyncio
import time
import requests
import aiohttp
import sys
import threading
import gevent
import queue
'''
基于协程的各种并发请求示例
在运行程序时 传入指定的命令行参数来开启执行的并发方式
ttp://127.0.0.1:8000/test/ 是本地实现的一个耗时两秒左右的接口
需要安装相应的包:pip install aiohttp gevent
'''


async def fetch(url, q):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            res = await response.text()
            q.put(res)
            return res


async def run_task(num, q):
    # task1 = asyncio.create_task(fetch('http://127.0.0.1:8000/test/',q))
    # task2 = asyncio.create_task(fetch('http://127.0.0.1:8000/test/',q))
    # # await task1
    # # await task2
    # # 上面的语句可以统一改为下面的,只是结果的接收不太方便
    # await asyncio.wait([task1, task2])
    # 再次优化
    tasks = []
    for i in range(num):
        tasks.append(asyncio.create_task(fetch('http://127.0.0.1:8000/test/',q)))
    await asyncio.wait(tasks)


async def run_gather(num, q):
    # task1 = asyncio.create_task(fetch('http://127.0.0.1:8000/test/',q))
    # task2 = asyncio.create_task(fetch('http://127.0.0.1:8000/test/',q))
    # # await asyncio.gather(
    # #     task1,
    # #     task2
    # # )
    # # 上面的语句也可以改为
    # await asyncio.gather(*[task1, task2])
    # 再次优化
    tasks = []
    for i in range(num):
        tasks.append(asyncio.create_task(fetch('http://127.0.0.1:8000/test/',q)))
    await asyncio.gather(*tasks)

async def run_loop_task(num, q, loop):
    tasks = []
    for i in range(num):
        # 通过事件创建task
        task = loop.create_task(fetch('http://127.0.0.1:8000/test/', q))
        tasks.append(task)
    # await asyncio.wait(tasks)
    await asyncio.gather(*tasks)

async def bound_fetch(sem, q):
  async with sem:
    await fetch('http://127.0.0.1:8000/test/', q)
        

if __name__ == '__main__':
    # 使用队列存储返回结果
    q = queue.Queue()
    start_time = time.time()
    if len(sys.argv) > 1 and sys.argv[1] == '1':
        asyncio.run(run_task(2, q))
    if len(sys.argv) > 1 and sys.argv[1] == '2':
        asyncio.run(run_gather(2, q))
    
    # python 官方文档表示开发者应当尽量使用高层级的 asyncio 函数,例如asyncio.run()。它会帮助你简化代码,更多的事情交给函数库去处理。当你需要更细致地控制事件循环行为时才会引用循环对象或调用其方法。
    elif len(sys.argv) > 1 and sys.argv[1] == '3':
        # 获取当前的事件循环
        loop = asyncio.get_event_loop()
        tasks = []
        for i in range(2):
            # 通过事件创建task
            task = loop.create_task(fetch('http://127.0.0.1:8000/test/', q))
            tasks.append(task)
        # 将任务注册到事件循环中,等待它们执行
        loop.run_until_complete(asyncio.wait(tasks)) # asyncio.wait(tasks)表示等待这些任务完成
    elif len(sys.argv) > 1 and sys.argv[1] == '4':
        # 获取当前的事件循环
        loop = asyncio.get_event_loop()
        tasks = []
        for i in range(2):
            # 通过事件创建task
            task = loop.create_task(fetch('http://127.0.0.1:8000/test/', q))
            tasks.append(task)
        # 将任务注册到事件循环中,等待它们执行
        loop.run_until_complete(asyncio.gather(*tasks)) # asyncio.gather(*tasks) 表示使用gather来并发这些任务,它接受一个序列类型的task任务列表,它搜集所有的task对象,然后等待它们返回。
    elif len(sys.argv) > 1 and sys.argv[1] == '5':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(fetch('http://127.0.0.1:8000/test/', q))
    elif len(sys.argv) > 1 and sys.argv[1] == '6':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(run_loop_task(2, q, loop))
    elif len(sys.argv) > 1 and sys.argv[1] == '7':
        loop = asyncio.get_event_loop()
        tasks = []
        # 限制并发量为1000
        sem = asyncio.Semaphore(1000)
        for i in range(2):
            task = loop.create_task(bound_fetch(sem, q))
            tasks.append(task)
        loop.run_until_complete(asyncio.gather(*tasks))
    while q.qsize() != 0:
            print(q.get())
    print('耗时:', time.time() - start_time)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章