天天看點

python爬蟲之Scrapy架構,基本介紹使用以及用架構下載下傳圖檔案例

一、Scrapy架構簡介

Scrapy是:由Python語言開發的一個快速、高層次的螢幕抓取和web抓取架構,用于抓取web站點并從頁面中提取結構化的資料,隻需要實作少量的代碼,就能夠快速的抓取。

Scrapy使用了Twisted異步網絡架構來處理網絡通信,可以加快我們的下載下傳速度,不用自己去實作異步架構,并且包含了各種中間件接口,可以靈活地實作各種需求。

Scrapy可以應用在包括資料挖掘、資訊處理或存儲曆史資料等一系列的程式中,其最初是為頁面抓取(更确切地說是網絡抓取)而設計的,也可以應用于擷取API所傳回的資料(例如Amazon Associates Web Services)或者通用的網絡爬蟲。

二、Scrapy架構

1、架構圖

官方架構圖

python爬蟲之Scrapy架構,基本介紹使用以及用架構下載下傳圖檔案例

翻譯架構圖

python爬蟲之Scrapy架構,基本介紹使用以及用架構下載下傳圖檔案例

2、元件

Scrapy主要包括了以下元件:

  • 爬蟲中間件(Spider Middleware):位于Scrapy引擎和爬蟲之間的架構,主要用于處理爬蟲的響應輸入和請求輸出。
  • 排程器中間件(Scheduler Middleware):位于Scrapy引擎和排程器之間的架構,主要用于處理從Scrapy引擎發送到排程器的請求和響應。
  • 排程器(Scheduler):用來接收引擎發過來的請求,壓入隊列中,并在引擎再次請求的時候傳回。它就像是一個URL的優先隊列,由它來決定下一個要抓取的網址是什麼,同時在這裡會去除重複的網址。
  • 下載下傳器中間件(Downloader Middleware):位于Scrapy引擎和下載下傳器之間的架構,主要用于處理Scrapy引擎與下載下傳器之間的請求及響應。代理IP和使用者代理可以在這裡設定。
  • 下載下傳器(Downloader):用于下載下傳網頁内容,并将網頁内容傳回給爬蟲。

Scrapy引擎(ScrapyEngine):用來控制整個系統的資料處理流程,并進行事務處理的觸發。

  • 爬蟲(Spiders):爬蟲主要是幹活的,用于從特定網頁中提取自己需要的資訊,即所謂的項目(又稱實體)。也可以從中提取URL,讓Scrapy繼續爬取下一個頁面。
  • 項目管道(Pipeline):負責處理爬蟲從網頁中爬取的項目,主要的功能就是持久化項目、驗證項目的有效性、清除不需要的資訊。當頁面被爬蟲解析後,将被送到項目管道,并經過幾個特定的次序來處理其資料。

3、運作流程

資料流(Data flow),Scrapy中的資料流由執行引擎(ScrapyEngine)控制,其過程如下:

  1. 引擎打開一個網站(open a domain),找到處理該網站的Spider并向該spider請求第一個要爬取的URL(s)。
  2. 引擎從Spider中擷取到第一個要爬取的URL并在排程器(Scheduler)以Request排程。
  3. 引擎向排程器請求下一個要爬取的URL。
  4. 排程器傳回下一個要爬取的URL給引擎,引擎将URL通過下載下傳中間件(請求(request)方向)轉發給下載下傳器(Downloader)。
  5. 一旦頁面下載下傳完畢,下載下傳器生成一個該頁面的Response,并将其通過下載下傳中間件(傳回(response)方向)發送給引擎。
  6. 引擎從下載下傳器中接收到Response并通過Spider中間件(輸入方向)發送給Spider處理。
  7. Spider處理Response并傳回爬取到的Item及(跟進的)新的Request給引擎。
  8. 引擎将(Spider傳回的)爬取到的Item給Item Pipeline,将(Spider傳回的)Request給排程器。
  9. (從第二步)重複直到排程器中沒有更多地request,引擎關閉該網站

三、Scrapy安裝以及生成項目

1、下載下傳安裝

Linux下載下傳方式,直接安裝

pip install scrapy
或者
pip3 install scrapy)           

windows 如果用Pycharm的話,在Pycharm底部打開指令終端

python爬蟲之Scrapy架構,基本介紹使用以及用架構下載下傳圖檔案例

輸入指令

pip install scrapy           

2、建立Scrapy項目

#建立一個叫ScrapyDemmo
scrapy startproject ScrapyDemmo
#進入項目檔案夾
cd ScrapyDemmo
#建立一個名為baidu的爬蟲,爬蟲目标www.baidu.com
scrapy genspider baidu www.baidu.com           

建立完成後,目錄結構如下:

