爬蟲之scrapy框架

1.Scrapy框架簡介

1.1 Scrapy框架介紹

寫一個爬蟲,須要作不少的事情。好比:發送網絡請求、數據解析、數據存儲、反反爬蟲機制(更換ip代理、設置請求頭等)、異步請求等。這些工做若是每次都要本身從零開始寫的話,比較浪費時間。所以Scrapy把一些基礎的東西封裝好了,在他上面寫爬蟲能夠變的更加的高效(爬取效率和開發效率)。所以真正在公司裏,一些上了量的爬蟲,都是使用Scrapy框架來解決。php

1.2 Scrapy架構圖

 

Scrapy框架模塊功能:css

  1. Scrapy Engine(引擎)Scrapy框架的核心部分。負責在SpiderItemPipelineDownloaderScheduler中間通訊、傳遞數據等。
  2. Spider(爬蟲):發送須要爬取的連接給引擎,最後引擎把其餘模塊請求回來的數據再發送給爬蟲,爬蟲就去解析想要的數據。這個部分是咱們開發者本身寫的,由於要爬取哪些連接,頁面中的哪些數據是咱們須要的,都是由程序員本身決定。
  3. Scheduler(調度器):負責接收引擎發送過來的請求,並按照必定的方式進行排列和整理,負責調度請求的順序等。
  4. Downloader(下載器):負責接收引擎傳過來的下載請求,而後去網絡上下載對應的數據再交還給引擎。
  5. Item Pipeline(管道):負責將Spider(爬蟲)傳遞過來的數據進行保存。具體保存在哪裏,應該看開發者本身的需求。
  6. Downloader Middlewares(下載中間件):能夠擴展下載器和引擎之間通訊功能的中間件。你可用該中間件作如下幾件事
    1. )process a request just before it is sent to the Downloader (i.e. right before Scrapy sends the request to the website);
    2. )change received response before passing it to a spider;
    3. )send a new Request instead of passing received response to a spider;
    4. )   pass response to a spider without fetching a web page;
  7. Spider Middlewares(Spider中間件):能夠擴展引擎和爬蟲之間通訊功能的中間件。

在Scrapy的數據流是由執行引擎控制,具體流程以下:html

  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,引擎關閉該網站。

2. Scrapy框架快速入門

2.1 scrapy安裝與文檔

  1. 安裝:經過pip install scrapy便可安裝。
  2. Scrapy官方文檔:http://doc.scrapy.org/en/latest
  3. Scrapy中文文檔:http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html
#Windows平臺
    一、pip3 install wheel #安裝後,便支持經過wheel文件安裝軟件,wheel文件官網:https://www.lfd.uci.edu/~gohlke/pythonlibs
    3、pip3 install lxml
    4、pip3 install pyopenssl
    五、下載並安裝pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
    六、下載twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    七、執行pip3 install 下載目錄\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
    8、pip3 install scrapy
  
#Linux平臺
    1、pip3 install scrapy

注意:
若是windows環境下直接經過pip install scrapy命令行安裝,報如下錯誤時:   在windows系統下,提示錯誤ModuleNotFoundError: No module named
'win32api',那麼使用如下命令能夠解決:pip install pypiwin32。   在windows系統下,提示錯誤:error: Microsoft Visual C++ 14.0 is required.那麼能夠在網址https://www.lfd.uci.edu/~gohlke/pythonlibs/下載Twisted模塊,安裝該模塊後,再從新使用命令行安裝pip install scrapy安裝scrapy模塊

2.2 建立項目和爬蟲

2.2.1 建立項目python

要使用Scrapy框架建立項目,須要經過命令來建立。首先進入到你想把這個項目存放的目錄。而後使用如下命令建立:程序員

scrapy startproject [項目名稱]

2.2.2 建立爬蟲web

建立爬蟲:進入到項目所在的路徑,執行命令「scrapy genspider [爬蟲名] [爬蟲域名]」ajax

注意:爬蟲名不能和建立的項目名一致正則表達式

建立完項目和爬蟲後,目錄結構以下圖:shell

項目中主要文件的做用:json

  1. items.py:用來存放爬蟲爬取下來數據的模型。
  2. middlewares.py:用來存放各類中間件的文件。
  3. pipelines.py:用來將items的模型存儲到本地磁盤中。
  4. settings.py:本爬蟲的一些配置信息(好比請求頭、多久發送一次請求、ip代理池等)。
  5. scrapy.cfg:項目的配置文件。
  6. spiders包:之後全部的爬蟲,都是存放到這個裏面。

