Scrapy框架的使用之Scrapy入門

接下來介紹一個簡單的項目,完成一遍Scrapy抓取流程。經過這個過程,咱們能夠對Scrapy的基本用法和原理有大致瞭解。

一,準備工做

本節要完成的任務以下。

  • 建立一個Scrapy項目。

  • 建立一個蜘蛛來抓取站點和處理數據。

  • 經過命令行將抓取的內容導出。

  • 將抓取的內容保存的到的MongoDB數據庫。

二,準備工做

咱們須要安裝好Scrapy框架,MongoDB的和PyMongo庫。

三,建立項目

建立一個Scrapy項目,文件項目能夠直接用
scrapy
命令生成,命令以下所示:

scrapy startproject教程複製代碼

這個命令能夠在任意文件夾運行若是提示權限問題,能夠加須藤運行該命令這個命令將會建立一個名爲教程的文件夾,文件夾結構以下所示。:

scrapy.cfg#Scrapy部署時的配置文件
教程#項目的模塊,須要從這裏引入
    __init__.py    
    items.py#Items的定義,定義爬取的數據結構
    middlewares.py#Middlewares的定義,定義爬取時的中間件
    pipelines.py#管道的定義,定義數據管道
    settings.py#配置文件
    蜘蛛#放置蜘蛛的文件夾
    __init__.py複製代碼

四,建立蜘蛛

蜘蛛是本身定義的類,Scrapy用它來從網頁裏抓取內容,並解析抓取的結果。不過這個類必須繼承Scrapy提供的蜘蛛類
scrapy.Spider
,還要定義蜘蛛的名稱和起始請求,以及怎樣處理爬取後的結果的方法。

也可使用命令行建立一個蜘蛛好比要生成行情這個蜘蛛,能夠執行以下命令:

光盤教程
scrapy genspider報價複製代碼

進入剛纔建立的教程文件夾,執行而後
genspider
命令。第一個參數是蜘蛛的名稱,第二個參數是網站域名。執行完畢以後,蜘蛛文件夾中多了一個quotes.py,它就是剛剛建立的蜘蛛,內容以下所示:

import scrapy 

class  QuotesSpider (scrapy.Spider):
     name = 「quotes」
     allowed_domains = [ 「quotes.toscrape.com」 ] 
    start_urls = [ 'http://quotes.toscrape.com/' ] 

    def  parse (self,response):
        經過複製代碼

這裏有三個屬性 -
name
allowed_domains
start_urls
,還有一個方法
parse

  • namecss

    ,它是每一個項目惟一的名字,用來區分不一樣的蜘蛛。

  • allowed_domainshtml

    ,它是容許爬取的域名,若是初始或後續的請求連接不是這個域名下的,則請求連接會被過濾掉。

  • start_urlsgit

    ,它包含了蜘蛛在啓動時爬取的URL列表,初始請求是由它來定義的。

  • parsegithub

    ,它是蜘蛛的一個方法。默認狀況下,調用被時
    start_urls
    裏面的連接構成的請求完成下載執行後,返回的響應就會做爲惟一的參數傳遞給這個函數
    。該方法負責解析返回的響應,提取數據或者進一步生成要處理的請求。

五,建立項目

項目是保存爬取數據的容器,它的使用方法和字典相似。不過,相比字典,項目多了額外的保護機制,能夠避免拼寫錯誤或者定義字段錯誤。

建立項目繼承須要
scrapy.Item
類,定義而且類型爲
scrapy.Field
的字段。觀察目標網站,咱們能夠獲取到到內容有
text
author
tags

定義項,此時將items.py修改以下:

import scrapy 

class  QuoteItem (scrapy.Item):

     text = scrapy.Field()
    author = scrapy.Field()
    tags = scrapy.Field()複製代碼

這裏定義了三個字段,接下來爬取時咱們會使用到這個項目。

六,解析響應

上文中咱們看到,
parse()
方法的參數
resposne
的英文
start_urls
裏面的連接爬取後的查詢查詢結果。在因此
parse
方法中,能夠咱們直接對
response
變量全部遊戲的內容進行解析,好比瀏覽請求結果的網頁源代碼,或者進一步分析源代碼內容,或者找出結果中的連接而獲得下一個請求

咱們能夠看到網頁中既有咱們想要的結果,又有下一頁的連接,這兩部份內容咱們都要進行處理。

首先看看網頁結構,以下圖所示。頁每一都有多個
class
quote
的區塊,每一個區塊內都包含
text
, ,
author
tags
那麼咱們先找出全部的
quote
,提取而後每個
quote
中的內容。

。提取的方式能夠是CSS選擇器或XPath的選擇器在這裏咱們使用CSS選擇器進行選擇,
parse()
方法的改寫以下所示:

高清 解析(個體經營,響應):
     報價= response.css( '.quote' )
    的報價在報價:
        文本= quote.css( ':: .text區段文本').extract_first()
        做者= quote.css( 」。 author'text ').extract_first()
        tags = quote.css( '.tags .tag :: text').extract()複製代碼

這裏首先利用選擇器選取全部的報價,其並將賦值爲
quotes
變量,利用而後
for
循環對每一個
quote
遍歷,每一個解析
quote
的內容。

text
來講,到觀察它的
class
text
,因此用能夠
.text
選擇器來選取,這個結果其實是整個帶有標籤的節點
,要獲取它的正文內容,加能夠
::text
來電子雜誌。這時的結果是長度爲1的列表,還需因此要用
extract_first()
方法來第電子雜誌一個元素。對於而
tags
來講,因爲咱們要獲取全部的標籤,因此用
extract()
方法電子雜誌整個列表便可。

第以一個
quote
的查詢查詢結果爲例,各個選擇方法及結果的說明以下內容

源碼以下:

< div  class = 「quote」  itemscope = 「」  itemtype = 「http://schema.org/CreativeWork」 > 
        < span  class = 「text」  itemprop = 「text」 > 「咱們創造它的世界是一個過程,咱們的想法。「 </ span > 
        < span > by < small  class = 」author「  itemprop = 」author「 >阿爾伯特愛因斯坦<
        一個 HREF = 「/做者/阿爾伯特-愛因斯坦」 >(約) </ 一 > 
        </ 跨度 > 
        < DIV  類 = 「標籤」 >
             標籤:
             < 元 類 = 「關鍵詞」  itemprop = 「關鍵詞」  內容 = 「變化,深的思想,思惟,世界」 >  
            < 一 類 = 「標籤」  HREF = 「/標籤/變動/頁/ 1 /」 >變動 </ 一 > 
            <一個 class =「標記」  HREF = 「/標記/深想法/頁面/ 1 /」 >深思想 </ 一 > 
            < 一 類 = 「標籤」  HREF = 「/標籤/思惟/頁面/ 1 /」 >思 </ 一 > 
            < 一 類 = 「標籤」  HREF = 「/標籤/世界/頁面/ 1 /」 >世界 </ 一 > 
        </ DIV > 
    </ DIV >複製代碼

不一樣選擇器的返回結果以下內容。

1。 quote.css('.text')

[<Selector xpath =「descendant-or-self :: * [@ class and contains(concat('',normalize-space(@class),''),'text')]」data ='<span class = 「text」itemprop =「text」>「The'>]複製代碼

2。 quote.css('.text::text')

