Scrapy框架實(shí)例:獲取圖書(shū)信息

Scrapy是一個(gè)為了爬取網(wǎng)站數(shù)據(jù),提取結(jié)構(gòu)性數(shù)據(jù)而編寫(xiě)的應(yīng)用框架。 可以應(yīng)用在包括數(shù)據(jù)挖掘,信息處理或存儲(chǔ)歷史數(shù)據(jù)等一系列的程序中。

其最初是為了頁(yè)面抓取(更確切來(lái)說(shuō),網(wǎng)絡(luò)抓取)所設(shè)計(jì)的, 也可以應(yīng)用在獲取API所返回的數(shù)據(jù)(例如Amazon Associates Web Services) 或者通用的網(wǎng)絡(luò)爬蟲(chóng)。

Scrapy構(gòu)架

下圖顯示Scrapy的結(jié)構(gòu)和組件,箭頭表示框架內(nèi)數(shù)據(jù)流情況。

Scrapy框架結(jié)構(gòu)和組件

Scrapy中的數(shù)據(jù)流由執(zhí)行引擎控制,具體流程如下:

  1. 引擎獲取初始請(qǐng)求從爬蟲(chóng)開(kāi)始抓取數(shù)據(jù)。
  2. 引擎在調(diào)度器中調(diào)度請(qǐng)求,并要求抓取下一個(gè)請(qǐng)求。
  3. 調(diào)度器將下一個(gè)請(qǐng)求返回給引擎。
  4. 引擎將請(qǐng)求發(fā)送到下載器,通過(guò)下載器中間件。
  5. 一旦頁(yè)面完成下載,下載器會(huì)生成響應(yīng)并將其發(fā)送到引擎,通過(guò)下載中間件。
  6. 引擎從下載器接收響應(yīng)并將其發(fā)送到爬蟲(chóng)進(jìn)行處理,通過(guò)爬中間件。
  7. 爬蟲(chóng)處理響應(yīng),并將抓取的項(xiàng)目和新的請(qǐng)求返回到引擎,通過(guò)爬蟲(chóng)中間件。
  8. 引擎將處理過(guò)的項(xiàng)目發(fā)送到項(xiàng)目管道,然后將處理過(guò)的請(qǐng)求發(fā)送到調(diào)度器,并要求可能的下一個(gè)請(qǐng)求爬取。
  9. 重復(fù)以上流程直到調(diào)度器沒(méi)有更多的請(qǐng)求。

接下來(lái)我們通過(guò)項(xiàng)目實(shí)例來(lái)看看它是如何運(yùn)行的。

安裝Scrapy

使用pip安裝:

pip install scrapy

windows系統(tǒng)下安裝會(huì)出現(xiàn)異常,需要到https://www.lfd.uci.edu/~gohlke/pythonlibs/下載Twisted庫(kù)的whl文件,切換到文件目錄后,在命令行下輸入:

pip install Twisted-17.9.0-cp35-cp35m-win_amd64.whl

注:當(dāng)前操作系統(tǒng)是64位的,環(huán)境是Python3.5,所以選擇下載Twisted-17.9.0-cp35-cp35m-win_amd64.whl

根據(jù)系統(tǒng)及環(huán)境選擇下載

安裝完后再次運(yùn)行:

pip install scrapy

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

圖靈社區(qū):http://www.ituring.com.cn/book

圖靈社區(qū):主要以出版計(jì)算機(jī)、數(shù)學(xué)統(tǒng)計(jì)、科普等圖書(shū),并授權(quán)銷(xiāo)售正版電子書(shū)的在線社區(qū)。是我比較喜歡的出版社之一,有興趣的可以看看上面的書(shū)籍,質(zhì)量都不錯(cuò)。

這里我們以爬取圖靈社區(qū)圖書(shū)信息作為項(xiàng)目先切換到要放置項(xiàng)目的目錄文件夾,在命令行下輸入:

scrapy startproject ituring
目錄結(jié)構(gòu)

該命令會(huì)創(chuàng)建包含下面內(nèi)容的ituring目錄:

  • scrapy.cfg:項(xiàng)目的配置文件
  • items.py:項(xiàng)目中的item文件
  • middlewares.py:項(xiàng)目中的中間件文件
  • pipelines.py:項(xiàng)目中的pipelines文件
  • settings.py:項(xiàng)目的設(shè)置文件