2.3 Scrapy命令行工具

#1 查看幫助
    scrapy -h
    scrapy <command> -h

#2 有兩種命令:其中Project-only必須切到項目文件夾下才能執行,而Global的命令則不須要
    Global commands:
        startproject #建立項目
        genspider    #建立爬蟲程序
        settings     #若是是在項目目錄下,則獲得的是該項目的配置
        runspider    #運行一個獨立的python文件,沒必要建立項目
        shell        #scrapy shell url地址  在交互式調試,如選擇器規則正確與否
        fetch        #獨立於程單純地爬取一個頁面,能夠拿到請求頭
        view         #下載完畢後直接彈出瀏覽器,以此能夠分辨出哪些數據是ajax請求
        version      #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依賴庫的版本
    Project-only commands:
        crawl        #運行爬蟲,必須建立項目才行,確保配置文件中ROBOTSTXT_OBEY = False
        check        #檢測項目中有無語法錯誤
        list         #列出項目中所包含的爬蟲名
        edit         #編輯器,通常不用
        parse        #scrapy parse url地址 --callback 回調函數  #以此能夠驗證咱們的回調函數是否正確
        bench        #scrapy bentch壓力測試

#3 官網連接
    https://docs.scrapy.org/en/latest/topics/commands.html

2.4 使用Scrapy框架爬取糗事百科段子

2.4.1 使用命令建立一個爬蟲

scrapy gensipder qsbk_spider "qiushibaike.com"

建立了一個名字叫作qsbk_spider的爬蟲,而且能爬取的網頁只會限制在qiushibaike.com這個域名下。

2.4.2 爬蟲代碼解析

# -*- coding: utf-8 -*-
import scrapy


class QsbkSpiderSpider(scrapy.Spider):
    name = 'qsbk_spider'
    allowed_domains = ['qiushibaike.com']
    start_urls = ['http://qiushibaike.com/']

    def parse(self, response):
        pass
qsbk_spider.py

其實這些代碼咱們徹底能夠本身手動去寫,而不用命令。只不過是不用命令,本身寫這些代碼比較麻煩。
要建立一個Spider,那麼必須自定義一個類,繼承自scrapy.Spider,而後在這個類中定義三個屬性和一個方法。

  1. name:這個爬蟲的名字,名字必須是惟一的。
  2. allow_domains:容許的域名。爬蟲只會爬取這個域名下的網頁,其餘不是這個域名下的網頁會被自動忽略。
  3. start_urls:爬蟲從這個變量中的url開始。
  4. parse:引擎會把下載器下載回來的數據扔給爬蟲解析,爬蟲再把數據傳給這個parse方法。這個是個固定的寫法。這個方法的做用有兩個,第一個是提取想要的數據。第二個是生成下一個請求的url。

2.4.3 修改settings.py代碼

在作一個爬蟲以前,必定要記得修改setttings.py中的設置。兩個地方是強烈建議設置的。

  1. ROBOTSTXT_OBEY設置爲False。默認是True。即遵照機器協議,那麼在爬蟲的時候,scrapy首先去找robots.txt文件,若是沒有找到。則直接中止爬取。
  2. DEFAULT_REQUEST_HEADERS添加User-Agent。這個也是告訴服務器,我這個請求是一個正常的請求,不是一個爬蟲。

2.4.4 完成的爬蟲代碼

  1. 爬蟲部分代碼:

    # -*- coding: utf-8 -*-
    import scrapy
    from qsbk.items import QsbkItem
    
    class QsbkSpiderSpider(scrapy.Spider):
        name = 'qsbk_spider'
        allowed_domains = ['qiushibaike.com']
        start_urls = ['https://www.qiushibaike.com/text/page/1/']
        base_domain = "https://www.qiushibaike.com"
    
        def parse(self, response):
            duanzidivs = response.xpath('//div[@id="content-left"]/div')
            for duanzidiv in duanzidivs:
                author = duanzidiv.xpath('.//h2/text()').get().strip()
                content = duanzidiv.xpath('.//div[@class="content"]//text()').getall()
                content = ''.join(content).strip()
                item = QsbkItem(author=author,content=content)
                yield item
            next_url = response.xpath('//ul[@class="pagination"]/li[last()]/a/@href').get()
            if not next_url:
                return
            else:
                yield scrapy.Request(self.base_domain+next_url,callback= self.parse)
    qsbk_spider.py
  2. items.py部分代碼:

     import scrapy
     class QsbkItem(scrapy.Item):
         author = scrapy.Field()
         content = scrapy.Field()
  3. pipeline部分代碼:

     -*- coding: utf-8 -*-
    
    # Define your item pipelines here
    #
    # Don't forget to add your pipeline to the ITEM_PIPELINES setting
    # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
    import json
    
    class QsbkPipeline(object):
        def __init__(self):
            self.fp = open('duanzi.json','w',encoding='utf-8')
        def open_spider(self,spider):
            pass
        def process_item(self, item, spider):
            item_json = json.dumps(dict(item),ensure_ascii=False)
            self.fp.write(item_json+'\n')
            return item
    
        def close_spider(self,spider):
            self.fp.close()
    pipelines.py