[<Selector xpath =「descendant-or-self :: * [@ class and contains(concat('',normalize-space(@class),''),'text')] / text()」data =' 「咱們創造它的世界是一個公關>]複製代碼

3。 quote.css('.text').extract()

['span class =「text」itemprop =「text」>「咱們創造它的世界是咱們思考的過程。不改變咱們的想法就沒法改變。「</ span>']複製代碼

4。 quote.css('.text::text').extract()

[''咱們創造的世界是咱們思考的過程。不改變咱們的想法就沒法改變。「']複製代碼

5。 quote.css('.text::text').extract_first()

「咱們創造它的世界是咱們思考的過程。若是不改變咱們的想法,它就不能改變。「複製代碼

因此,對於
text
,獲取結果的第一個元素便可,使用因此
extract_first()
方法,對於
tags
,要獲取全部結果組成的列表,使用因此
extract()
方法。

七,使用項目

上文定義了項目,接下來就要使用它了.Item能夠理解爲一個字典,不過在聲明的時候須要實例化。而後依次用剛纔解析的結果賦值項目的每個字段,最後將產品返回便可。

QuotesSpider數據庫

的改寫以下所示:

進口 scrapy 
從 tutorial.items 導入 QuoteItem 

類 QuotesSpider (scrapy.Spider) :
     名稱= 「引號」
     allowed_domains = [ 「quotes.toscrape.com」 ] 
    start_urls = [ 'http://quotes.toscrape.com/' ] 

    DEF  解析(個體,響應):
         報價= response.css('.quote' )
        用於引用在引號:
            項= QuoteItem()
            項[ '文本' ] = quote.css(':: .text區段文本')。extract_first() 
            項目['author' ] = quote.css('.author :: text').extract_first()
            item [ 'tags' ] = quote.css('.tags .tag :: text').extract()
            yield item複製代碼

如此一來,首頁的全部內容被解析出來,並被賦值成了一個個
QuoteItem

八,後續請求

上面的操做實現了從初始頁面抓取內容。那麼,下一頁的內容該如何抓取?這就須要咱們從當前頁面中找到信息來生成下一個請求,而後在下一個請求的頁面裏找到信息再構造再下一個請求。這樣循環往復迭代,從而實現整站的爬取。

將剛纔的頁面拉到最底部,以下圖所示。

這裏有一個接着按鈕查看它的源代碼,能夠發現它的連接是/頁/ 2 /,全連接就是:HTTP://quotes.toscrape.com/page/2,經過這個連接咱們就能夠構造下一個請求。

構造請求時須要用到scrapy.Request這裏咱們傳遞兩個參數-
url
callback
,這兩個參數的說明以下。

  • urljson

    :它是請求連接。

  • callbackbash

    :。它是回調函數當指定了該回調函數的請求完成以後
    ,獲取到響應,引擎會將該響應做爲參數傳遞給這個回調函數回調函數進行解析或生成下一個請求
    ,函數回調文如上的
    parse()
    所示。

因爲
parse()
就是解析
text
author
tags
的方法,而下一頁的結構和剛纔已經解析的頁面結構是同樣的
,因此能夠咱們再次使用
parse()
方法來作頁面解析。

接下來咱們要作的就是利用選擇器獲得下一頁連接並生成請求
,在
parse()
方法後追加以下的代碼:

next = response.css('.pager .next a :: attr(href)').extract_first()
url = response.urljoin(next)
yield scrapy.Request(url = url,callback = self.parse)複製代碼

第一句代碼首先經過CSS選擇器獲取下一個頁面的連接,即要獲取一個連接超中的
href
屬性。用到這裏了
::attr(href)
操做。再而後調用
extract_first()
方法電子雜誌內容。

句第二代碼調用了
urljoin()
方法,
urljoin()
方法能夠將相對URL構形成一個絕對的URL例如,獲取到的下一頁地址/是/第2頁。
urljoin()
方法處理後獲得的結果就是:HTTP://quotes.toscrape .COM /頁/ 2 /。

句第三代碼經過
url
狀語從句:
callback
變量構造了一個新的請求,函數回調
callback
依然使用
parse()
方法。這個請求完成後,會響應從新通過
parse
方法處理,獲得第二頁的解析結果,而後生成第二頁的下一頁,也就是第三頁的請求。這樣爬蟲就進入了一個循環,直到最後一頁。

經過幾行代碼,咱們就輕鬆實現了一個抓取循環,將每一個頁面的結果抓取下來了。

如今,以後改寫整個的
Spider
類以下所示:

進口 scrapy 
從 tutorial.items 導入 QuoteItem 

類 QuotesSpider (scrapy.Spider) :
     名稱= 「引號」
     allowed_domains = [ 「quotes.toscrape.com」 ] 
    start_urls = [ 'http://quotes.toscrape.com/' ] 

    DEF  解析(個體,響應):
         報價= response.css('.quote' )
        用於引用在引號:
            項= QuoteItem()
            項[ '文本' ] = quote.css(':: .text區段文本')。extract_first() 
            項目['author' ] = quote.css('.author :: text').extract_first()
            item [ 'tags' ] = quote.css('.tags .tag :: text').extract()
            yield item 

        next = response.css('.pager .next a :: attr(「href」)').extract_first()
        url = response.urljoin(next)
        yield scrapy.Request(url = url,callback = self.parse)複製代碼

九,運行

接下來,進入目錄,運行以下命令:

scrapy抓取報價複製代碼

就能夠看到Scrapy的運行結果了。

2017-02-19 13:37:20 [scrapy.utils.log]信息:Scrapy 1.3.0開始(bot:教程)
2017-02-19 13:37:20 [scrapy.utils.log]信息:重寫設置:{'NEWSPIDER_MODULE''tutorial.spiders''SPIDER_MODULES':['tutorial.spiders'],'ROBOTSTXT_OBEY':True ,'BOT_NAME''教程'}
2017-02-19 13:37:20 [scrapy.middleware]信息:啓用擴展:
[ 'scrapy.extensions.logstats.LogStats''scrapy.extensions.telnet.TelnetConsole''scrapy.extensions.corestats.CoreStats']
2017-02-19 13:37:20 [scrapy.middleware]信息:啓用下載中間件:
[ 'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware''scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware''scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware''scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware''scrapy.downloadermiddlewares.useragent.UserAgentMiddleware''scrapy.downloadermiddlewares.retry.RetryMiddleware''scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware''scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware''scrapy.downloadermiddlewares.redirect.RedirectMiddleware''scrapy.downloadermiddlewares.cookies.CookiesMiddleware''scrapy.downloadermiddlewares.stats.DownloaderStats']
2017-02-19 13:37:20 [scrapy.middleware]信息:啓用蜘蛛中間件:
[ 'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware''scrapy.spidermiddlewares.offsite.OffsiteMiddleware''scrapy.spidermiddlewares.referer.RefererMiddleware''scrapy.spidermiddlewares.urllength.UrlLengthMiddleware''scrapy.spidermiddlewares.depth.DepthMiddleware']
2017-02-19 13:37:20 [scrapy.middleware]信息:啓用項目管道:
[]
2017-02-19 13:37:20 [scrapy.core.engine]信息:蜘蛛打開
2017-02-19 13:37:20 [scrapy.extensions.logstats]信息:爬行0頁(0頁/分鐘),刮0項(0項/分鐘)
2017-02-19 13:37:20 [scrapy.extensions.telnet] DEBUG:Telnet控制檯監聽127.0.0.1:6023
2017-02-19 13:37:21 [scrapy.core.engine] DEBUG:Crawled(404)<GET http://quotes.toscrape.com/robots.txt>(referer:無)
2017-02-19 13:37:21 [scrapy.core.engine] DEBUG:Crawled(200)<GET http://quotes.toscrape.com/>(referer:無)
2017-02-19 13:37:21 [scrapy.core.scraper]調試:從<200 http://quotes.toscrape.com/刮掉>
{'做者':愛因斯坦愛因斯坦', '標籤':[u'change',u'deep-thoughts',u'thinking',u'world'], '文本':u'\ u201c咱們創造它的世界是咱們思考的過程。不改變咱們的想法就不能改變。\ u201d'} 2017-02-19 13:37:21 [scrapy.core.scraper]調試:從<200 http://quotes.toscrape.com/刮掉> {'做者':u'JK羅琳', 'tags':[u'abilities',u'choices'], '文本':你是咱們的選擇,哈利,這代表咱們真正的存在,遠遠超過咱們的能力。\ u201d'}
...
2017-02-19 13:37:27 [scrapy.core.engine]信息:關閉蜘蛛(完成)
2017-02-19 13:37:27 [scrapy.statscollectors]信息:傾銷Scrapy統計信息:
{'downloader / request_bytes':2859,
 'downloader / request_count':11,
 'downloader / request_method_count / GET':11,
 'downloader / response_bytes':24871,
 'downloader / response_count':11,
 'downloader / response_status_count / 200':10,
 'downloader / response_status_count / 404':1,
 'dupefilter / filtered':1,
 'finish_reason''完成''finish_time':datetime.datetime(2017,2,19,5,37,27,227438),
 'item_scraped_count':100,
 'log_count / DEBUG':113,
 'log_count / INFO':7,
 'request_depth_max':10,
 'response_received_count':11,
 '調度程序/出隊':10,
 'scheduler / dequeued / memory':10,
 '調度程序/入隊':10,
 'scheduler / enqueued / memory':10,
 'start_time':datetime.datetime(2017,2,19,5,37,20,321557)}
2017-02-19 13:37:27 [scrapy.core.engine]信息:蜘蛛關閉(完成)複製代碼

這裏只是部分運行結果,中間一些抓取結果已省略。

首先,Scrapy輸出了當前的版本號以及正在啓動的項目名稱。接着輸出了當前settings.py中一些重寫後的配置。而後輸出了當前所應用的中間件和Pipelines.Middlewares默認是啓用的,能夠在settings.py中修改.Pipelines默認是空,一樣也能夠在settings.py中配置。後面會對它們進行講解。

接下來就是輸出各個頁面的抓取結果了,能夠看到爬蟲一邊解析,一邊翻頁,直至將全部內容抓取完畢,而後終止。

最後,Scrapy輸出了整個抓取過程的統計信息,如請求的字節數,請求次數,響應次數,完成緣由等。

整個Scrapy程序成功運行。咱們經過很是簡單的代碼就完成了一個網站內容的爬取,這樣相比以前一點點寫程序簡潔不少。

十,保存到文件

運行完Scrapy後,咱們只在控制檯看到了輸出結果。若是想保存結果該怎麼辦呢?

要完成這個任務其實不須要任何額外的代碼,Scrapy提供的Feed Export能夠輕鬆將抓取結果輸出。例如,咱們想將上面的結果保存成JSON文件,能夠執行以下命令:

scrapy抓取引號-o quotes.json複製代碼

命令運行後,項目內多了一個quotes.json文件,文件包含了剛纔抓取的全部內容,內容是JSON格式。

另外咱們還能夠每個項目輸出一行JSON,輸出後綴爲JL,爲jsonline的縮寫,命令以下所示:

scrapy抓取引號-o quotes.jl複製代碼

scrapy抓取引號-o引用.jsonlines複製代碼

輸出格式還支持不少種,例如CSV,XML,鹹菜,元帥等,還支持FTP,S3等遠程輸出,另外還能夠經過自定義ItemExporter來實現其餘的輸出。

例如,下面命令對應的輸出分別爲CSV,XML,鹹菜,元帥格式以及FTP遠程輸出:

scrapy抓取引號-o quotes.csv
scrapy抓取引號-o quotes.xml
scrapy抓取引號-o quotes.pickle
scrapy抓取引號-o引用.marshal
scrapy抓取引號-o ftp:// user:pass@ftp.example.com/path/to/quotes.csv複製代碼

其中,FTP輸出須要正確配置用戶名,密碼,地址,輸出路徑,不然會報錯。

經過Scrapy提供的Feed Exports,咱們能夠輕鬆地輸出抓取結果到文件。對於一些小型項目來講,這應該足夠了。不過若是想要更復雜的輸出,如輸出到數據庫等,咱們可使用Item Pileline來完成。

十一,使用Item Pipeline

若是想進行更復雜的操做,如將結果保存到MongoDB數據庫,或者篩選某些有用的Item,則咱們能夠定義Item Pileline來實現。

Item Pipeline爲項目管道。當Item生成後,它會自動被送到Item Pipeline進行處理,咱們經常使用Item Pipeline來作以下操做。

  • 清理HTML數據。

  • 驗證爬取數據,檢查爬取字段。

  • 查重並丟棄重複內容。

  • 將爬取結果保存到數據庫。

要實現Item Pipeline很簡單,只須要定義一個類並實現
process_item()
方法便可。啓用Item Pipeline後,Item Pipeline會自動調用這個方法。
process_item()
方法必須返回包含數據的字典或Item對象,或者拋出DropItem異常。

process_item()微信

方法有兩個參數。一個參數是
item
,每次蜘蛛生成的項目都會做爲參數傳遞過來。另外一個參數是
spider
,就是蜘蛛的實例。

接下來,咱們實現一個Item Pipeline,篩掉
text
長度大於50的Item,並將結果保存到MongoDB。

修改項目裏的pipelines.py文件,以前用命令行自動生成的文件內容能夠刪掉
,一個增長
TextPipeline
類,內容以下所示:

from scrapy.exceptions import DropItem 

class  TextPipeline (object):
    def  __init__ (self):
         self.limit = 50 

    def  process_item (self,item,spider):
        if item [ 'text' ]:
            if len(item [ 'text' ]] )> self.limit:
                item [ 'text' ] = item [ 'text' ] [ 0:self.limit] .rstrip()+ '...' 
            return item 
        elsereturn DropItem('Missing Text'))複製代碼