python爬蟲之Scrapy架構,基本介紹使用以及用架構下載下傳圖檔案例
  • scrapy.cfg: 項目的配置檔案。
  • scrapyspider/: 該項目的python子產品。之後您将在此加入代碼。
  • scrapyspider/items.py: 項目中的item檔案。
  • scrapyspider/pipelines.py: 項目中的pipelines檔案。
  • scrapyspider/settings.py: 項目的設定檔案。
  • scrapyspider/spiders/: 放置spider代碼的目錄。

spiders下的baidu.py是scrapy用指令(scrapy genspider baidu www.baidu.com)自動為我們生成的。

内容如下:

import scrapy

class BaiduSpider(scrapy.Spider):
    name = 'baidu'
    allowed_domains = ['www.baidu.com']
    start_urls = ['http://www.baidu.com/']

    def parse(self, response):
        title = response.xpath('//html/dead/title/text()')
        print(title)           

當然,可以不用指令生成,可以自己在spiders下建立爬蟲,您必須繼承 scrapy.Spider 類, 且定義以下三個屬性:

  • name: 用于差別Spider。 該名字必須是唯一的,您不可以為不同的Spider設定相同的名字。
  • start_urls: 包含了Spider在啟動時進行爬取的url清單。 是以,第一個被擷取到的頁面将是其中之一。 後續的URL則從初始的URL擷取到的資料中提取。
  • parse() 是spider的一個方法。 被調用時,每個初始URL完成下載下傳後生成的 Response 對象将會作為唯一的參數傳遞給該函數。 該方法負責解析傳回的資料(response data),提取資料(生成item)以及生成需要進一步處理的URL的 Request 對象。

3、運作爬蟲

運作方法:

在項目目錄底下用指令運作,如下,我項目目錄 D:\Python\ScrapyDemmo,運作name為baidu的爬蟲

D:\Python\ScrapyDemmo> scrapy crawl baidu           

在scrapy中,為了避免每一次運作或調試都輸入一串指令,可以在項目檔案下建立一個run.py檔案,每次運作爬蟲隻需要運作此腳本即可。且運作調試模式也需要設定此啟動腳本。

from scrapy import cmdline

cmdline.execute("scrapy crawl baidu".split())           

最後運作這個run.py即可,執行結果:

D:\Python\venv\Scripts\python.exe D:\Python\ScrapyDemmo\ScrapyDemmo\run.py 
2022-10-28 10:12:55 [scrapy.utils.log] INFO: Scrapy 2.7.0 started (bot: ScrapyDemmo)
2022-10-28 10:12:55 [scrapy.utils.log] INFO: Versions: lxml 4.9.1.0, libxml2 2.9.12, cssselect 1.1.0, parsel 1.6.0, w3lib 2.0.1, Twisted 22.8.0, Python 3.9.13 (tags/v3.9.13:6de2ca5, May 17 2022, 16:36:42) [MSC v.1929 64 bit (AMD64)], pyOpenSSL 22.1.0 (OpenSSL 3.0.5 5 Jul 2022), cryptography 38.0.1, Platform Windows-10-10.0.22000-SP0
2022-10-28 10:12:55 [scrapy.crawler] INFO: Overridden settings:
{'BOT_NAME': 'ScrapyDemmo',
 'NEWSPIDER_MODULE': 'ScrapyDemmo.spiders',
 'REQUEST_FINGERPRINTER_IMPLEMENTATION': '2.7',
 'ROBOTSTXT_OBEY': True,
 'SPIDER_MODULES': ['ScrapyDemmo.spiders'],
 'TWISTED_REACTOR': 'twisted.internet.asyncioreactor.AsyncioSelectorReactor'}
2022-10-28 10:12:55 [asyncio] DEBUG: Using selector: SelectSelector
...
...           

若嫌棄scrapy日志檔案太雜亂,想無日志輸出,隻需在後面增加--nolog即可:

from scrapy import cmdline

cmdline.execute('scrapy crawl baidu --nolog'.split())           

執行導出為json或scv格式,執行爬蟲檔案時添加-o選項即可

scrapy crawl 項目名 -o *.csv

scrapy crawl 項目名 -o *.json

對于json檔案,在setting.js檔案裡添加,設定編碼格式,否則會亂碼:

from scrapy import cmdline
 
cmdline.execute('scrapy crawl baidu -o baidu.csv'.split())           

四、Scrapy配置檔案settings.py

預設配置檔案,主要設定參數:

BOT_NAME = 'ScrapyDemmo' #Scrapy項目的名字,這将用來構造預設 User-Agent,同時也用來log,當您使用 startproject 指令建立項目時其也被自動指派。

SPIDER_MODULES = ['ScrapyDemmo.spiders'] #Scrapy搜尋spider的子產品清單 預設: [xxx.spiders]
NEWSPIDER_MODULE = 'ScrapyDemmo.spiders' #使用 genspider 指令建立新spider的子產品。預設: 'xxx.spiders'  


