爬蟲 - Scrapy 框架-CrawlSpider以及圖片管道使用

語言: CN / TW / HK

theme: smartblue

這是我參與11月更文挑戰的第15天,活動詳情檢視:2021最後一次更文挑戰」。

Scrapy 框架-CrawlSpider

1. CrawlSpiders

原理圖

sequenceDiagram start_urls ->>排程器: 初始化url 排程器->>下載器: request 下載器->>rules: response rules->>資料提取: response rules->>排程器: 新的url

通過下面的命令可以快速建立 CrawlSpider模板 的程式碼 scrapy genspider -t crawl 檔名 (allowed_url) 首先在說下Spider,它是所有爬蟲的基類,而CrawSpiders就是Spider的派生類。對於設計原則是隻爬取start_url列表中的網頁,而從爬取的網頁中獲取link並繼續爬取的工作CrawlSpider類更適合

2. Rule物件

Rule類與CrawlSpider類都位於scrapy.contrib.spiders模組中

class scrapy.contrib.spiders.Rule ( link_extractor, callback=None,cb_kwargs=None,follow=None,process_links=None,process_request=None ) 引數含義: - link_extractor為LinkExtractor,用於定義需要提取的連結 - callback引數:當link_extractor獲取到連結時引數所指定的值作為回撥函式 - callback引數使用注意: 當編寫爬蟲規則時,請避免使用parse作為回撥函式。於CrawlSpider使用parse方法來實現其邏輯,如果您覆蓋了parse方法,crawlspider將會執行失敗

  • follow:指定了根據該規則從response提取的連結是否需要跟進。當callback為None,預設值為True

  • process_links:主要用來過濾由link_extractor獲取到的連結

  • process_request:主要用來過濾在rule中提取到的request

3.LinkExtractors

3.1 概念

顧名思義,連結提取器

3.2 作用

response物件中獲取連結,並且該連結會被接下來爬取 每個LinkExtractor有唯一的公共方法是 extract_links(),它接收一個 Response 物件,並返回一個 scrapy.link.Link 物件

3.3 使用

class scrapy.linkextractors.LinkExtractor( allow = (), deny = (), allow_domains = (), deny_domains = (), deny_extensions = None, restrict_xpaths = (), tags = ('a','area'), attrs = ('href'), canonicalize = True, unique = True, process_value = None ) 主要引數:

  • allow:滿足括號中“正則表示式”的值會被提取,如果為空,則全部匹配。

  • deny:與這個正則表示式(或正則表示式列表)不匹配的URL一定不提取。

  • allow_domains:會被提取的連結的domains。

  • deny_domains:一定不會被提取連結的domains。

  • restrict_xpaths:使用xpath表示式,和allow共同作用過濾連結(只選到節點,不選到屬性)

  • restrict_css:使用css表示式,和allow共同作用過濾連結(只選到節點,不選到屬性)
3.3.1 檢視效果(shell中驗證)

首先執行 scrapy shell http://www.fhxiaoshuo.com/read/33/33539/17829387.shtml

