異步pyppeteer:併發運行多個瀏覽器並收集結果

網上代碼一大抄,居然網上講pyppeteer異步的一大推,但運行起來都是await,並沒有講如何同時併發運行十幾二個pyppeteer頁面,那有個卵用呀,還不如開個多進程呢。

話不多說,上代碼。

爬取目標是額,就夜幕吧,知名的爬蟲論壇,沒錯,就肝它吧。

頁面有44個,先用pyppeteer用await的方法一個一個爬,實際和同步用時一樣,大概76秒爬完44個頁面。

而用asyncio併發去跑,不控制信號量。就是讓它跑滿,實際用時12秒,快樂7倍有多。

# -*- coding: utf-8 -*-
# @Time : 2021/4/13 18:44
# @File : crawler.py

import asyncio
from parsel import Selector
import pyppeteer
from read_config import *
from pipeline.mongo import MongoDBCls
from datetime import datetime

class BaseCrawler():

    def __init__(self):
        self.db_cls = MongoDBCls('db_technology','nightteam') # 如果不存數據,可以註釋掉
    
    async def browser(self):
        browser = await pyppeteer.launch(
            {'headless': HEADLESS,
             'userDataDir': UserDataDir,
             # 'ignoreDefaultArgs': ignoreDefaultArgs,  # 忽略內置參數
             'args': ['--start-maximized'],  # 最大化
             }
        )
        return browser

    

    async def crawl(self, **kwargs):

        browser = await self.browser()
        tasks=[]
        for i in range(1, 45):
            tasks.append(self.get_single_page(browser,i,**kwargs))
        result =await asyncio.gather(*tasks)
        print(len(result))
        await browser.close()
        return result

    async def start(self):
        result = await self.crawl(time=1000*600)
        for item in result:
            self.db_cls.add(item)


    async def get_single_page(self,browser,i,**kwargs):
        page = await browser.newPage()
        url = self.base_url.format(i)
        # print(url)
        await page.goto(url, **kwargs)
        content = await page.content()
        return self.parse(content)


    def parse(self, content):
        response = Selector(text=content)
        item_list = response.xpath('//div[@class="card-body card-p0"]/ul/li')
        result_list =[]
        for item in item_list:
            item_dict = dict()


            detail_url = item.xpath(
                './/div[@class="media-body"]/div[@class="subject break-all"]/a/@href').extract_first()
            title = item.xpath(
                './/div[@class="media-body"]/div[@class="subject break-all"]/a/text()').extract_first()

            detail_url = self.url_completion(detail_url)
            username = item.xpath(
                './/div[@class="d-flex justify-content-between small mt-2"]/div/span[@class="haya-post-info-username "]/span[1]/text()').extract_first()
            post_time = item.xpath(
                './/div[@class="d-flex justify-content-between small mt-2"]/div/span[@class="haya-post-info-username "]/span[2]/text()').extract_first()
            view_count = item.xpath(
                './/div[@class="d-flex justify-content-between small mt-2"]/div[@class="text-muted small"]/span[@class="ml-2"]/text()').extract_first()

            item_dict['detail_url'] = detail_url
            item_dict['title'] = title
            item_dict['username'] = username
            item_dict['post_time'] = post_time
            item_dict['crawltime'] = datetime.now()
            item_dict['view_count'] = int(view_count) if view_count else 0
            # print(item_dict)
            result_list.append(item_dict)

        return result_list

    @property
    def base_url(self):
        base_url = 'https://bbs.nightteam.cn/index-{}.htm'
        return base_url

運行代碼:

import asyncio
import time
def main():
    start = time.time()
    spider = BaseCrawler()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(spider.start())
    print(f'time used {time.time()-start:.2} s')

if __name__ == '__main__':
    main()

上面就是完整的代碼,

這裏爲了展示效果,把獲取到的字段打印出來即可。

 

 這裏重點是:

        browser = await self.browser()
        tasks=[]
        for i in range(1, 45):
            tasks.append(self.get_single_page(browser,i,**kwargs))
        result =await asyncio.gather(*tasks)
        print(len(result))
        await browser.close()
        return result

如果把page傳進去作爲參數,得到的結果是全部都是一樣的,是最後一頁。

所以只能把browser傳進去,讓函數執行打開頁面,而不能先把頁面獲取,再分配給函數。

tasks.append(self.get_single_page(browser,i,**kwargs))

這一句是把所有的協程放在一個列表裏面,這個是代碼的關鍵。

這時抓取動作還沒有執行,等待一下條 await 阻塞語句出現是,這個時候,事件循環就開始分配任務,把這個44個頁面的任務分配出去,並同時執行,最後等待最後一個頁面執行完畢,所用耗時由最慢的一個頁面決定。

最後把所有解析數據收集到返回給result,這是就可以拿數據取存儲或者輸出啦。

轉自:https://zhuanlan.zhihu.com/p/364646776

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