2.4.5 運行scrapy項目

  運行scrapy項目。須要在終端,進入項目所在的路徑,而後scrapy crawl [爬蟲名字]便可運行指定的爬蟲。若是不想每次都在命令行中運行,那麼能夠把這個命令寫在一個文件中。之後就在pycharm中執行運行這個文件就能夠了。好比如今新建立一個文件叫作start.py,而後在這個文件中填入如下代碼:

from scrapy import cmdline

cmdline.execute(["scrapy","crawl","qsbk_spider"])
#cmdline.execute("scrapy crawl qsbk_spider".split())

 2.4.6 糗事百科scrapy爬蟲筆記

  1.parse(self, response)中的response是一個'scrapy.http.response.html.HtmlResponse'對象。能夠執行'xpath'和'css'語法來提取數據。

  2.提出出來的數據是一個'Selector'或者'Selectorlist'對象,若是想要獲取其中的字符串,能夠經過getall()或者get()方法。

  3.getall()方法獲取‘Selector’中的全部文本,返回的是一個列表。get()方法獲取的是‘Selector’中的第一個文本,返回的是一個str類型。

  4.若是數據解析完成後,要傳給pipline處理,可使用'yield'來返回,或者是收取全部的item,組合成一個列表,最後統一使用return返回。

  5.item:建議在'items.py'中定義好要傳遞的數據。

  6.pipeline:這個是專門用來保存數據的,其中有三個方法常常會用到。

  *  "open_spider(self,spider)":當爬蟲被打開時執行。

  *  "process_item(self,item,spider)":當爬蟲有item傳過來時被調用。

  *  "close_spider(self,spider)":當爬蟲關閉的時候被調用。

  要激活pipeline,須要在'settings.py中設置"ITEM_PIPELINES"。示例以下:

  ITEM_PIPELINES = {
  'qsbk.pipelines.QsbkPipeline': 300,
    }

   7.保存json數據的時候,可使用JsonItemExporter和JsonLinesItemExporter類,優化數據存儲方式

  *  JsonItemExporter:每次把數據添加到內存中,最後統一寫入到磁盤。優勢:存儲的數據是一個知足json規則的數據。缺點:當數據量很大的時候比較耗內存。示例代碼以下:

from scrapy.exporters import JsonItemExporter

class QsbkPipeline(object):
    def __init__(self):
        self.fp = open('duanzi.json','wb')
        self.exporter = JsonItemExporter(self.fp,ensure_ascii = False,encoding = 'utf-8')
        self.exporter.start_exporting()
    def open_spider(self,spider):
        print('爬蟲開始了...')
    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

    def close_spider(self,spider):
        self.exporter.finish_exporting()
        self.fp.close()
        print('爬蟲結束')
View Code

  *  JsonLinesItemExporter:這個是每次調用export_item的時候就把這個item存儲到硬盤中,缺點:每一個字典是一行,整個文件不是一個知足json格式的文件。優勢:每次處理數據的時候就直接存儲到硬盤中,這樣不會耗內存,數據也比較安全。示例代碼以下:

from scrapy.exporters import JsonLinesItemExporter
class QsbkPipeline(object):
    def __init__(self):
        self.fp = open('duanzi.json','wb')
        self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii = False,encoding = 'utf-8')
    def open_spider(self,spider):
        print('爬蟲開始了...')
    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

    def close_spider(self,spider):
        self.fp.close()
        print('爬蟲結束')
View Code

3. CrawlSpider

