自己寫python爬蟲框架(一)

前言

作為一個爬蟲框架,與其他爬蟲差異為:靈活擴(kuò)展以及入門簡單

整體架構(gòu)

image
  • 調(diào)度器
    從請求管理器中取請求,然后調(diào)用下載器進(jìn)行下載,調(diào)度解析器進(jìn)行解析,對解析出的請求再次加入到請求管理器中;解析出的數(shù)據(jù)進(jìn)行緩存,最后寫入到文本中
    調(diào)度器為爬蟲框架的核心,也是爬蟲的入口,其成員變量為下載器、解析器、請求管理器、文件寫入類、日志類,對于不同的爬蟲需求,可以對以上接口有不同的實(shí)現(xiàn)。
    核心方法:
    從請求管理器中取用 request,采用模板設(shè)計(jì)模式,不可被子類重寫
def run(self, request):
    '''
    運(yùn)行爬蟲方法,從requestManager中取出可用的request,然后扔進(jìn)下載器中進(jìn)行下載,通過解析器對下載到的文檔進(jìn)行解析;
    需要傳入一個或者一組request作為初始request進(jìn)行抓取

        @param :: request : Request類型請求
        return : None
    '''
    self.__start_icon()
    self.__logger.info('\tStart crawl...')
    self.__requestManager.add_new_request(request)
    while self.__requestManager.has_new_request():
        request = self.__requestManager.get_new_request()
        print(request.__dict__)
        self.crawl(request)
    self.__logger.info('\tEnd crawl...')
  • 調(diào)度下載器與解析器,可被子類繼承擴(kuò)展,可擴(kuò)展為多線程、多進(jìn)程等
def crawl(self, request):
    '''
    對request進(jìn)行請求進(jìn)行爬取并解析結(jié)果的運(yùn)行單元,子類可對該方法重寫進(jìn)行多線程、多進(jìn)程運(yùn)行或異步抓取與解析;也可裝飾該方法,進(jìn)行多線程、多進(jìn)程抓取。
    下載器對傳入的request進(jìn)行下載,解析器解析下載到的文檔,并將解析出的request扔進(jìn)requestManager中進(jìn)行管理,以進(jìn)行深度爬??;將解析出的data扔進(jìn)writter中,將數(shù)據(jù)存儲到磁盤上

        @param :: request : Request類型請求
        return None
    '''
    try:
        self.__logger.info('\t'+request.url)
        response = self.__downloader.download(request)
        if response.status_code == 200:
            requests, data = self.__parser.parse(response)
            self.__requestManager.add_new_requests(requests)
            self.__writter.write_buffer(data)
        else:
            self.__logger.warn(
                'crawled data is None and response_status is ' + str(response.status_code))
    except Exception as e:
        self.__logger.exception('\tCrawling occurs error\n' + e.__repr__())

  • 下載器
    對傳入的請求進(jìn)行請求下載
    抽象方法:
def download(self, request):
  '''
  對request進(jìn)行請求下載,需要將請求到的response返回

      @param :: request : 請求對象
      return response
  '''
  raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
      self.__class__.__name__, get__function_name()))

  • 解析器
    對傳入的文本進(jìn)行解析
    抽象方法:
def parse(self, response):
    '''
    對response進(jìn)行解析,需要將解析到的requests與data返回

        @param :: response : 響應(yīng)對象
        return requests,data
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))

  • 請求管理器
    管理所有的請求
    抽象方法:
def add_new_requests(self, requests):
    '''
    模板方法,不需要子類重寫
    添加requests序列到requestManager中進(jìn)行管理

        @param :: requests : request對象列表
        return None
    '''
    for request in requests:
        self.add_new_request(request)

def add_new_request(self, request):
    '''
    添加request對象到requestManager中進(jìn)行管理

        @param :: request : request對象
        return None
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))

def has_new_request(self):
    '''
    判斷requestManager中是否還有新的請求,返回布爾類型結(jié)果

        return Bool
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))

def get_new_request(self):
    '''
    從requestManager中取新的請求

        return request
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))
  • 數(shù)據(jù)寫入類
    將數(shù)據(jù)以特定格式寫入到磁盤中
    抽象方法:
def write(self, data):
     '''
     將數(shù)據(jù)data寫入磁盤

         return None
     '''
     raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
         self.__class__.__name__, get__function_name()))

 def write_buffer(self, data):
     '''
     緩存數(shù)據(jù)

         return None
     '''
     raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
         self.__class__.__name__, get__function_name()))

 def flush_buffer(self):
     '''
     刷新緩存數(shù)據(jù)到磁盤上

         return None
     '''
     raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
         self.__class__.__name__, get__function_name()))

  • 日志
    記錄爬蟲運(yùn)行
    抽象方法:
def debug(self, message):
    '''
    debug級別日志
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))

def info(self, message):
    '''
    info級別日志
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))

def warn(self, message):
    '''
    warn級別日志
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))

def exception(self, message):
    '''
    exception級別日志
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))

def error(self, message):
    '''
    error級別日志
    '''
    raise NotImplementedError("未實(shí)現(xiàn)的父類方法: %s.%s" % (
        self.__class__.__name__, get__function_name()))

設(shè)計(jì)思想

在 Abstract Factory 模式為將’抽象零件’組裝為’抽象產(chǎn)品’,因此對于下載器、解析器、請求管理器、數(shù)據(jù)寫入類、日志類僅是限定其抽象接口,在調(diào)度器中分別調(diào)用其每個零件的具體實(shí)現(xiàn)。

調(diào)度器則參照了模板方法設(shè)計(jì)模式,將開始爬取方法(startcrawl)固定,對于調(diào)度器的多線程多進(jìn)程擴(kuò)展可繼承該調(diào)度器,并重寫調(diào)度爬取方法(crawl),提供多線程多進(jìn)程爬取方法

在爬蟲整體框架設(shè)計(jì)中,主體未調(diào)度器、下載器、解析器、請求管理器、數(shù)據(jù)寫入類、日志類,滿足了基本爬蟲的需求,在實(shí)際中爬蟲進(jìn)行爬取數(shù)據(jù)時,需要設(shè)置網(wǎng)絡(luò)代理、設(shè)置不同的 cookie 身份、以及分布式爬取調(diào)度任務(wù)等,可對以上基本組件運(yùn)用裝飾者模式進(jìn)行裝飾,已達(dá)到擴(kuò)展功能的需求

小白文章,還請各位多多請教!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容