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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章