Scrapy框架-Spider

Bricker666發表於2019-02-15

1. Spider

Spider類定義瞭如何爬取某個(或某些)網站。包括了爬取的動作(例如:是否跟進連結)以及如何從網頁的內容中提取結構化資料(爬取item)。 換句話說,Spider就是您定義爬取的動作及分析某個網頁(或者是有些網頁)的地方。

class scrapy.Spider是最基本的類,所有編寫的爬蟲必須繼承這個類。

主要用到的函式及呼叫順序為:

  • init() : 初始化爬蟲名字和start_urls列表

  • start_requests() 呼叫make_requests_from url():生成Requests物件交給Scrapy下載並返回response

  • parse() : 解析response,並返回Item或Requests(需指定回撥函式)。Item傳給Item pipline持久化 , 而Requests交由Scrapy下載,並由指定的回撥函式處理(預設parse()),一直進行迴圈,直到處理完所有的資料為止。

2.Scrapy原始碼


#所有爬蟲的基類,使用者定義的爬蟲必須從這個類繼承
class Spider(object_ref):

    #定義spider名字的字串(string)。spider的名字定義了Scrapy如何定位(並初始化)spider,所以其必須是唯一的。
    #name是spider最重要的屬性,而且是必須的。
    #一般做法是以該網站(domain)(加或不加 字尾 )來命名spider。 例如,如果spider爬取 mywebsite.com ,該spider通常會被命名為 mywebsite
    name = None

    #初始化,提取爬蟲名字,start_ruls
    def __init__(self, name=None, **kwargs):
        if name is not None:
            self.name = name
        # 如果爬蟲沒有名字,中斷後續操作則報錯
        elif not getattr(self, `name`, None):
            raise ValueError("%s must have a name" % type(self).__name__)

        # python 物件或型別通過內建成員__dict__來儲存成員資訊
        self.__dict__.update(kwargs)

        #URL列表。當沒有指定的URL時,spider將從該列表中開始進行爬取。 因此,第一個被獲取到的頁面的URL將是該列表之一。 後續的URL將會從獲取到的資料中提取。
        if not hasattr(self, `start_urls`):
            self.start_urls = []

    # 列印Scrapy執行後的log資訊
    def log(self, message, level=log.DEBUG, **kw):
        log.msg(message, spider=self, level=level, **kw)

    # 判斷物件object的屬性是否存在,不存在做斷言處理
    def set_crawler(self, crawler):
        assert not hasattr(self, `_crawler`), "Spider already bounded to %s" % crawler
        self._crawler = crawler

    @property
    def crawler(self):
        assert hasattr(self, `_crawler`), "Spider not bounded to any crawler"
        return self._crawler

    @property
    def settings(self):
        return self.crawler.settings

    #該方法將讀取start_urls內的地址,併為每一個地址生成一個Request物件,交給Scrapy下載並返回Response
    #該方法僅呼叫一次
    def start_requests(self):
        for url in self.start_urls:
            yield self.make_requests_from_url(url)

    #start_requests()中呼叫,實際生成Request的函式。
    #Request物件預設的回撥函式為parse(),提交的方式為get
    def make_requests_from_url(self, url):
        return Request(url, dont_filter=True)

    #預設的Request物件回撥函式,處理返回的response。
    #生成Item或者Request物件。使用者必須實現這個類
    def parse(self, response):
        raise NotImplementedError

    @classmethod
    def handles_request(cls, request):
        return url_is_from_spider(request.url, cls)

    def __str__(self):
        return "<%s %r at 0x%0x>" % (type(self).__name__, self.name, id(self))

    __repr__ = __str__

2.1. Scrapy主要屬性和方法

  • name

定義spider名字的字串。

例如,如果spider爬取 mywebsite.com ,該spider通常會被命名為 mywebsite

  • allowed_domains

包含了spider允許爬取的域名(domain)的列表,可選。

  • start_urls

初始URL元祖/列表。當沒有制定特定的URL時,spider將從該列表中開始進行爬取。

  • start_requests(self)

該方法必須返回一個可迭代物件(iterable)。該物件包含了spider用於爬取(預設實現是>使用 start_urls 的url)的第一個Request。

當spider啟動爬取並且未指定start_urls時,該方法被呼叫。

  • parse(self, response)

當請求url返回網頁沒有指定回撥函式時,預設的Request物件回撥函式。用來處理網頁返回的response,以及生成Item或者Request物件。

  • log(self, message[, level, component])

使用 scrapy.log.msg() 方法記錄(log)message。 更多資料請參見 logging

3.parse()方法的工作機制

1. 因為使用的yield,而不是return。parse函式將會被當做一個生成器使用。scrapy會逐一獲取parse方法中生成的結果,並判斷該結果是一個什麼樣的型別;
2. 如果是request則加入爬取佇列,如果是item型別則使用pipeline處理,其他型別則返回錯誤資訊。
3. scrapy取到第一部分的request不會立馬就去傳送這個request,只是把這個request放到佇列裡,然後接著從生成器裡獲取;
4. 取盡第一部分的request,然後再獲取第二部分的item,取到item了,就會放到對應的pipeline裡處理;
5. parse()方法作為回撥函式(callback)賦值給了Request,指定parse()方法來處理這些請求 scrapy.Request(url, callback=self.parse)
6. Request物件經過排程,執行生成 scrapy.http.response()的響應物件,並送回給parse()方法,直到排程器中沒有Request(遞迴的思路)
7. 取盡之後,parse()工作結束,引擎再根據佇列和pipelines中的內容去執行相應的操作;
8. 程式在取得各個頁面的items前,會先處理完之前所有的request佇列裡的請求,然後再提取items。
7. 這一切的一切,Scrapy引擎和排程器將負責到底。

相關文章