#爬取的預設User-Agent,除非被覆寫 
#USER_AGENT = 'ScrapyDemmo (+http://www.yourdomain.com)'

#如果啟用,Scrapy将會采用 robots.txt政策 
ROBOTSTXT_OBEY = True

#Scrapy downloader 并發請求(concurrent requests)的最大值,預設: 16 
#CONCURRENT_REQUESTS = 32

#為同一網站的請求配置延遲(預設值:0) 
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3 #下載下傳器在下載下傳同一個網站下一個頁面前需要等待的時間,該選項可以用來限制爬取速度,減輕伺服器壓力。同時也支援小數:0.25 以秒為機關  

#下載下傳延遲設定隻有一個有效 
#CONCURRENT_REQUESTS_PER_DOMAIN = 16  #對單個網站進行并發請求的最大值。
#CONCURRENT_REQUESTS_PER_IP = 16	#對單個IP進行并發請求的最大值。如果非0,則忽略

#禁用Cookie(預設情況下啟用) 
#COOKIES_ENABLED = False

#禁用Telnet控制台(預設啟用) 
#TELNETCONSOLE_ENABLED = False

#覆寫預設請求标頭:  
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}

#項目管道,300為優先級,越低越爬取的優先度越高
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
#    'ScrapyDemmo.pipelines.ScrapydemmoPipeline': 300,
#}           

還可以設定日志的等級與日志存放的路徑:

相關變量

LOG_LEVEL= ""
LOG_FILE="日志名.log"           

日志等級分為,預設等級是1

  1. DEBUG 調試資訊
  2. INFO 一般資訊
  3. WARNING 警告
  4. ERROR 普通錯誤
  5. CRITICAL 嚴重錯誤

如果設定

LOG_LEVEL="WARNING",就隻會WARNING等級之下的ERROR和CRITICAL

一般主要需要配置的幾個參數,其他按需配置即可。

USER_AGENT:預設是注釋的,這個東西非常重要,如果不寫很容易被判斷為電腦爬蟲。

ROBOTSTXT_OBEY:是否遵循機器人協定,預設是true,需要改為false,否則很多東西爬不了

DEFAULT_REQUEST_HEADERS:和USER_AGENT類似,隻是參數更完整。

五、完整案例(下載下傳圖檔)

用scrapy架構下載下傳以前的示例:python爬蟲之批量下載下傳圖檔

1、修改settings.py 主要參數

#關閉robot.txt協定
ROBOTSTXT_OBEY = False

#頁面延遲下載下傳,我這裡測試,可以先不設定
DOWNLOAD_DELAY = 1

# 是否啟用Cookie
COOKIES_ENABLED = True

#請求頭 
DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
  'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'
}
#打開下載下傳器
DOWNLOADER_MIDDLEWARES = {
    'ScrapyDemmo.middlewares.ScrapydemmoDownloaderMiddleware': 543,
}
#打開優先級,并添加自己編寫的圖檔下載下傳管道
ITEM_PIPELINES = {
   'ScrapyDemmo.pipelines.ScrapydemmoPipeline': 300,
   'ScrapyDemmo.pipelines.ImageDownloadPipeline': 300,
}
#添加下載下傳儲存目錄
IMAGES_STORE = 'D:\Python\pic'

# 檔案儲存時間
#IMAGES_EXPIRES = 90           

2、定義Item字段(Items.py)

本項目用于下載下傳圖檔,是以可以僅建構圖檔名和圖檔位址字段。

import scrapy


class ScrapydemmoItem(scrapy.Item):
	#圖檔下載下傳連結
    image_url = scrapy.Field()
    #圖檔名稱
    image_name = scrapy.Field()           

3、編寫爬蟲檔案(spiders目錄下)

這裡檔案名為:image_download.py

以前用requests庫和BeautifulSoup庫下載下傳圖檔,這裡就不需要了,scrapy自帶相關函數和方法。

scrapy元素定位,提供三種方式,正則、Xpath表達式、css。

我這裡有xpath定位方式。

import scrapy
import re
from ..items import ScrapydemmoItem

class ImageSpider(scrapy.Spider):
    name = 'image_download'
    allowed_domains = ['desk.3gbizhi.com']
    start_urls = ['https://desk.3gbizhi.com/deskMV/index.html']

    def parse(self, response):
    	#導入Items.py字段
        items = ScrapydemmoItem()
        #擷取所有連結清單
        lists = response.xpath('//div[5]/ul/li')
        #點位元素循環擷取圖檔連結和圖檔名稱
        for i in lists:
        	#圖檔名稱
            image_name = i.xpath('./a/img/@alt').get()
            #圖檔連結
            items['image_url'] = i.xpath('./a/img/@*[1]').get().replace('.278.154.jpg', '')
            #圖檔格式類型
            image_type = re.sub(r'h.*\d+.', '', items['image_url'])
            #拼接檔案名,圖檔名稱+圖檔格式
            items['image_name'] = '{}.{}'.format(image_name, image_type)
            yield  items
		#循環跳轉下一頁,并重複傳回資料,這裡測試先下載下傳1頁的圖檔,總共23頁。
        for i in range(2,3):
            next_url = 'https://desk.3gbizhi.com/deskMV/index_{}.html'.format(i)
            yield scrapy.Request(next_url,callback=self.parse)           