在上一個糗事百科的爬蟲案例中。咱們是本身在解析完整個頁面後獲取下一頁的url,而後從新發送一個請求。有時候咱們想要這樣作,只要知足某個條件的url,都給我進行爬取。那麼這時候咱們就能夠經過CrawlSpider來幫咱們完成了。CrawlSpider繼承自Spider,只不過是在以前的基礎之上增長了新的功能,能夠定義爬取的url的規則,之後scrapy碰到知足條件的url都進行爬取,而不用手動的yield Request

3.1 CrawlSpider爬蟲

3.1.1建立CrawlSpider爬蟲

以前建立爬蟲的方式是經過scrapy genspider [爬蟲名字] [域名]的方式建立的。若是想要建立CrawlSpider爬蟲,那麼應該經過如下命令建立:

scrapy genspider -t crawl [爬蟲名字] [域名]

3.1.2 LinkExtractors連接提取器

使用LinkExtractors能夠不用程序員本身提取想要的url,而後發送請求。這些工做均可以交給LinkExtractors,他會在全部爬的頁面中找到知足規則的url,實現自動的爬取。如下對LinkExtractors類作一個簡單的介紹:

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:容許的url。全部知足這個正則表達式的url都會被提取。
  • deny:禁止的url。全部知足這個正則表達式的url都不會被提取。
  • allow_domains:容許的域名。只有在這個裏面指定的域名的url纔會被提取。
  • deny_domains:禁止的域名。全部在這個裏面指定的域名的url都不會被提取。
  • restrict_xpaths:嚴格的xpath。和allow共同過濾連接。

3.1.3 Rule規則類

定義爬蟲的規則類。如下對這個類作一個簡單的介紹:

class scrapy.spiders.Rule(
    link_extractor, 
    callback = None, 
    cb_kwargs = None, 
    follow = None, 
    process_links = None, 
    process_request = None
)

主要參數講解:

  • link_extractor:一個LinkExtractor對象,用於定義爬取規則。
  • callback:知足這個規則的url,應該要執行哪一個回調函數。由於CrawlSpider使用了parse做爲回調函數,所以不要覆蓋parse做爲回調本身的回調函數。
  • follow:指定根據該規則從response中提取的連接是否須要跟進。
  • process_links:從link_extractor中獲取到連接後會傳遞給這個函數,用來過濾不須要爬取的連接。

 3.2 CrawlSpider 爬取微信小程序社區內容

 爬蟲部分代碼:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from wxapp.items import WxappItem


class WxAppSpider(CrawlSpider):
    name = 'wx_app'
    allowed_domains = ['wxapp-union.com']
    start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']

    rules = (
        Rule(LinkExtractor(allow=r'.+mod=list&catid=2&page=\d'),  follow=True),
        Rule(LinkExtractor(allow=r'.+article-.+\.html'),callback="parse_detail",follow=False),
    )

    def parse_detail(self, response):
        title = response.xpath('//h1[@class="ph"]/text()').get()
        author_p = response.xpath('//p[@class="authors"]')
        author  =  author_p.xpath('.//a/text()').get()
        pub_time = author_p.xpath('.//span/text()').get()
        content = response.xpath('//td[@id="article_content"]//text()').getall()
        content = ''.join(content).strip()
        item = WxappItem(title=title,author=author,pub_time=pub_time,content=content)
        #item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
        #item['name'] = response.xpath('//div[@id="name"]').get()
        #item['description'] = response.xpath('//div[@id="description"]').get()
        yield item
spiders//wxapp.py

items.py部分代碼:

import scrapy


class WxappItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    author = scrapy.Field()
    pub_time = scrapy.Field()
    content = scrapy.Field()
items.py

pipeline部分代碼:

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

from scrapy.exporters import JsonLinesItemExporter
class WxappPipeline(object):
    def __init__(self):
        self.fp = open('wxjc.json','wb')
        self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii = False,encoding = 'utf-8')
    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item
    def close_spider(self,spider):
        self.fp.close()
pipelines.py

 總結:

1. CrawlSpider使用"LinkExtractor"和Rule決定爬蟲的具體走向。

2. LinkExtractor參數 allow設置規則:設置的url正則表達式要可以限制在咱們想要的url上

3. Rule參數follow的設置:若是在爬取頁面的時候,須要將知足當前條件的url再進行跟進,那麼設置爲Ture,不然設置爲False。

4.何時指定callback:若是這個url對應的頁面,只是爲了獲取更多的url,並不須要裏面的數據,那麼能夠不指定callback,若是想要獲取url對應頁面中的數據,那麼就須要指定一個callback。

