python異步爬蟲的實現過程

在日常爬蟲中我們會涉及到同步與異步問題,一般異步編程可以大幅度的提高系統的吞吐量,提高單位時間內發出的請求數目。之前的文章分享了些同步的知識,就是對aurl發起請求,等待響應。然後再訪問burl,等待響應。。。

大量的時間消耗在等待上,如果能近似的同時對多個網址發起請求,等待響應,速度回快很多倍。其實所謂的同時也是有先後順序的,所以叫異步。

異步爬蟲的方式有以下2種

1、多線程,多進程(不建議):

好處:可以爲相關阻塞的操作單獨開啓線程,阻塞操作就可以異步執行。弊端:無法無限制的開啓多線程或者多進程。

2、線程池、進程池(適當的使用):好處:可以降低系統對進程或者線程創建和銷燬的一個頻率,從而很好的降低系統的開銷。弊端:池中線程或進程的數量是有上限。

接下來我們通過aiohttp異步爬蟲來爬取一個書籍網站的數據, https://spa5.scrape.center/,通過簡單的網站分析,反爬機制不是很嚴,爲了爬取順利這裏添加了代理IP,由於這個網站的數據量多一些,所以選擇用異步方式來爬取,代碼實例如下:

# 導入相關庫
import asyncio
import aiohttp
from aiohttp_socks import ProxyConnector
from bs4 import BeautifulSoup

# 定義目標網站和代理服務器的參數
url = "https://spa5.scrape.center/"
proxy = "socks5://16yun:[email protected]:11111"

# 定義異步函數來發送GET請求,並使用代理服務器來連接目標網站
async def fetch(session, url):
    try:
        async with session.get(url) as response:
            # 檢查響應狀態碼是否爲200,否則拋出異常
            if response.status != 200:
                raise Exception(f"Bad status code: {response.status}")
            # 返回響應內容的文本格式
            return await response.text()
    except Exception as e:
        # 打印異常信息,並返回None
        print(e)
        return None

# 定義異步函數來處理響應結果,並解析HTML內容
async def parse(html):
    # 如果響應結果不爲空,則進行解析操作
    if html is not None:
        # 使用bs4庫來創建BeautifulSoup對象,並指定解析器爲html.parser
        soup = BeautifulSoup(html, "html.parser")
        # 提取網頁中的標題標籤,並打印其文本內容
        title = soup.find("title")
        print(title.text)
    else:
        # 否則打印None表示無效結果
        print(None)

# 定義異步函數來統計成功次數,並打印結果
async def count(results):
    # 初始化成功次數爲0
    success = 0
    # 遍歷所有的結果,如果不爲空,則增加成功次數,否則跳過
    for result in results:
        if result is not None:
            success += 1
    # 打印總共的請求數和成功次數    
    print(f"Total requests: {len(results)}")
    print(f"Success requests: {success}")

# 定義異步主函數來創建並運行多個協程任務,並控制併發數量和超時時間等參數    
async def main():
    # 創建一個aiohttp_socks.ProxyConnector對象,用來設置代理服務器的參數    
    connector = ProxyConnector.from_url(proxy)
    # 創建一個aiohttp.ClientSession對象,用來發送HTTP請求,並傳入connector參數    
    async with aiohttp.ClientSession(connector=connector) as session:
        # 創建一個空列表,用來存儲所有的協程任務        
        tasks = []
        # 循環10000次,每次創建一個fetch函數的協程任務,並添加到列表中        
        for i in range(10000):
            task = asyncio.create_task(fetch(session, url))
            tasks.append(task)
        
        # 使用asyncio.gather函數來收集並執行所有的協程任務,並返回一個包含所有結果的列表        
        results = await asyncio.gather(*tasks)
        
        # 創建一個空列表,用來存儲所有的解析任務        
        parse_tasks = []
        
         for result in results:
             parse_task = asyncio.create_task(parse(result))
             parse_tasks.append(parse_task)
             
         await asyncio.gather(*parse_tasks)   
         
         await count(results)

# 在程序入口處調用異步主函數,並啓動事件循環         
if __name__ == "__main__":
     asyncio.run(main())

 

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