Spider開發流程

實現一個Spider子類的過程很像是完成一系列填空題,Scrapy框架提出以下問題讓用戶在Spider子類中作答:
● 爬蟲從哪個或哪些頁面開始爬取?
● 對於一個已下載的頁面,提取其中的哪些數據?
● 爬取完當前頁面後,接下來爬取哪個或哪些頁面?上面問題的答案包含了一個爬蟲最重要的邏輯,回答了這些問題,
一個爬蟲也就開發出來了。

下面給出一個簡易爬蟲的實例給讀者找找感覺

# -*- coding: utf-8 -*-
import scrapy
class BooksSpider(scrapy.Spider):
    # 每一個爬蟲的唯一標識
    name = "books"
    # 定義爬蟲爬取的起始點,起始點可以是多個,我們這裏是一個
    start_urls = ['http://books.toscrape.com/']
    def parse(self, response):
        # 提取數據
        # 每一本書的信息是在<article class="product_pod">中,我們使用
        # css()方法找到所有這樣的article 元素,並依次迭代
        for book in response.css('article.product_pod'):
            # 書名信息在article > h3 > a 元素的title屬性裏
            # 例如: <a title="A Light in the Attic">A Light in the ...</a>
            name = book.xpath('./h3/a/@title').extract_first()
            # 書價信息在 <p class="price_color">的TEXT中。
            # 例如: <p class="price_color">£51.77</p>
            price = book.css('p.price_color::text').extract_first()
            yield {
                'name': name,
                'price': price,
              }
            # 提取鏈接
            # 下一頁的url 在ul.pager > li.next > a 裏面
            # 例如: <li class="next"><a href="catalogue/page-2.html">next</a></li>
            next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
            if next_url:
                # 如果找到下一頁的url,得到絕對路徑,構造新的Request 對象
                next_url = response.urljoin(next_url)
                yield scrapy.Request(next_url, callback=self.parse)

實現一個Spider只需要完成下面4個步驟:
步驟 01 繼承scrapy.Spider。
步驟 02 爲Spider取名。
步驟 03 設定起始爬取點。
步驟 04 實現頁面解析函數。

繼承scrapy.Spider

Scrapy框架提供了一個Spider基類,我們編寫的Spider需要繼承
它:

import scrapy
class BooksSpider(scrapy.Spider):
...


這個Spider基類實現了以下內容:
● 供Scrapy引擎調用的接口,例如用來創建Spider實例的類方法from_crawler。
● 供用戶使用的實用工具函數,例如可以調用log方法將調試信息輸出到日誌。
● 供用戶訪問的屬性,例如可以通過settings屬性訪問配置文件中的配置。
實際上,在初學Scrapy時,不必關心Spider基類的這些細節,未來有需求時再去查閱文檔即可。

爲Spider命名

在一個Scrapy項目中可以實現多個Spider,每個Spider需要有一個能夠區分彼此的唯一標識,Spider的類屬性name便是這個唯一標識。

class BooksSpider(scrapy.Spider):
name = "books"
...

執行scrapy crawl命令時就用到了這個標識,告訴Scrapy使用哪個Spider進行爬取。

設定起始爬取點

Spider必然要從某個或某些頁面開始爬取,我們稱這些頁面爲起始爬取點,可以通過類屬性start_urls來設定起始爬取點:

class BooksSpider(scrapy.Spider):
    ...
    start_urls = ['http://books.toscrape.com/']
    ...

start_urls通常被實現成一個列表,其中放入所有起始爬取點的url(例子中只有一個起始點)。看到這裏,大家可能會想,請求頁面下載不是一定要提交Request對象麼?而我們僅定義了url列表,是誰暗中構造並提交了相應的Request對象呢?通過閱讀Spider基類的源碼可以找到答案,相關代碼如下:

class Spider(object_ref):
    ...
    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
        ...


從代碼中可以看出,Spider基類的start_requests方法幫助我們構造並提交了Request對象,對其中的原理做如下解釋:
● 實際上,對於起始爬取點的下載請求是由Scrapy引擎調用Spider對象的start_requests方法提交的,由於BooksSpider類沒有實現start_requests方法,因此引擎會調用Spider基類的start_requests方法。
● 在start_requests方法中,self.start_urls便是我們定義的起始爬取點列表(通過實例訪問類屬性),對其進行迭代,用迭代出的每個url作爲參數調用make_requests_from_url方法。
● 在make_requests_from_url方法中,我們找到了真正構造Reqeust對象的代碼,僅使用url和dont_filter參數構造Request對象。
● 由於構造Request對象時並沒有傳遞callback參數來指定頁面解析函數,因此默認將parse方法作爲頁面解析函數。此時BooksSpider必須實現parse方法,否則就會調用Spider基類的parse方法,從而拋出NotImplementedError異常(可以看作基類定義了一個抽象接口)。
● 起始爬取點可能有多個,start_requests方法需要返回一個可迭代對象(列表、生成器等),其中每一個元素是一個Request對象。這裏,start_requests方法被實現成一個生成器函數(生成器對象是可迭代的),每次由yield語句返回一個Request對象。由於起始爬取點的下載請求是由引擎調用Spider對象的start_requests方法產生的,因此我們也可以在BooksSpider中實現start_requests方法(覆蓋基類Spider的start_requests方法),直接構造並提交起始爬取點的Request對象。在某些場景下使用這種方式更加靈活,例如有時想爲Request添加特定的HTTP請求頭部,或想爲Request指定特定的頁面解析函數。以下是通過實現start_requests方法定義起始爬取點的示例代碼
(改寫BooksSpider):

class BooksSpider(scrapy.Spider):
    # start_urls = ['http://books.toscrape.com/']
    # 實現start_requests 方法, 替代start_urls類屬性
    def start_requests(self):
        yield scrapy.Request('http://books.toscrape.com/',callback=self.parse_book,
            headers={'User-Agent': 'Mozilla/5.0'},dont_filter=True)
    # 改用parse_book 作爲回調函數
    def parse_book(response):
        ...


到此,我們介紹完了爲爬蟲設定起始爬取點的兩種方式:
● 定義start_urls屬性。
● 實現start_requests方法。

實現頁面解析函數

頁面解析函數也就是構造Request對象時通過callback參數指定的回調函數(或默認的parse方法)。頁面解析函數是實現Spider中最核心的部分,它需要完成以下兩項工作:
● 使用選擇器提取頁面中的數據,將數據封裝後(Item或字典)提交給Scrapy引擎。
● 使用選擇器或LinkExtractor提取頁面中的鏈接,用其構造新的Request對象並提交給Scrapy引擎(下載鏈接頁面)。一個頁面中可能包含多項數據以及多個鏈接,因此頁面解析函數被要求返回一個可迭代對象(通常被實現成一個生成器函數),每次迭代返回一項數據(Item或字典)或一個Request對象。

發佈了15 篇原創文章 · 獲贊 6 · 訪問量 3582
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章