4. Scrapy 之Request對象和Response對象

4.1 Request對象

class Request(object_ref):

    def __init__(self, url, callback=None, method='GET', headers=None, body=None,
                 cookies=None, meta=None, encoding='utf-8', priority=0,
                 dont_filter=False, errback=None, flags=None):

        self._encoding = encoding  # this one has to be set first
        self.method = str(method).upper()
        self._set_url(url)
        self._set_body(body)
        assert isinstance(priority, int), "Request priority not an integer: %r" % priority
        self.priority = priority

        if callback is not None and not callable(callback):
            raise TypeError('callback must be a callable, got %s' % type(callback).__name__)
        if errback is not None and not callable(errback):
            raise TypeError('errback must be a callable, got %s' % type(errback).__name__)
        assert callback or not errback, "Cannot use errback without a callback"
        self.callback = callback
        self.errback = errback

        self.cookies = cookies or {}
        self.headers = Headers(headers or {}, encoding=encoding)
        self.dont_filter = dont_filter

        self._meta = dict(meta) if meta else None
        self.flags = [] if flags is None else list(flags)

Request對象在咱們寫爬蟲爬取一頁數據後須要從新發送一個請求的時候調用,這個類須要傳遞一些參數,其中比較經常使用的參數有:

  1. url:這個是request對象發送請求的url

  2. callback:在下載器下載完成相應的數據後執行回調函數

  3. method:請求的方法,默認爲GET方法,能夠設置爲其餘方法

  4. headers:請求頭,對於一些固定的設置,放在settings.py中指定就能夠,對於非固定的請求頭,能夠在發送請求的時候指定。

  5. cookies (dict or list) :請求的cookies。能夠被設置成以下兩種形式。

    1. )Using a dict:
      request_with_cookies = Request(url="http://www.example.com", cookies={'currency': 'USD', 'country': 'UY'}) 
    2. )Using a list of dicts:
      request_with_cookies = Request(url="http://www.example.com", cookies=[{'name': 'currency', 'value': 'USD', 'domain': 'example.com', 'path': '/currency'}])

  6. meta:比較經常使用,用於在不一樣的請求之間傳遞數據

  7. encoding:編碼,默認爲utf-8

  8. dot_filter:表示不禁調度器過濾,在執行屢次重複的請求的時候用的較多,設置爲True,可避免重複發送相同的請求

  9. errback:在發送錯誤的時候執行的函數

發送Post請求:

  有時候咱們想要在請求數據的時候發送post請求,那麼這時候須要使用Request的子類FormRequest來實現。若是想要在爬蟲一開始的時候就發送Post請求,那麼須要在爬蟲類中重寫start_requests(self)方法,而且再也不調用start_urls裏的url.

4.2 Response對象

 Response對象通常由Scrapy自動構建,所以開發者不須要關心如何建立Response對象。Response對象有不少屬性,經常使用屬性以下:

  1. meta:從其餘請求傳過來的meta屬性,能夠用來保持多個請求之間的數據鏈接

  2. encoding:返回當前字符串編碼和解碼格式

  3. text:將返回的數據做爲unicode字符串返回

  4. body:將返回的數據做爲bytes字符串返回

  5. xpath:xpath選擇器

  6. css:css選擇器

4.3 使用Request對象模式登陸

4.3.1 模擬登陸人人網

 爬蟲部分代碼:

# -*- coding: utf-8 -*-
import scrapy

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

    def start_requests(self):
        # 在爬蟲一開始發送Post請求,須要重寫start_requests(self)方法
        #發送post請求,推薦使用scrapy.FormRequest方法,能夠指定表單數據
        url = 'http://www.renren.com/PLogin.do'
        data = {'email':'970138074@qq.com','password':'pythonspider'}
        request = scrapy.FormRequest(url,formdata=data,callback=self.parse_page)
        yield request

    def parse_page(self,response):
        request = scrapy.Request(url = 'http://www.renren.com/443362311/profile',
                                 callback=self.parse_profile)
        yield request

    def parse_profile(self,response):
        with open('dp.html','w',encoding='utf-8') as fp:
            fp.write(response.text)
renren.py

5. 下載文件和圖片

 5.1 實戰-汽車之家寶馬5系圖片下載爬蟲

1) 經過本身寫圖片下載程序下載汽車之家寶馬5系圖片,示例以下:

 爬蟲部分代碼:

