1. 程式人生 > >多爬蟲實現之三 -- 多爬蟲檔案

多爬蟲實現之三 -- 多爬蟲檔案

目標

  • 優化現有的爬蟲結構,實現同時開始執行多個爬蟲

1 為什麼需要優化現有的爬蟲結構

當爬蟲比較少的時候,我們的專案結構相對合理,但是當要抓取的網站比較多的時候,可以借鑑scrapy的方法,把不同網站的爬蟲分別在不同的py檔案中編寫,之後放在一個目錄下;同時,我們很多時候還希望能夠有同時啟動專案中的所有的爬蟲

2 將多個爬蟲類分離為多個爬蟲檔案爬蟲檔案

為了解耦合,應將每個站點的爬蟲寫為單獨一個py檔案,因此更改一下放置爬蟲的模組,結構如下:

- 專案資料夾
  -- main.py
  -- spiders
     -- __init__.py
     -- baidu.py
     -- douban.py
  -- settings.py

其中baidu.pydouban.py分別是抓取百度和豆瓣的爬蟲檔案

  • baidu.py:
# project_dir/spiders/baidu.py
from scrapy_plus.core.spider import Spider

# 繼承框架的爬蟲基類
class BaiduSpider(Spider):

    start_urls = ['http://www.baidu.com']    # 設定初始請求url
  • douban.py: 抓取豆瓣電影top250的列表頁資訊
# project_dir/spiders/douban.py
from scrapy_plus.core.spider import Spider
from scrapy_plus.http.request import Request
from scrapy_plus.item import Item


class DoubanSpider(Spider):

    start_urls = []  # 重寫start_requests方法後,這個屬性就沒有設定的必要了

    def start_requests(self):
        # 重寫start_requests方法,返回多個請求
        base_url = 'http://movie.douban.com/top250?start='
        for i in range(0, 250, 25):    # 逐個返回第1-10頁的請求屬相
            url = base_url + str(i)
            yield Request(url)

    def parse(self, response):
        '''解析豆瓣電影top250列表頁'''
        title_list = []    # 儲存所有的
        for li in response.xpath("//ol[@class='grid_view']/li"):    # 遍歷每一個li標籤
            # title = li.xpath(".//span[@class='title'][1]/text()")    # 提取該li標下的 標題
            # title_list.append(title[0])
            detail_url = li.xpath(".//div[@class='info']/div[@class='hd']/a/@href")[0]
            yield Request(detail_url, parse="parse_detail")    # 發起詳情頁的請求,並指定解析函式是parse_detail方法
        # yield Item(title_list)    # 返回標題

    def parse_detail(self, response):
        '''解析詳情頁'''
        print('詳情頁url:', response.url)    # 列印一下響應的url
        return []    # 由於必須返回一個容器,這裡返回一個空列表
  • 對main.py進行相應修改,測試新增的douban爬蟲

      from scrapy_plus.core.engine import Engine    # 匯入引擎
    
      from spiders.baidu import BaiduSpider
      from spiders.douban import DoubanSpider
    
      if __name__ == '__main__':
          # spider = BaiduSpider()    # 例項化爬蟲物件
          douban_spider = DoubanSpider()    # 例項化爬蟲物件
          engine = Engine(douban_spider)    # 傳入爬蟲物件
          engine.start()    # 啟動引擎
    

3 同時執行多個不同的爬蟲

如把豆瓣爬蟲和百度爬蟲一起啟動並執行

傳入形式:並用字典的形式傳入多個爬蟲:

  • main.py
# project_dir/main.py
from scrapy_plus.core.engine import Engine    # 匯入引擎

from spiders.baidu import BaiduSpider
from spiders.douban import DoubanSpider

if __name__ == '__main__':
    baidu_spider = BaiduSpider()    # 例項化爬蟲物件
    douban_spider = DoubanSpider()    # 例項化爬蟲物件
    spiders = {'baidu':baidu_spider, 'douban':douban_spider}
    engine = Engine(spiders)    # 傳入爬蟲物件
    engine.start()    # 啟動引擎

在引擎中用到爬蟲物件的地方都要做相應的修改

  • engine.py
'''引擎
a. 對外提供整個的程式的入口
b. 依次呼叫其他元件對外提供的介面,實現整個框架的運作(驅動)
'''
......
class Engine(object):

    def __init__(self, spiders):    # 接收外部傳入的多個爬蟲物件
        self.spiders = spiders    # 爬蟲物件

        ......

    ......

    def _start_requests(self):
        '''向排程器新增初始請求'''
        # 1. 爬蟲模組發出初始請求
        for spider_name, spider in self.spiders.items():
            for start_request in spider.start_requests():
                # 2. 把初始請求新增給排程器
                # 利用爬蟲中介軟體預處理請求物件
                start_request = self.spider_mid.process_request(start_request)
                start_request.spider_name = spider_name    #為請求物件繫結它所屬的爬蟲的名稱
                self.scheduler.add_request(start_request)

    def _execute_request_response_item(self):
        '''根據請求、發起請求獲取響應、解析響應、處理響應結果'''

        ......

        spider = self.spiders[request.spider_name]  # 根據請求的spider_name屬性,獲取對應的爬蟲物件

        # 5. 利用爬蟲的解析響應的方法,處理響應,得到結果
        parse = getattr(spider, request.parse)    # 獲取對應的解析函式
        results = parse(response)    # parse函式的返回值是一個容器,如列表或者生成器物件
        for result in results:
           # 6. 判斷結果物件
           # 6.1 如果是請求物件,那麼就再交給排程器
           if isinstance(result, Request):
               # 利用爬蟲中介軟體預處理請求物件
               result = self.spider_mid.process_request(result)
               result.spider_name = request.spider_name  # 為請求物件繫結它所屬的爬蟲的名稱
               self.scheduler.add_request(result)
           # 6.2 否則,就交給管道處理
           ......
    ......

安裝程式碼,並執行main.py,直到除錯成功

4 再次改進,將每個爬蟲的名稱直接設定為爬蟲類的一個屬性

參考:

class BaiduSpider(Spider):
    name = 'baidu'    # 為爬蟲命名
    start_urls = ['http://www.baidu.com']    # 設定初始請求url

'''那麼main.py就可以按照這樣的方式設定key值'''
spiders = {BaiduSpider.name: baidu_spider, DoubanSpider.name: douban_spider}