單線程+異步協程的簡單爬蟲模型

event_loop:事件循環,至關於一個無限循環(不清楚循環多少次),咱們能夠把一些特殊函數註冊(放置)到這個事件循環上,當知足某些條件的時候,函數就會被循環執行。程序是按照設定的順序從頭執行到尾,運行的次數也是徹底按照設定。當在編寫異步程序時,必然其中有部分程序的運行耗時是比較久的,須要先讓出當前程序的控制權,讓其在背後(掛起)運行,讓另外一部分的程序先運行起來。當背後運行的程序完成後,也須要及時通知主程序已經完成任務能夠進行下一步操做,但這個過程所需的時間是不肯定的,須要主程序不斷的監聽狀態,一旦收到了任務完成的消息,就開始進行下一步。loop就是這個持續不斷的監視器。

coroutine:中文翻譯叫協程,在 Python 中常指代爲協程對象類型,咱們能夠將協程對象註冊到事件循環中,
它會被事件循環調用。咱們可使用 async 關鍵字來定義一個方法,這個方法在調用時不會當即被執行,
而是返回一個協程對象。

task:任務,它是對協程對象的進一步封裝,包含了任務的各個狀態。

future:表明未來執行或尚未執行的任務,實際上和 task 沒有本質區別。

另外咱們還須要瞭解 async/await 關鍵字,它是從 Python 3.6 纔出現的,專門用於定義協程。其中,async 定義一個協程,await 用來掛起阻塞方法的執行。

import asyncio
async def request(url):
    print('正在請求:',url)
    print('下載成功:',url)

c = request('www.baidu.com')

# 第一步:實例化一個事件循環對象
loop = asyncio.get_event_loop()
# 第二步:建立一個任務對象,將協程對象封裝到了該對象中
# task = loop.create_task(c)

# 另外一種形式實例化任務對象的方法   
task = asyncio.ensure_future(c)

#將協程對象註冊到事件循環對象中,而且咱們須要啓動事件循環對象
loop.run_until_complete(task)
# 打印task能夠看到任務對象狀態
print(task)

綁定回調函數,在爬蟲中必須用回調函數,由於在數據爬取下來後,用回調函數能夠進行數據解析面試

import asyncio

async def request(url):
    print('正在請求:',url)
    print('下載成功:',url)
    return url

#回調函數必須有一個參數:task
#task.result():任務對象中封裝的協程對象對應的特殊函數內部的返回值
def callbak(task):
    print('this is callback!')
    print(task.result())

c = request('www.baidu.com')

#給任務對象綁定一個回調函數
#  建立任務對象
task = asyncio.ensure_future(c)
# 綁定回調函數
task.add_done_callback(callbak)

# 註冊到時間循環中
loop = asyncio.get_event_loop()

loop.run_until_complete(task)

多任務異步協程網絡

from time import sleep
import asyncio
import time
urls = ['www.baidu.com','www.sogou.com','www.goubanjia.com']
start = time.time()
async def request(url):
    print('正在請求:',url)
    #在多任務異步協程實現中,不能夠出現不支持異步的相關代碼。
    # sleep(2)
    await asyncio.sleep(2)
    print('下載成功:',url)

loop = asyncio.get_event_loop()
#任務列表:放置多個任務對象
tasks = []
for url in urls:
    c = request(url)
    task = asyncio.ensure_future(c)
    tasks.append(task)

loop.run_until_complete(asyncio.wait(tasks))

print(time.time()-start)

單線程+多任務異步app

# 面試問題,如何提高爬取效率
# 使用方向:數據量大,數據佔內存大
#aiohttp:支持異步的一個基於網絡請求的模塊
# 和requests模塊功能應用都同樣,區別就是支持異步
# pip install aiohttp

import requests
import asyncio
import time
import aiohttp
#單線程+多任務異步協程
urls = [
    'http://127.0.0.1:5000/jay',
    'http://127.0.0.1:5000/bobo',
    'http://127.0.0.1:5000/tom'
]
# 異步效果,遇到阻塞,掛起阻塞,執行別的任務
#代理操做:
#async with await s.get(url,proxy="http://ip:port") as response:
async def get_pageText(url):
    # 請求對象,with不關閉資源,
   async with aiohttp.ClientSession() as s:
      async with await s.get(url) as response:
          # 拿響應數據有可能阻塞因此須要await, 這裏是text方法,request是text方法
           page_text = await response.text()
            # 藉助於回調函數進行響應數據的解析操做
           return page_text
#封裝回調函數用於數據解析
def parse(task):
    #1.獲取響應數據
    page_text = task.result()
    print(page_text+',即將進行數據解析!!!')
    #解析操做寫在該位置

start = time.time()
tasks = []
for url in urls:
    c = get_pageText(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(time.time()-start)
相關文章
相關標籤/搜索