# -*- coding: utf-8 -*-
import scrapy
from BM5X.items import Bm5XItem


class Bm5xSpider(scrapy.Spider):
    name = 'bm5x'
    allowed_domains = ['https://car.autohome.com.cn/']
    start_urls = ['https://car.autohome.com.cn/pic/series/65.html']

    def parse(self, response):
        uniboxs = response.xpath("//div[@class='uibox']")[1:]
        for unibox in uniboxs:
            category = unibox.xpath(".//div[@class='uibox-title']/a/text()").get()
            urls = unibox.xpath(".//ul/li/a/img/@src").getall()
            urls = list(map(lambda url:response.urljoin(url),urls))
            item = Bm5XItem(category=category,urls=urls)
            yield item
spiders//bm5x.py

items.py部分代碼:

import scrapy


class Bm5XItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    category = scrapy.Field()
    urls = scrapy.Field()
items.py

pipeline部分代碼:

import os
from urllib import request

class Bm5XPipeline(object):
    def __init__(self):
        BATH = os.path.dirname(os.path.dirname(__file__))
        self.path = os.path.join(BATH,'images')
        if not os.path.exists(self.path):
            os.makedirs(self.path)

    def process_item(self, item, spider):
        category = item['category']
        urls = item['urls']
        category_path = os.path.join(self.path,category)
        if not os.path.exists(category_path):
            os.makedirs(category_path)
        for url in urls:
            image_name = url.split('_')[-1]
            request.urlretrieve(url,os.path.join(category_path,image_name))

        return item
pipelines.py

 2) 使用scrapy 內置下載文件方法下載汽車之家寶馬5系圖片,示例以下:

 Scrapy爲下載item中包含的文件(好比在爬取到產品時,同時也想保存對應的圖片)提供了一個可重用的item pipelines。這些pipelines有些共同的方法和結構(咱們稱之爲media pipeline)。通常來講你會使用Files pipline 或者Images pipline。

爲何要選擇使用scrapy內置的下載文件的方法:

  1) 避免從新下載最近已經下載過的數據

  2) 能夠方便的指定文件存儲的路徑

  3) 能夠將下載的圖片轉換爲通用的格式,好比png或jpg

  4) 能夠方便的生成縮略圖

  5) 能夠方便的檢測圖片的寬和高,確保他們知足最小的限制

  6) 異步下載,效率很是高

下載文件的Files Pipeline:

當使用Files Pipline 下載文件的時候,按照如下步驟來完成:

  1) 定義好一個Item,而後在這個item中定義兩個屬性,分別爲file_urls以及files。file_urls是用來存儲須要下載的文件的url連接,須要給一個列表。

  2) 當文件下載完成後,會把文件下載的相關信息存儲到item的files屬性中,好比下載路徑和文件校驗碼等。

  3) 在配置文件setting.py中配置FILES_STORE,這個配置是用來設置文件下載下來的路徑。

  4) 啓動pipline,在ITEM_PIPELINES中設置

  ITEM_PIPELINES = {
  # 'BM5X.pipelines.Bm5XPipeline': 300,
  'scrapy.pipelines.files.FilesPipeline':1
  }

下載圖片的Images Pipeline:

當使用Images Pipline下載文件的時候,按照如下步驟來完成:

  1) 定義好一個Item,而後在這個item中定義兩個屬性,分別爲image_urls以及images。image_urls是用來存儲須要下載的圖片的url連接,須要給一個列表。

  2) 當文件下載完成後,會把文件下載的相關信息存儲到item的images屬性中,以下下載路徑,下載的url和圖片的校驗碼等。

  3) 在配置文件settings.py中配置IMAGES_STORE,這個配置是用來設置圖片下載下來的路徑。

  4) 啓動pipline ,在ITEM_PIPELINES中設置

  ITEM_PIPELINES = {
  # 'BM5X.pipelines.Bm5XPipeline': 300,
  'scrapy.pipelines.images.ImagesPipeline':1
  }

  爬蟲部分代碼:

# -*- coding: utf-8 -*-
import scrapy
from BM5X.items import Bm5XItem


