scrapy系列(三)——基礎spider源碼解析


前面兩章介紹了scrapy的安裝和項目的新建,那么這一章就講講spider吧。

scrapy有個命令是runspider, 這個命令的作用就是將一個spider當做一個python文件去執行,而不用創建一個完整的項目。可以說是最簡單的一個爬蟲項目了,只有一個文件,這也體現出了spider對於scrapy的重要性,item和pipline可有可無,settings等也可以使用默認的,可是spider必須自己構造。而我們寫爬蟲的時候大部分時間和精力也是耗費在這里,所以spider的重要性就不言而喻了。

這次教程將結合官方文檔和源碼一起來講解,希望大家能夠喜歡。

首先看看官方文檔對於spider的介紹:

最上面一段就不解釋了,就是介紹spider的作用和功能。對於spider來說主要經歷如下幾件事:

1.根據url生成Request並指定回調方法處理Response。第一個Request是通過start_requests()產生的,該方法下面會講到。

2. 在回調方法中,解析頁面的Response,返回Item實例或者Request實例,或者這兩種實例的可迭代對象。

3.在回調方法中,通常使用Selectors(也可以使用BeautifulSoup,lxml等)來提取數據。

4.最后spider會return item給Pipline完成數據的清洗,持久化等操作。

scrapy為我們提供了幾款基礎的spider,我們需要繼承這些來實現自己的spider。我們接下來就根據資料及源碼來了解它們。

class scrapy.spiders.Spider 下面是它的源碼:

class Spider(object_ref):
    """Base class for scrapy spiders. All spiders must inherit from this
    class.
    """

    name = None
    custom_settings = None

    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__)
        self.__dict__.update(kwargs)
        if not hasattr(self, 'start_urls'):
            self.start_urls = []

    @property
    def logger(self):
        logger = logging.getLogger(self.name)
        return logging.LoggerAdapter(logger, {'spider': self})

    def log(self, message, level=logging.DEBUG, **kw):
        """Log the given message at the given log level

        This helper wraps a log call to the logger within the spider, but you
        can use it directly (e.g. Spider.logger.info('msg')) or use any other
        Python logger too.
        """
        self.logger.log(level, message, **kw)

    @classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        spider = cls(*args, **kwargs)
        spider._set_crawler(crawler)
        return spider

    def set_crawler(self, crawler):
        warnings.warn("set_crawler is deprecated, instantiate and bound the "
                      "spider to this crawler with from_crawler method "
                      "instead.",
                      category=ScrapyDeprecationWarning, stacklevel=2)
        assert not hasattr(self, 'crawler'), "Spider already bounded to a " \
                                             "crawler"
        self._set_crawler(crawler)

    def _set_crawler(self, crawler):
        self.crawler = crawler
        self.settings = crawler.settings
        crawler.signals.connect(self.close, signals.spider_closed)

    def start_requests(self):
        for url in self.start_urls:
            yield self.make_requests_from_url(url)

    def make_requests_from_url(self, url):
        return Request(url, dont_filter=True)

    def parse(self, response):
        raise NotImplementedError

    @classmethod
    def update_settings(cls, settings):
        settings.setdict(cls.custom_settings or {}, priority='spider')

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

    @staticmethod
    def close(spider, reason):
        closed = getattr(spider, 'closed', None)
        if callable(closed):
            return closed(reason)

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

    __repr__ = __str__

該類的基類是object_ref,其定義如下圖所示:

從源碼中可以看到這個類的子類的實例都會記錄它本身的存活狀況,這個作用會在以后講解,目前用不到。

類scrapy.spiders.Spider 是最簡單的spider,所有的spider包括自己定義的和scrapy提供的都會繼承它。它只是提供最基本的特性,也是我最常用的spider類。

name:

  這個屬性是字符串變量,是這個類的名稱,代碼會通過它來定位spider,所以它必須唯一,它是spider最重要的屬性。回頭看看源碼中__init__的定義,可以發現這個屬性是可以修改的,如果不喜歡或者有需要重命名spider的name,可以在啟動的時候傳參修改name屬性。