創(chuàng)建爬蟲(chóng)文件

進(jìn)入項(xiàng)目文件夾ituring/ituring,使用如下命令:

cd ituring

創(chuàng)建爬蟲(chóng)程序并設(shè)定允許爬取的域名地址:

scrapy genspider ituring_spider ituring.com.cn

在spiders目錄下會(huì)新創(chuàng)建ituring_spider.py文件,具體代碼如下:

import scrapy


class IturingSpiderSpider(scrapy.Spider):
    name = 'ituring_spider'
    allowed_domains = ['ituring.com.cn']
    start_urls = ['http://ituring.com.cn/']

    def parse(self, response):
        pass

定義Item

Item 是保存爬取到的數(shù)據(jù)的容器,本質(zhì)上就是Python字典數(shù)據(jù)類(lèi)型,提供了額外保護(hù)機(jī)制來(lái)避免拼寫(xiě)錯(cuò)誤導(dǎo)致的未定義字段錯(cuò)誤。每個(gè)自定義的Item類(lèi)都繼承scrapy.item類(lèi),字段定義類(lèi)型scrapy.Field類(lèi)屬性。

根據(jù)我們要爬取網(wǎng)站的數(shù)據(jù)進(jìn)行設(shè)定字段,從http://www.ituring.com.cn/book主頁(yè)點(diǎn)擊進(jìn)入書(shū)籍,可以看到書(shū)籍詳情數(shù)據(jù),這里我們需要選取的數(shù)據(jù)有,標(biāo)題、鏈接地址、書(shū)籍圖片、推薦人數(shù)、閱讀人數(shù)及圖書(shū)價(jià)格。

打開(kāi)items.py文件開(kāi)始在Item中定義相應(yīng)字段,對(duì)IturingItem類(lèi)進(jìn)行修改:

import scrapy


class IturingItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()  # 標(biāo)題
    link = scrapy.Field()  # 鏈接
    img = scrapy.Field()  # 圖片
    up_count = scrapy.Field()  # 推薦數(shù)
    read_count = scrapy.Field()  # 閱讀數(shù)
    price = scrapy.Field()  # 價(jià)格

編寫(xiě)爬蟲(chóng)

Spider是用戶編寫(xiě)用于從單個(gè)網(wǎng)站(或者一些網(wǎng)站)爬取數(shù)據(jù)的類(lèi)??梢源嬖诙鄠€(gè)Spider類(lèi),但name屬性的值(即爬蟲(chóng)名稱)必須唯一,start_urls作為初始的URL,可以對(duì)其進(jìn)行重寫(xiě),一旦重寫(xiě)則start_urls立即失效。

在編寫(xiě)爬蟲(chóng)前我們先對(duì)網(wǎng)站進(jìn)行分析,開(kāi)始從http://www.ituring.com.cn/book主頁(yè)獲取每本書(shū)籍詳情頁(yè)鏈接,后面將要請(qǐng)求的request發(fā)送給調(diào)度器,接著使用編寫(xiě)的回調(diào)函數(shù)對(duì)詳情頁(yè)進(jìn)行解析。

通過(guò)檢查元素發(fā)現(xiàn)書(shū)籍在a標(biāo)簽下的href屬性值里,并且因?yàn)槭窍鄬?duì)地址,還需要拼接成絕對(duì)地址。

檢查元素

進(jìn)入詳情頁(yè)http://www.ituring.com.cn/book/1927使用谷歌瀏覽器插件 XPath Helper提取需要的字段的XPath進(jìn)行調(diào)試(這里也可以用檢查元素進(jìn)行Copy XPath)。

選擇器

下面開(kāi)始我們的第一個(gè)爬蟲(chóng),打開(kāi)ituring/ituring/spiders目錄下的ituring_spider.py

import scrapy

from ituring.items import IturingItem


class IturingSpiderSpider(scrapy.Spider):
    name = 'ituring_spider'  # 爬蟲(chóng)名稱
    allowed_domains = ['ituring.com.cn']  # 允許爬取的域名地址
    start_urls = ['http://www.ituring.com.cn/book']  # 初始URL

    def parse(self, response):
        """
        初始URl默認(rèn)使用該解析方法
        """
        for book in response.xpath('//div[@class="book-img"]/a/@href'):
            url = response.urljoin(book.extract())  # 從列表頁(yè)獲取書(shū)籍URL鏈接
            yield scrapy.Request(url, callback=self.parse_book_info)  # 發(fā)送給調(diào)度器,回調(diào)函數(shù)使用parse_book_info

    def parse_book_info(self, response):
        """
        解析圖書(shū)詳情頁(yè)信息
        """
        item = IturingItem()  # 定義Item
        item['title'] = response.xpath('//div[@class="book-title"]/h2/text()').extract_first()
        item['link'] = response.url
        item['img'] = response.xpath('//div[@class="book-img"]/a/img/@src').extract_first()
        item['up_count'] = response.xpath('//*[@id="toggle-vote"]/span[1]/text()').extract_first()
        item['read_count'] = response.xpath('//*[@id="book-fav-vote"]/div/span[1]/text()').extract_first()
        item['price'] = response.xpath('//dl/dd/span[@class="price"]/text()').extract_first()
        yield item

啟動(dòng)爬蟲(chóng),抓取數(shù)據(jù)

到這里,我們的爬蟲(chóng)程序基本雛形已經(jīng)完成,現(xiàn)在可以運(yùn)行我們的爬蟲(chóng)程序,在命令行下輸入:

scrapy crawl ituring_spider

啟動(dòng)爬蟲(chóng)后,將得到類(lèi)似以下的輸出信息:

終端輸出信息

可以看到我們的爬蟲(chóng)程序已經(jīng)爬取到詳情頁(yè)信息數(shù)據(jù),但仍然有問(wèn)題數(shù)據(jù)里多了很多無(wú)用的字符例如\r、\n。

項(xiàng)目管道(Item Pipeline)

當(dāng)Spider最終處理的Item之后,會(huì)被傳遞到項(xiàng)目管道,管道按順序進(jìn)行執(zhí)行處理,在這里我們可以定義處理清除無(wú)用字符串的Pipeline。另外需要判斷進(jìn)入管道的Item是否有需要處理的字段,因?yàn)槲覀円苍S有很多的Item進(jìn)入到管道里。通過(guò)管道將Item里字段多余的無(wú)用字符刪除掉,達(dá)到清理數(shù)據(jù)的效果。

打開(kāi)pipelines.py文件,看到默認(rèn)的管道類(lèi):

class IturingPipeline(object):
    def process_item(self, item, spider):
        return item

處理后的Item最終需要return,下面開(kāi)始自定義我們的管道吧!

class StripPipeline(object):
    """
    清除無(wú)用字符
    """

    def process_item(self, item, spider):
        if item['price']:
            item['price'] = item['price'].replace(' ', '').replace('\r', '').replace('\n', '').replace('¥', '')
        if item['title']:
            item['title'] = item['title'].replace(' ', '').replace('\r', '').replace('\n', '')
        return item

翻頁(yè)爬取

在這之前我們還只是爬取單頁(yè)的URL鏈接,而爬取多頁(yè)則需要分析網(wǎng)頁(yè)的翻頁(yè)。通過(guò)點(diǎn)擊下一頁(yè),發(fā)現(xiàn)原來(lái)的URL跳轉(zhuǎn)到http://www.ituring.com.cn/book?tab=book&sort=hot&page=1,看出page參數(shù)是翻頁(yè)頁(yè)碼,起始頁(yè)是0。使用構(gòu)造頁(yè)碼的方式可以遍歷所有的頁(yè)碼頁(yè)面,當(dāng)沒(méi)有獲取到對(duì)應(yīng)數(shù)據(jù)則停止。進(jìn)一步分析發(fā)現(xiàn)最有一頁(yè)沒(méi)有下一頁(yè),而下一頁(yè)則正是我們接下來(lái)要爬取的頁(yè)面。所以可以通過(guò)判斷當(dāng)前頁(yè)面是否有下一頁(yè),如果有則從“下一頁(yè)”標(biāo)簽中的鏈接開(kāi)始爬取,如果沒(méi)有下一頁(yè)則爬取完后停止程序。

第一頁(yè)
最后一頁(yè)

打開(kāi)ituring_spider.py文件,添加翻頁(yè)代碼:

import scrapy

from ituring.items import IturingItem


class IturingSpiderSpider(scrapy.Spider):
    name = 'ituring_spider'  # 爬蟲(chóng)名稱
    allowed_domains = ['ituring.com.cn']  # 允許爬取的域名地址
    start_urls = ['http://www.ituring.com.cn/book']  # 初始URL

    def parse(self, response):
        """
        初始URl默認(rèn)使用該解析方法
        """
        for book in response.xpath('//div[@class="book-img"]/a/@href'):
            url = response.urljoin(book.extract())  # 從列表頁(yè)獲取書(shū)籍URL鏈接
            yield scrapy.Request(url, callback=self.parse_book_info)  # 發(fā)送給調(diào)度器,回調(diào)函數(shù)使用parse_book_info

        next_page = response.xpath('//div/ul/li[@class="PagedList-skipToNext"]/a/@href')

        if next_page:
            url = response.urljoin(next_page.extract_first())  # 下一頁(yè)的鏈接
            yield scrapy.Request(url, callback=self.parse)

    def parse_book_info(self, response):
        """
        解析圖書(shū)詳情頁(yè)信息
        """
        item = IturingItem()  # 定義Item
        item['title'] = response.xpath('//div[@class="book-title"]/h2/text()').extract_first()
        item['link'] = response.url
        item['img'] = response.xpath('//div[@class="book-img"]/a/img/@src').extract_first()
        item['up_count'] = response.xpath('//*[@id="toggle-vote"]/span[1]/text()').extract_first()
        item['read_count'] = response.xpath('//*[@id="book-fav-vote"]/div/span[1]/text()').extract_first()
        item['price'] = response.xpath('//dl/dd/span[@class="price"]/text()').extract_first()
        yield item

配置settings

不管是管道還是中間件,都需要到setting文件里面進(jìn)行設(shè)置啟用,這步可以可以在編寫(xiě)完管道或中間件后進(jìn)行。
設(shè)置存放在settings.py文件,打開(kāi)后編輯添加配置信息:

  1. 激活管道
    管道對(duì)應(yīng)的值越大則越后通過(guò),這里可以看到兩個(gè)管道,默認(rèn)管道和處理無(wú)用字符的管道。
ITEM_PIPELINES = {
    'ituring.pipelines.IturingPipeline': 300,
    'ituring.pipelines.StripPipeline': 400,
}
  1. 設(shè)置延時(shí)
    因?yàn)榕老x(chóng)程序設(shè)計(jì)爬取到多頁(yè)面,為防止對(duì)服務(wù)器造成影響和可能被kill掉,需要添加每次請(qǐng)求延時(shí)時(shí)間。
DOWNLOAD_DELAY = 3

保存數(shù)據(jù)

最簡(jiǎn)單存儲(chǔ)爬取的數(shù)據(jù)的方式是使用Feed exports,其自帶的類(lèi)型有:

  • JSON
  • JSON lines
  • CSV
  • XML
    你也可以通過(guò)FEED_EXPORTERS設(shè)置擴(kuò)展支持的屬性,也可以存儲(chǔ)到數(shù)據(jù)庫(kù)里,數(shù)據(jù)庫(kù)推薦使用MongoDB。

在啟動(dòng)爬蟲(chóng)的命令后面加上-o file_name.json將對(duì)爬取的數(shù)據(jù)進(jìn)行序列化并采用JSON格式存儲(chǔ),生成文件file_name.json文件。
scrapy crawl ituring_spider -o items.json

items.json

最后打開(kāi)items.json文件查看數(shù)據(jù)發(fā)現(xiàn)title標(biāo)題數(shù)據(jù)中文亂碼,打開(kāi)settings.py文件配置FEED_EXPORTERS導(dǎo)出的編碼方式:

# 設(shè)置輸出格式
# FEED_EXPORT_ENCODING = 'utf-8'
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容