class Bm5xSpider(scrapy.Spider):
    name = 'bm5x'
    allowed_domains = ['https://car.autohome.com.cn/']
    start_urls = ['https://car.autohome.com.cn/pic/series/65.html']

    def parse(self, response):
        uniboxs = response.xpath("//div[@class='uibox']")[1:]
        for unibox in uniboxs:
            category = unibox.xpath(".//div[@class='uibox-title']/a/text()").get()
            urls = unibox.xpath(".//ul/li/a/img/@src").getall()
            urls = list(map(lambda url:response.urljoin(url),urls))
            item = Bm5XItem(category=category,image_urls=urls)
            yield item
spiders//bm5x.py

items.py部分代碼:

import scrapy


class Bm5XItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    category = scrapy.Field()
    image_urls = scrapy.Field()
    images = scrapy.Field()
items.py

pipeline部分代碼:

import os
from scrapy.pipelines.images import ImagesPipeline
from BM5X import settings

class BM5XImagesPipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
        #這個方法是在發送下載請求以前調用
        #其實這個方法自己就是去發送下載請求的
        request_objs = super(BM5XImagesPipeline,self).get_media_requests(item,info)
        for request_obj in request_objs:
            request_obj.item = item
        return request_objs
    def file_path(self, request, response=None, info=None):
        #這個方法是在圖片將要被保存的時候調用,來獲取這個圖片存儲的路徑
        path = super(BM5XImagesPipeline,self).file_path(request,response,info)
        category = request.item.get('category')
        images_store = settings.IMAGES_STORE
        category_path = os.path.join(images_store,category)
        if not os.path.exists(category_path):
            os.makedirs(category_path)
        image_name = path.replace('full/','')
        image_path = os.path.join(category_path,image_name)
        return image_path
View Code

setting部分代碼:

# -*- coding: utf-8 -*-

# Scrapy settings for BM5X project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://doc.scrapy.org/en/latest/topics/settings.html
#     https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://doc.scrapy.org/en/latest/topics/spider-middleware.html
import os
BOT_NAME = 'BM5X'

SPIDER_MODULES = ['BM5X.spiders']
NEWSPIDER_MODULE = 'BM5X.spiders'


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'BM5X (+http://www.yourdomain.com)'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
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/73.0.3683.103 Safari/537.36',
}

# Enable or disable spider middlewares
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    'BM5X.middlewares.Bm5XSpiderMiddleware': 543,
#}

# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    'BM5X.middlewares.Bm5XDownloaderMiddleware': 543,
#}

# Enable or disable extensions
# See https://doc.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    'scrapy.extensions.telnet.TelnetConsole': None,
#}

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
  # 'BM5X.pipelines.Bm5XPipeline': 300,
    'BM5X.pipelines.BM5XImagesPipeline':1
}

# Enable and configure the AutoThrottle extension (disabled by default)
# See https://doc.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
#圖片下載路徑,供images pipline使用
IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)),'images')
settings.py

6. 下載器中間件(DownLoader Middlewares)

下載器中間件是引擎和下載器之間通訊的中間件。在這個中間件中咱們能夠設置代理、更換請求頭等來達到反反爬蟲的目的。要寫下載器中間件,能夠在下載器中實現兩個方法,一個是process_request(self,request,spider),這個方法是在請求發送以前執行,還有一個是process_response(self,request,response,spider),這個方法是數據下載到引擎以前執行。

process_request(self,request,spider):

這個方法是下載器在發送請求以前執行,通常能夠在這個裏面設置隨機代理ip等。

1)參數:

  request: 發送請求的request對象。

  spider: 發送請求的spider對象。

2)返回值:

  返回None:若是返回None,Scrapy將繼續處理該request,執行其餘中間件中的相應方法,直到合適的下載器處理函數被調用

  返回Response對象:Scrapy 將不會調用任何其餘的process_request方法,將直接返回這個response對象。已經激活的中間件process_response()方法則會在每一個response返回時被調用

  返回Request對象:再也不使用以前的request對象去下載數據,而是根據如今返回的request對象返回數據。

  若是這個方法中出現了異常,則會調用process_exception方法

process_response(self,request,response,spider):

這個是下載器下載數據到引擎過程當中執行的方法

1)參數:

  request:request 對象

  response:被處理的response對象

  spider:spider對象

2)返回值:

  返回Response對象:會將這個新的response對象傳給其餘中間件,最終傳給爬蟲

  返回Request對象:下載器鏈被切斷,返回的request會從新被下載器調度下載

  若是拋出一個異常,那麼調用request的errback方法,若是沒有指定這個方法,那麼會拋出一個異常

 

 

 

 

 

>>>>>>待續

相關文章
相關標籤/搜索