allowed_domains:

  這個屬性是一個列表,里面記載了允許采集的網站的域名,該值如果沒定義或者為空時表示所有的域名都不進行過濾操作。如果url的域名不在這個變量中,那么這個url將不會被處理。不想使用域名過濾功能時可以在settings中注釋掉OffsiteMiddleware, 個人不建議這么做。

start_urls:

  這個屬性是一個列表或者元組,其作用是存放起始urls,相當於這次任務的種子。使用默認模板創建spider時,該值是個元組,創建元組並且只有一個元素時需要在元素后面添加“,”來消除歧義,不然會報錯:“ValueError: Missing scheme in request url: h”。這邊經常有人出錯,為了避免這個錯誤可以根據上一章內容,將模板中start_urls的值設置為列表。

custom_settings:

  這個屬性值是一個字典,存放settings鍵值對,用於覆蓋項目中的settings.py的值,可以做到在一個項目中的不同spider可以有不同的配置。不過這個值要慎用,有些settings的值覆蓋也沒有起作用,eg:“LOG_FILE”。如果想每個spider都有自己的log文件的話就不能這么做。因為日志操作在這個方法執行之前,那么無論怎么改都改不了之前的行為。不過這個問題scrapy研發團隊已經注意到了,相信不久的將來會進行處理的。

crawler:

  這個值從源碼可以看出來自於方法from_crawler()。該值是一個Crawler 實例, 其作用后面的教程會講解,這邊就不細說了。

 settings:

  這個值也是來自於方法from_crawler()。是一個Settings 實例,這個后面也會細說,稍安勿躁哈。

logger:

  顧名思義,記錄日志用的,也是后面講,耐心等候哈。

from_crawler:

  這是一個類方法,scrapy創建spider的時候會調用。調用位置在crawler.py 的類Crawler中,源碼可以自己去看看,就不帶大家看了。這個方法的源碼在上面,我們可以看到,在實例化這個spider以后,這個實例才有的settings和crawler屬性,所以在__init__方法中是沒法訪問這倆屬性的。如果非要在__init__方法中使用相關屬性,那么只能重寫該方法,大家可以嘗試寫寫。

start_requests():

  這個方法必須返回一個可迭代對象,切記!!!!上面就有源碼很簡單,就不細說了。如果想對屬性start_urls做一些操作(增刪改),並希望結果作為種子url去采集網站的時候,可以重寫這個方法來實現。有了這個方法,甚至都不用在代碼中定義start_urls。比如我們想要讀取持久化的url執行采集操作,那么就沒必要轉存進start_urls里面,可以直接請求這些urls。當種子urls需要post請求的話,也需要重寫該方法。

 make_requests_from_url(url):

  這個方法顧名思義,要是還不懂就看看上面的源碼。這里只說一點,因為這里的Request初始化沒有回調方法,就是默認采用parse方法作為回調。另外這里的dont_filter值為True,這個值的作用是該url不會被過濾,至於具體細節請聽下回分解。

 parse(self, response):

  這個方法對於有經驗的同學來說再熟悉不過了,我就簡單的說說。這個方法作為默認回調方法,Request沒有指定回調方法的時候會調用它,這個回調方法和別的回調方法一樣返回值只能是Request, 字典和item對象,或者它們的可迭代對象。

 log(message[, level, component]):

  這個方法是對logger的包裝,看看源碼就好,沒什么什么可說的。

closed(reason):

  當這個spider結束時這個方法會被調用,參數是一個字符串,是結束的原因。這種用法以后會介紹,這里只需記住,想在spider結束時做一些操作時可以寫在這里。

scrapy基礎spider算是介紹完了,有興趣的小伙伴可以對照一個好的實例來看這個文章,或許幫助更大。如果有什么疑問,大家可以在評論區討論,共同進步。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM