關(guān)于Python3爬蟲中Scrapy的用法

小編給大家分享一下關(guān)于Python3爬蟲中Scrapy的用法,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!

成都創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的烈山網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

仁壽網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián)公司,仁壽網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為仁壽千余家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請(qǐng)找那個(gè)售后服務(wù)好的仁壽做網(wǎng)站的公司定做!

Scrapy 入門

接下來(lái)介紹一個(gè)簡(jiǎn)單的項(xiàng)目,完成一遍 Scrapy 抓取流程。通過這個(gè)過程,我們可以對(duì) Scrapy 的基本用法和原理有大體了解。

1. 本節(jié)目標(biāo)

本節(jié)要完成的任務(wù)如下。

創(chuàng)建一個(gè) Scrapy 項(xiàng)目。

創(chuàng)建一個(gè) Spider 來(lái)抓取站點(diǎn)和處理數(shù)據(jù)。

通過命令行將抓取的內(nèi)容導(dǎo)出。

將抓取的內(nèi)容保存到 MongoDB 數(shù)據(jù)庫(kù)。

2. 準(zhǔn)備工作

我們需要安裝好 Scrapy 框架、MongoDB 和 PyMongo 庫(kù)。如果尚未安裝,請(qǐng)參照上一節(jié)的安裝說明。

3. 創(chuàng)建項(xiàng)目

創(chuàng)建一個(gè) Scrapy 項(xiàng)目,項(xiàng)目文件可以直接用 scrapy 命令生成,命令如下所示:

scrapy startproject tutorial

這個(gè)命令可以在任意文件夾運(yùn)行。如果提示權(quán)限問題,可以加 sudo 運(yùn)行該命令。這個(gè)命令將會(huì)創(chuàng)建一個(gè)名為 tutorial 的文件夾,文件夾結(jié)構(gòu)如下所示:

scrapy.cfg     # Scrapy 部署時(shí)的配置文件
tutorial         # 項(xiàng)目的模塊,引入的時(shí)候需要從這里引入
    __init__.py    
    items.py     # Items 的定義,定義爬取的數(shù)據(jù)結(jié)構(gòu)
    middlewares.py   # Middlewares 的定義,定義爬取時(shí)的中間件
    pipelines.py       # Pipelines 的定義,定義數(shù)據(jù)管道
    settings.py       # 配置文件
    spiders         # 放置 Spiders 的文件夾
    __init__.py

4. 創(chuàng)建 Spider

Spider 是自己定義的類,Scrapy 用它來(lái)從網(wǎng)頁(yè)里抓取內(nèi)容,并解析抓取的結(jié)果。不過這個(gè)類必須繼承 Scrapy 提供的 Spider 類 scrapy.Spider,還要定義 Spider 的名稱和起始請(qǐng)求,以及怎樣處理爬取后的結(jié)果的方法。

也可以使用命令行創(chuàng)建一個(gè) Spider。比如要生成 Quotes 這個(gè) Spider,可以執(zhí)行如下命令:

cd tutorial
scrapy genspider quotes

進(jìn)入剛才創(chuàng)建的 tutorial 文件夾,然后執(zhí)行 genspider 命令。第一個(gè)參數(shù)是 Spider 的名稱,第二個(gè)參數(shù)是網(wǎng)站域名。執(zhí)行完畢之后,spiders 文件夾中多了一個(gè) quotes.py,它就是剛剛創(chuàng)建的 Spider,內(nèi)容如下所示:

import scrapy
class QuotesSpider(scrapy.Spider):
    name = "quotes"
    allowed_domains = ["quotes.toscrape.com"]
    start_urls = ['http://quotes.toscrape.com/']
    def parse(self, response):
        pass

這里有三個(gè)屬性 ——name、allowed_domains 和 start_urls,還有一個(gè)方法 parse。

name,它是每個(gè)項(xiàng)目唯一的名字,用來(lái)區(qū)分不同的 Spider。

allowed_domains,它是允許爬取的域名,如果初始或后續(xù)的請(qǐng)求鏈接不是這個(gè)域名下的,則請(qǐng)求鏈接會(huì)被過濾掉。

start_urls,它包含了 Spider 在啟動(dòng)時(shí)爬取的 url 列表,初始請(qǐng)求是由它來(lái)定義的。

parse,它是 Spider 的一個(gè)方法。默認(rèn)情況下,被調(diào)用時(shí) start_urls 里面的鏈接構(gòu)成的請(qǐng)求完成下載執(zhí)行后,返回的響應(yīng)就會(huì)作為唯一的參數(shù)傳遞給這個(gè)函數(shù)。該方法負(fù)責(zé)解析返回的響應(yīng)、提取數(shù)據(jù)或者進(jìn)一步生成要處理的請(qǐng)求。

5. 創(chuàng)建 Item

Item 是保存爬取數(shù)據(jù)的容器,它的使用方法和字典類似。不過,相比字典,Item 多了額外的保護(hù)機(jī)制,可以避免拼寫錯(cuò)誤或者定義字段錯(cuò)誤。

創(chuàng)建 Item 需要繼承 scrapy.Item 類,并且定義類型為 scrapy.Field 的字段。觀察目標(biāo)網(wǎng)站,我們可以獲取到的內(nèi)容有 text、author、tags。

定義 Item,此時(shí)將 items.py 修改如下:

import scrapy
class QuoteItem(scrapy.Item):
    text = scrapy.Field()
    author = scrapy.Field()
    tags = scrapy.Field()

這里定義了三個(gè)字段,將類的名稱修改為 QuoteItem,接下來(lái)爬取時(shí)我們會(huì)使用到這個(gè) Item。

6. 解析 Response

前面我們看到,parse() 方法的參數(shù) response 是 start_urls 里面的鏈接爬取后的結(jié)果。所以在 parse() 方法中,我們可以直接對(duì) response 變量包含的內(nèi)容進(jìn)行解析,比如瀏覽請(qǐng)求結(jié)果的網(wǎng)頁(yè)源代碼,或者進(jìn)一步分析源代碼內(nèi)容,或者找出結(jié)果中的鏈接而得到下一個(gè)請(qǐng)求。

我們可以看到網(wǎng)頁(yè)中既有我們想要的結(jié)果,又有下一頁(yè)的鏈接,這兩部分內(nèi)容我們都要進(jìn)行處理。

首先看看網(wǎng)頁(yè)結(jié)構(gòu),如圖 13-2 所示。每一頁(yè)都有多個(gè) class 為 quote 的區(qū)塊,每個(gè)區(qū)塊內(nèi)都包含 text、author、tags。那么我們先找出所有的 quote,然后提取每一個(gè) quote 中的內(nèi)容。

關(guān)于Python3爬蟲中Scrapy的用法

圖 13-2 頁(yè)面結(jié)構(gòu)

提取的方式可以是 CSS 選擇器或 XPath 選擇器。在這里我們使用 CSS 選擇器進(jìn)行選擇,parse() 方法的改寫如下所示:

def parse(self, response):
    quotes = response.css('.quote')
    for quote in quotes:
        text = quote.css('.text::text').extract_first()
        author = quote.css('.author::text').extract_first()
        tags = quote.css('.tags .tag::text').extract()

這里首先利用選擇器選取所有的 quote,并將其賦值為 quotes 變量,然后利用 for 循環(huán)對(duì)每個(gè) quote 遍歷,解析每個(gè) quote 的內(nèi)容。

對(duì) text 來(lái)說,觀察到它的 class 為 text,所以可以用.text 選擇器來(lái)選取,這個(gè)結(jié)果實(shí)際上是整個(gè)帶有標(biāo)簽的節(jié)點(diǎn),要獲取它的正文內(nèi)容,可以加::text 來(lái)獲取。這時(shí)的結(jié)果是長(zhǎng)度為 1 的列表,所以還需要用 extract_first() 方法來(lái)獲取第一個(gè)元素。而對(duì)于 tags 來(lái)說,由于我們要獲取所有的標(biāo)簽,所以用 extract() 方法獲取整個(gè)列表即可。

以第一個(gè) quote 的結(jié)果為例,各個(gè)選擇方法及結(jié)果的說明如下內(nèi)容。

源碼如下:

<div class="quote" itemscope=""itemtype="http://schema.org/CreativeWork">
        <span class="text" itemprop="text">“The world as we have created it is a process of our thinking. 
        It cannot be changed without changing our thinking.”</span>
        <span>by <small class="author" itemprop="author">Albert Einstein</small>
        <a href="/author/Albert-Einstein">(about)</a>
        </span>
        <div class="tags">
            Tags:
            <meta class="keywords" itemprop="keywords" content="change,deep-thoughts,thinking,world"> 
            <a class="tag" href="/tag/change/page/1/">change</a>
            <a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>
            <a class="tag" href="/tag/thinking/page/1/">thinking</a>
            <a class="tag" href="/tag/world/page/1/">world</a>
        </div>
    </div>

不同選擇器的返回結(jié)果如下。

quote.css(‘.text’)

[<Selector xpath="descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), ' text ')]
"data='<span class="text"itemprop="text">“The '>]

quote.css(‘.text::text’)

[<Selector xpath="descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), ' text ')]
/text()"data='“The world as we have created it is a pr'>]

quote.css(‘.text’).extract()

['<span class="text"itemprop="text">“The world as we have created it is a process of our thinking. It cannot be 
changed without changing our thinking.”</span>']

quote.css(‘.text::text’).extract()

['“The world as we have created it is a process of our thinking. It cannot be changed without changing our 
thinking.”']

quote.css(‘.text::text’).extract_first()

“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”

所以,對(duì)于 text,獲取結(jié)果的第一個(gè)元素即可,所以使用 extract_first() 方法,對(duì)于 tags,要獲取所有結(jié)果組成的列表,所以使用 extract() 方法。

7. 使用 Item

上文定義了 Item,接下來(lái)就要使用它了。Item 可以理解為一個(gè)字典,不過在聲明的時(shí)候需要實(shí)例化。然后依次用剛才解析的結(jié)果賦值 Item 的每一個(gè)字段,最后將 Item 返回即可。

QuotesSpider 的改寫如下所示:

import scrapy
from tutorial.items import QuoteItem
class QuotesSpider(scrapy.Spider):
    name = "quotes"
    allowed_domains = ["quotes.toscrape.com"]
    start_urls = ['http://quotes.toscrape.com/']
    def parse(self, response):
        quotes = response.css('.quote')
        for quote in quotes:
            item = QuoteItem()
            item['text'] = quote.css('.text::text').extract_first()
            item['author'] = quote.css('.author::text').extract_first()
            item['tags'] = quote.css('.tags .tag::text').extract()
            yield item

如此一來(lái),首頁(yè)的所有內(nèi)容被解析出來(lái),并被賦值成了一個(gè)個(gè) QuoteItem。

8. 后續(xù) Request

上面的操作實(shí)現(xiàn)了從初始頁(yè)面抓取內(nèi)容。那么,下一頁(yè)的內(nèi)容該如何抓???這就需要我們從當(dāng)前頁(yè)面中找到信息來(lái)生成下一個(gè)請(qǐng)求,然后在下一個(gè)請(qǐng)求的頁(yè)面里找到信息再構(gòu)造再下一個(gè)請(qǐng)求。這樣循環(huán)往復(fù)迭代,從而實(shí)現(xiàn)整站的爬取。

將剛才的頁(yè)面拉到最底部,如圖 13-3 所示。

關(guān)于Python3爬蟲中Scrapy的用法

圖 13-3 頁(yè)面底部

有一個(gè) Next 按鈕,查看一下源代碼,可以發(fā)現(xiàn)它的鏈接是 /page/2/,實(shí)際上全鏈接就是:http://quotes.toscrape.com/page/2,通過這個(gè)鏈接我們就可以構(gòu)造下一個(gè)請(qǐng)求。

構(gòu)造請(qǐng)求時(shí)需要用到 scrapy.Request。這里我們傳遞兩個(gè)參數(shù) ——url 和 callback,這兩個(gè)參數(shù)的說明如下。

url:它是請(qǐng)求鏈接。

callback:它是回調(diào)函數(shù)。當(dāng)指定了該回調(diào)函數(shù)的請(qǐng)求完成之后,獲取到響應(yīng),引擎會(huì)將該響應(yīng)作為參數(shù)傳遞給這個(gè)回調(diào)函數(shù)。回調(diào)函數(shù)進(jìn)行解析或生成下一個(gè)請(qǐng)求,回調(diào)函數(shù)如上文的 parse() 所示。

由于 parse() 就是解析 text、author、tags 的方法,而下一頁(yè)的結(jié)構(gòu)和剛才已經(jīng)解析的頁(yè)面結(jié)構(gòu)是一樣的,所以我們可以再次使用 parse() 方法來(lái)做頁(yè)面解析。

接下來(lái)我們要做的就是利用選擇器得到下一頁(yè)鏈接并生成請(qǐng)求,在 parse() 方法后追加如下的代碼:

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

第一句代碼首先通過 CSS 選擇器獲取下一個(gè)頁(yè)面的鏈接,即要獲取 a 超鏈接中的 href 屬性。這里用到了::attr(href) 操作。然后再調(diào)用 extract_first() 方法獲取內(nèi)容。

第二句代碼調(diào)用了 urljoin() 方法,urljoin() 方法可以將相對(duì) URL 構(gòu)造成一個(gè)絕對(duì)的 URL。例如,獲取到的下一頁(yè)地址是 /page/2,urljoin() 方法處理后得到的結(jié)果就是:http://quotes.toscrape.com/page/2/。

第三句代碼通過 url 和 callback 變量構(gòu)造了一個(gè)新的請(qǐng)求,回調(diào)函數(shù) callback 依然使用 parse() 方法。這個(gè)請(qǐng)求完成后,響應(yīng)會(huì)重新經(jīng)過 parse 方法處理,得到第二頁(yè)的解析結(jié)果,然后生成第二頁(yè)的下一頁(yè),也就是第三頁(yè)的請(qǐng)求。這樣爬蟲就進(jìn)入了一個(gè)循環(huán),直到最后一頁(yè)。

通過幾行代碼,我們就輕松實(shí)現(xiàn)了一個(gè)抓取循環(huán),將每個(gè)頁(yè)面的結(jié)果抓取下來(lái)了。

現(xiàn)在,改寫之后的整個(gè) Spider 類如下所示:

import scrapy
from tutorial.items import QuoteItem
class QuotesSpider(scrapy.Spider):
    name = "quotes"
    allowed_domains = ["quotes.toscrape.com"]
    start_urls = ['http://quotes.toscrape.com/']
    def parse(self, response):
        quotes = response.css('.quote')
        for quote in quotes:
            item = QuoteItem()
            item['text'] = quote.css('.text::text').extract_first()
            item['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)

9. 運(yùn)行

接下來(lái),進(jìn)入目錄,運(yùn)行如下命令:

scrapy crawl quotes

就可以看到 Scrapy 的運(yùn)行結(jié)果了。

2017-02-19 13:37:20 [scrapy.utils.log] INFO: Scrapy 1.3.0 started (bot: tutorial)
2017-02-19 13:37:20 [scrapy.utils.log] INFO: Overridden settings: {'NEWSPIDER_MODULE': 'tutorial.spiders', 
'SPIDER_MODULES': ['tutorial.spiders'], 'ROBOTSTXT_OBEY': True, 'BOT_NAME': 'tutorial'}
2017-02-19 13:37:20 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.logstats.LogStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.corestats.CoreStats']
2017-02-19 13:37:20 [scrapy.middleware] INFO: Enabled downloader middlewares:
['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] INFO: Enabled spider middlewares:
['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] INFO: Enabled item pipelines:
[]
2017-02-19 13:37:20 [scrapy.core.engine] INFO: Spider opened
2017-02-19 13:37:20 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items 
(at 0 items/min)
2017-02-19 13:37:20 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 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: None)
2017-02-19 13:37:21 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/> (referer: None)
2017-02-19 13:37:21 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/>
{'author': u'Albert Einstein',
 'tags': [u'change', u'deep-thoughts', u'thinking', u'world'],
 'text': u'u201cThe world as we have created it is a process of our thinking. It cannot be changed without 
 changing our thinking.u201d'}
2017-02-19 13:37:21 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/>
{'author': u'J.K. Rowling',
 'tags': [u'abilities', u'choices'],
 'text': u'u201cIt is our choices, Harry, that show what we truly are, far more than our abilities.u201d'}
...
2017-02-19 13:37:27 [scrapy.core.engine] INFO: Closing spider (finished)
2017-02-19 13:37:27 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'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': 'finished',
 '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,
 'scheduler/dequeued': 10,
 'scheduler/dequeued/memory': 10,
 'scheduler/enqueued': 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] INFO: Spider closed (finished)

這里只是部分運(yùn)行結(jié)果,中間一些抓取結(jié)果已省略。

首先,Scrapy 輸出了當(dāng)前的版本號(hào)以及正在啟動(dòng)的項(xiàng)目名稱。接著輸出了當(dāng)前 settings.py 中一些重寫后的配置。然后輸出了當(dāng)前所應(yīng)用的 Middlewares 和 Pipelines。Middlewares 默認(rèn)是啟用的,可以在 settings.py 中修改。Pipelines 默認(rèn)是空,同樣也可以在 settings.py 中配置。后面會(huì)對(duì)它們進(jìn)行講解。

接下來(lái)就是輸出各個(gè)頁(yè)面的抓取結(jié)果了,可以看到爬蟲一邊解析,一邊翻頁(yè),直至將所有內(nèi)容抓取完畢,然后終止。

最后,Scrapy 輸出了整個(gè)抓取過程的統(tǒng)計(jì)信息,如請(qǐng)求的字節(jié)數(shù)、請(qǐng)求次數(shù)、響應(yīng)次數(shù)、完成原因等。

整個(gè) Scrapy 程序成功運(yùn)行。我們通過非常簡(jiǎn)單的代碼就完成了一個(gè)網(wǎng)站內(nèi)容的爬取,這樣相比之前一點(diǎn)點(diǎn)寫程序簡(jiǎn)潔很多。

10. 保存到文件

運(yùn)行完 Scrapy 后,我們只在控制臺(tái)看到了輸出結(jié)果。如果想保存結(jié)果該怎么辦呢?

要完成這個(gè)任務(wù)其實(shí)不需要任何額外的代碼,Scrapy 提供的 Feed Exports 可以輕松將抓取結(jié)果輸出。例如,我們想將上面的結(jié)果保存成 JSON 文件,可以執(zhí)行如下命令:

scrapy crawl quotes -o quotes.json

命令運(yùn)行后,項(xiàng)目?jī)?nèi)多了一個(gè) quotes.json 文件,文件包含了剛才抓取的所有內(nèi)容,內(nèi)容是 JSON 格式。

另外我們還可以每一個(gè) Item 輸出一行 JSON,輸出后綴為 jl,為 jsonline 的縮寫,命令如下所示:

scrapy crawl quotes -o quotes.jl

scrapy crawl quotes -o quotes.jsonlines

輸出格式還支持很多種,例如 csv、xml、pickle、marshal 等,還支持 ftp、s3 等遠(yuǎn)程輸出,另外還可以通過自定義 ItemExporter 來(lái)實(shí)現(xiàn)其他的輸出。

例如,下面命令對(duì)應(yīng)的輸出分別為 csv、xml、pickle、marshal 格式以及 ftp 遠(yuǎn)程輸出:

scrapy crawl quotes -o quotes.csv
scrapy crawl quotes -o quotes.xml
scrapy crawl quotes -o quotes.pickle
scrapy crawl quotes -o quotes.marshal
scrapy crawl quotes -o ftp://user:pass@ftp.example.com/path/to/quotes.csv

其中,ftp 輸出需要正確配置用戶名、密碼、地址、輸出路徑,否則會(huì)報(bào)錯(cuò)。

通過 Scrapy 提供的 Feed Exports,我們可以輕松地輸出抓取結(jié)果到文件。對(duì)于一些小型項(xiàng)目來(lái)說,這應(yīng)該足夠了。不過如果想要更復(fù)雜的輸出,如輸出到數(shù)據(jù)庫(kù)等,我們可以使用 Item Pileline 來(lái)完成。

11. 使用 Item Pipeline

如果想進(jìn)行更復(fù)雜的操作,如將結(jié)果保存到 MongoDB 數(shù)據(jù)庫(kù),或者篩選某些有用的 Item,則我們可以定義 Item Pipeline 來(lái)實(shí)現(xiàn)。

Item Pipeline 為項(xiàng)目管道。當(dāng) Item 生成后,它會(huì)自動(dòng)被送到 Item Pipeline 進(jìn)行處理,我們常用 Item Pipeline 來(lái)做如下操作。

清洗 HTML 數(shù)據(jù)

驗(yàn)證爬取數(shù)據(jù),檢查爬取字段

查重并丟棄重復(fù)內(nèi)容

將爬取結(jié)果儲(chǔ)存到數(shù)據(jù)庫(kù)

要實(shí)現(xiàn) Item Pipeline 很簡(jiǎn)單,只需要定義一個(gè)類并實(shí)現(xiàn) process_item() 方法即可。啟用 Item Pipeline 后,Item Pipeline 會(huì)自動(dòng)調(diào)用這個(gè)方法。process_item() 方法必須返回包含數(shù)據(jù)的字典或 Item 對(duì)象,或者拋出 DropItem 異常。

process_item() 方法有兩個(gè)參數(shù)。一個(gè)參數(shù)是 item,每次 Spider 生成的 Item 都會(huì)作為參數(shù)傳遞過來(lái)。另一個(gè)參數(shù)是 spider,就是 Spider 的實(shí)例。

接下來(lái),我們實(shí)現(xiàn)一個(gè) Item Pipeline,篩掉 text 長(zhǎng)度大于 50 的 Item,并將結(jié)果保存到 MongoDB。

修改項(xiàng)目里的 pipelines.py 文件,之前用命令行自動(dòng)生成的文件內(nèi)容可以刪掉,增加一個(gè) TextPipeline 類,內(nèi)容如下所示:

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
        else:
            return DropItem('Missing Text')

這段代碼在構(gòu)造方法里定義了限制長(zhǎng)度為 50,實(shí)現(xiàn)了 process_item() 方法,其參數(shù)是 item 和 spider。首先該方法判斷 item 的 text 屬性是否存在,如果不存在,則拋出 DropItem 異常;如果存在,再判斷長(zhǎng)度是否大于 50,如果大于,那就截?cái)嗳缓笃唇邮÷蕴?hào),再將 item 返回即可。

接下來(lái),我們將處理后的 item 存入 MongoDB,定義另外一個(gè) Pipeline。同樣在 pipelines.py 中,我們實(shí)現(xiàn)另一個(gè)類 MongoPipeline,內(nèi)容如下所示:

import pymongo
class 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, spider):
        self.client.close()

MongoPipeline 類實(shí)現(xiàn)了 API 定義的另外幾個(gè)方法。

from_crawler,這是一個(gè)類方法,用 @classmethod 標(biāo)識(shí),是一種依賴注入的方式,方法的參數(shù)就是 crawler,通過 crawler 這個(gè)我們可以拿到全局配置的每個(gè)配置信息,在全局配置 settings.py 中我們可以定義 MONGO_URI 和 MONGO_DB 來(lái)指定 MongoDB 連接需要的地址和數(shù)據(jù)庫(kù)名稱,拿到配置信息之后返回類對(duì)象即可。所以這個(gè)方法的定義主要是用來(lái)獲取 settings.py 中的配置的。

open_spider,當(dāng) Spider 被開啟時(shí),這個(gè)方法被調(diào)用。在這里主要進(jìn)行了一些初始化操作。

close_spider,當(dāng) Spider 被關(guān)閉時(shí),這個(gè)方法會(huì)調(diào)用,在這里將數(shù)據(jù)庫(kù)連接關(guān)閉。

最主要的 process_item() 方法則執(zhí)行了數(shù)據(jù)插入操作。

定義好 TextPipeline 和 MongoPipeline 這兩個(gè)類后,我們需要在 settings.py 中使用它們。MongoDB 的連接信息還需要定義。

我們?cè)?settings.py 中加入如下內(nèi)容:

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

賦值 ITEM_PIPELINES 字典,鍵名是 Pipeline 的類名稱,鍵值是調(diào)用優(yōu)先級(jí),是一個(gè)數(shù)字,數(shù)字越小則對(duì)應(yīng)的 Pipeline 越先被調(diào)用。

再重新執(zhí)行爬取,命令如下所示:

scrapy crawl quotes

爬取結(jié)束后,MongoDB 中創(chuàng)建了一個(gè) tutorial 的數(shù)據(jù)庫(kù)、QuoteItem 的表,如圖 13-4 所示。

關(guān)于Python3爬蟲中Scrapy的用法

圖 13-4 爬取結(jié)果

長(zhǎng)的 text 已經(jīng)被處理并追加了省略號(hào),短的 text 保持不變,author 和 tags 也都相應(yīng)保存。

看完了這篇文章,相信你對(duì)關(guān)于Python3爬蟲中Scrapy的用法有了一定的了解,想了解更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

網(wǎng)站欄目:關(guān)于Python3爬蟲中Scrapy的用法
文章出自:http://bm7419.com/article38/igcppp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計(jì)、域名注冊(cè)、靜態(tài)網(wǎng)站做網(wǎng)站、網(wǎng)站改版、企業(yè)網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開發(fā)