繼續import相關模組: from scrapy.linkextractors import LinkExtractor 提取當前網頁中獲得的連結 link = LinkExtractor(restrict_xpaths=(r'//div[@class="bottem"]/a[4]') 呼叫LinkExtractor例項的extract_links()方法查詢匹配結果 link.extract_links(response)

3.3.2 檢視效果 CrawlSpider版本

``` from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from xiaoshuo.items import XiaoshuoItem

class XiaoshuoSpiderSpider(CrawlSpider): name = 'xiaoshuo_spider' allowed_domains = ['fhxiaoshuo.com'] start_urls = ['http://www.fhxiaoshuo.com/read/33/33539/17829387.shtml']

rules = [
    Rule(LinkExtractor(restrict_xpaths=(r'//div[@class="bottem"]/a[4]')), callback='parse_item'),]

def parse_item(self, response):
    info = response.xpath("//div[@id='TXT']/text()").extract()
    it = XiaoshuoItem()
    it['info'] = info
    yield it

**注意:** rules = [ Rule(LinkExtractor(restrict_xpaths=(r'//div[@class="bottem"]/a[4]')), callback='parse_item'),] ``` - callback後面函式名用引號引起 - 函式名不能是parse - 格式問題

圖片管道使用

1. 介紹

Scrapy提供了一個 item pipeline ,來下載屬於某個特定專案的圖片,比如,當你抓取產品時,也想把它們的圖片下載到本地。

這條管道,被稱作圖片管道,在 ImagesPipeline 類中實現,提供了一個方便並具有額外特性的方法,來下載並本地儲存圖片:

  • 將所有下載的圖片轉換成通用的格式(JPG)和模式(RGB)
  • 避免重新下載最近已經下載過的圖片
  • 縮圖生成
  • 檢測影象的寬/高,確保它們滿足最小限制

這個管道也會為那些當前安排好要下載的圖片保留一個內部佇列,並將那些到達的包含相同圖片的專案連線到那個佇列中。 這可以避免多次下載幾個專案共享的同一個圖片

2. 使用圖片管道

當使用 ImagesPipeline ,典型的工作流程如下所示:

  1. 在一個爬蟲裡,你抓取一個專案,把其中圖片的URL放入 image_urls 組內
  2. 專案從爬蟲內返回,進入專案管道
  3. 當專案進入 ImagesPipeline,image_urls 組內的URLs將被Scrapy的排程器和下載器(這意味著排程器和下載器的中介軟體可以複用)安排下載,當優先順序更高,會在其他頁面被抓取前處理。專案會在這個特定的管道階段保持“locker”的狀態,直到完成圖片的下載(或者由於某些原因未完成下載)。
  4. 當圖片下載完,另一個組(images)將被更新到結構中。這個組將包含一個字典列表,其中包括下載圖片的資訊,比如下載路徑、源抓取地址(從 image_urls 組獲得)和圖片的校驗碼。 images 列表中的圖片順序將和源 image_urls 組保持一致。如果某個圖片下載失敗,將會記錄下錯誤資訊,圖片也不會出現在 images 組中

3. 具體流程(此處以zol網站為例)

  1. 定義item ``` import scrapy

class ImagedownloadItem(scrapy.Item): # define the fields for your item here like: img_name = scrapy.Field() img_urls =scrapy.Field() ```

  1. 編寫spider

    思路:獲取檔案地址-->獲取圖片名稱-->推送地址

此處是一張一張的推送 ``` class ZolSpiderSpider(scrapy.Spider): name = 'zol' allowed_domains = ['zol.com.cn'] url ='http://desk.zol.com.cn' start_urls = [url+'/bizhi/7106_88025_2.html']

def parse(self, response):
    image_url = response.xpath('//img[@id="bigImg"]/@src').extract_first()
    image_name = response.xpath('//h3')[0].xpath('string(.)').extract_first().strip().replace('\r\n\t\t', '')
    next_image = response.xpath('//a[@id="pageNext"]/@href').extract_first()
    item = ImagedownloadItem()
    item["img_name"] = image_name
    item["img_urls"] = image_url
    yield item

    yield scrapy.Request(self.url+next_image,callback=self.parse,)

``` 3. 編寫pipline

以下如果不想改檔名,meta屬性可以忽略不寫

def get_media_requests(self, item, info): ''' #如果item[urls]裡裡面是列表,用下面 urls= item['urls'] for url in urls: yield scrapy.Request(url,meta={"item",item}) ''' # 如果item[urls]裡裡面是一個圖片地址,用這下面的 yield scrapy.Request(item['img_urls'], meta={"item": item})

因為scrapy裡是使用它們URL的 SHA1 hash 作為檔名,所以如果想重新命名: ``` def file_path(self, request, response=None, info=None): item = request.meta["item"] #去掉檔案裡的/,避免建立圖片檔案時出錯 filename = item["img_name"].replace("/","-")+".jpg"

    return filename

4. 定義圖片儲存在哪? 在settings中增加一句 IMAGES_STORE = "e:/pics" ```