關于 yield 的了解,⾸先,如果你還沒有對yield有個初步分認識,那麼你先把yield看做“return”,這個是直覺的,它⾸先是個return。

最主要的不同在于yield在傳回值後還可以繼續運作接下來的代碼,使用的函數會傳回一個生成器,而return在傳回後就不在執行代碼。

以上兩個yield:

  • yield items:這裡我們通過 yield 傳回的不是 Request 對象,而是一個 ScrapydemmoItem 對象。scrap有架構獲得這個對象之後,會将這個對象傳遞給 pipelines.py來做進一步處理。我們将在 pipelines.py裡将傳遞過來的 scrapy.Item 對象儲存到資料庫裡去。
  • yield scrapy.Request:這裡是在爬取完一頁的資訊後,我們在目前頁面擷取到了下一頁的連結,然後通過 yield 發起請求,并且将 parse 自己作為回調函數來處理下一頁的響應。

4、修改管道檔案pipelines.py用于下載下傳圖檔

除了爬取文本,我們可能還需要下載下傳檔案、視訊、圖檔、壓縮包等,這也是一些常見的需求。scrapy提供了FilesPipeline和ImagesPipeline,專門用于下載下傳普通檔案及圖檔。

繼承 Scrapy 内置的 ImagesPipeline,隻需要重寫get_media_requests 和item_completed函數即可。

from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem
from scrapy import Request

class ScrapydemmoPipeline:
    def process_item(self, item, spider):
        return item

class ImageDownloadPipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
    	# 下載下傳圖檔,如果傳過來的是集合需要循環下載下傳
    	# meta裡面的資料是從spider擷取,然後通過meta傳遞給下面方法:file_path
        yield Request(url = item['image_url'],meta = {'filename':item['image_name']})

    def item_completed(self, results, item, info):
     	# 分析下載下傳結果并剔除下載下傳失敗的圖檔
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Item contains no images")
        return item

    def file_path(self, request, response=None, info=None):
    	# 接收上面meta傳遞過來的圖檔名稱
        file_name = request.meta['filename']
        return file_name           
  • get_media_requests()。它的第一個參數 item 是爬取生成的 Item 對象。我們将它的 url 字段取出來,然後直接生成 Request 對象。此 Request 加入排程隊列,等待被排程,執行下載下傳。
  • item_completed(),它是當單個 Item 完成下載下傳時的處理方法。因為可能有個别圖檔未成功下載下傳,是以需要分析下載下傳結果并剔除下載下傳失敗的圖檔。該方法的第一個參數 results 就是該 Item 對應的下載下傳結果,它是一個清單形式,清單每一個元素是一個元組,其中包含了下載下傳成功或失敗的資訊。這裡我們周遊下載下傳結果找出所有成功的下載下傳清單。如果清單為空,那麼說明該 Item 對應的圖檔下載下傳失敗了,随即抛出異常DropItem,該 Item 忽略。否則傳回該 Item,說明此 Item 有效。

以上兩個函數即可下載下傳圖檔了,圖檔名稱為自動已哈希值命名,如:0db6e07054d966513f0a6f315b687f205c7ced90.jpg 這種命名方式不友好,是以我們需要重寫 file_path函數,自定義圖檔名稱。

  • file_path():它的第一個參數 request 就是目前下載下傳對應的 Request 對象。這個方法用來傳回儲存的檔案名,接收上面meta傳遞過來的圖檔名稱,将圖檔以原來的名稱和定義格式進行儲存。

5、編寫執行檔案run.py運作

在項目下建立run.py作為執行檔案

from scrapy import cmdline

#cmdline.execute('scrapy crawl image_download --nolog'.split())
cmdline.execute('scrapy crawl image_download'.split())           

運作此檔案,執行結果,在目錄下載下傳第一頁桌面完成。

python爬蟲之Scrapy架構,基本介紹使用以及用架構下載下傳圖檔案例

六、小結

除了 ImagesPipeline 處理圖檔外,還有 FilesPipeline 可以處理檔案,使用方法與圖檔類似,事實上 ImagesPipeline 是 FilesPipeline 的子類,因為圖檔也是檔案的一種。

Scrapy很強大,對于大型網站非常實用,還可以同時運作多個爬蟲程式,提升效率。Scrapy還有很多功能,可以自己研究。