這段代碼在構造方法裏定義了限制長度爲
50,了實現
process_item()
方法,參數其的英文
item
狀語從句:
spide
- [R首先該方法判斷。
item
text
屬性是否存在,若是不存在,拋出則
DropItem
異常;若是存在,再判斷長度是否大於50,若是大於,那就截斷而後拼接省略號,再將
item
報道查看便可。

接下來,將咱們處理後的
item
存入MongoDB中,定義另一個管道一樣在pipelines.py中,咱們實現另外一個類。
MongoPipeline
,內容以下所示:

導入 pymongo 

類 MongoPipeline (object):
    def  __init__ (self,mongo_uri,mongo_db):
         self.mongo_uri = mongo_uri 
        self.mongo_db = mongo_db 

    @classmethod 
    def  from_crawler (cls,crawler):
        return cls(
            mongo_uri = crawler.settings.get(' MONGO_URI'),
            mongo_db = crawler.settings.get('MONGO_DB')
        )

    def  open_spider (self,spider):
         self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client [self.mongo_db] 

    def  process_item (self,item,spider):
         name = item .__ class __.__ name__ 
        self.db [name] .insert(dict(item))
        return item 

    def  close_spider (self,蜘蛛):
         self.client.close()複製代碼

MongoPipelinecookie

類實現了API定義的另外幾個方法。

  • from_crawler網絡

    。它是一個類方法,用
    @classmethod
    標識,是一種依賴注入的方式。它的參數就是
    crawler
    ,經過
    crawler
    咱們能夠拿到全局配置的每一個配置信息。在全局配置settings.py中,能夠咱們定義
    MONGO_URI
    狀語從句:
    MONGO_DB
    來指定MongoDB的鏈接須要的地址和數據庫名稱,拿到配置信息以後返回類對象便可。因此這個方法的定義主要是用來獲取 settings.py中的配置的

  • open_spider

    。當蜘蛛開啓時,這個方法被調用。上文程序中主要進行了一些初始化操做。

  • close_spider

    。當蜘蛛關閉時,這個方法會調用。上文程序中將數據庫鏈接關閉。

的最主要
process_item()
方法則執行了數據插入操做。

好定義
TextPipeline
狀語從句:
MongoPipeline
這兩個類後,咱們須要在settings.py中使用它們.MongoDB的鏈接信息還須要定義。

咱們在settings.py中加入以下內容:

ITEM_PIPELINES = { 
   'tutorial.pipelines.TextPipeline':300,
   'tutorial.pipelines.MongoPipeline':400,
} 
MONGO_URI = 'localhost'MONGO_DB
 = 'tutorial'複製代碼

賦值
ITEM_PIPELINES
字典搜索,鍵名是管道的類名稱,鍵值是調用優先級,是一個數字,數字越小則對應的管道越先被調用。

再從新執行爬取,命令以下所示:

scrapy抓取報價複製代碼

爬取結束後,MongoDB的中建立了一個教程的數據庫,QuoteItem的表,以下圖所示。

的長
text
已經被處理並追加了省略號,的短
text
保持不變,
author
狀語從句:
tags
也。都相應保存。

十二,源代碼

本節代碼地址爲:HTTPS://github.com/Python3WebSpider/ScrapyTutorial。


本資源首發於崔慶才的我的博客靜覓: Python3網絡爬蟲開發實戰教程 | 靜覓

如想了解更多爬蟲資訊,請關注個人我的微信公衆號:進擊的Coder

weixin.qq.com/r/5zsjOyvEZ… (二維碼自動識別)

相關文章
相關標籤/搜索