DC-01:爬蟲(chóng)框架scrapy入門(mén)

本主題主要是scrapy入門(mén),包含內(nèi)容如下:
??1. Scrapy框架環(huán)境搭建;
??2. 理解scrapy框架結(jié)構(gòu);
??3. 理解并能處理簡(jiǎn)單的數(shù)據(jù)流;
??
如果想關(guān)注爬蟲(chóng)的高級(jí)技術(shù)與應(yīng)用場(chǎng)景,請(qǐng)關(guān)注后繼內(nèi)容與馬哥教育。這個(gè)系列包括數(shù)據(jù)采集,數(shù)據(jù)分析與數(shù)據(jù)可視化。


一、Scrapy安裝

??Scrapy的安裝是比較簡(jiǎn)單的,直接使用pip可以完成最新版本的安裝。
??
??目前最新版本是:1.6。

1.官網(wǎng)地址

??Scrapy的官方地址是:https://scrapy.org !

scrapy官網(wǎng)截圖

2. 安裝

??安裝指令


pip install scrapy

??安裝過(guò)程
安裝過(guò)程截圖

3. 測(cè)試安裝

??只需要啟動(dòng)python交互式編程終端,看看能否import模塊scrapy即可;
安裝測(cè)試截圖

4. scrapy幫助

??在交互式編程終端,使用import引入scrapy模塊,并使用help(scrapy)獲取scrapy框架API整體模塊結(jié)構(gòu)(其他更加詳細(xì)的幫助可以使用help與dir獲?。?div id="u0z1t8os" class="image-package">
scrapy幫助使用截圖

5. 教程與參考資料

??最好的教程我個(gè)人認(rèn)為還是官方的教程+API幫助。
官方教程與API截圖

二、Scrapy組件結(jié)構(gòu)與工作流程

1. 核心組件介紹

1.1. 組件01:引擎(Scrapy Engine)

??Scrapy Engine引擎作用有兩個(gè):
????1. 控制Scrapy框架中所有組件之間的數(shù)據(jù)流;
????2. 并在某些爬取動(dòng)作發(fā)生的時(shí)候觸發(fā)數(shù)據(jù)處理事件。

1.2. 組件02:調(diào)度器(Scheduler)

??調(diào)度器(Scheduler)接收來(lái)自引擎的請(qǐng)求,并將它們排隊(duì),以便在引擎請(qǐng)求時(shí)向引擎提供這些請(qǐng)求。

1.3. 組件03:下載器(Downloader)

??下載者(Downloader)負(fù)責(zé)下載網(wǎng)頁(yè)并將其發(fā)送給引擎,引擎把下載的網(wǎng)頁(yè)發(fā)送給蜘蛛/爬蟲(chóng)處理。

1.4. 組件04:蜘蛛/爬蟲(chóng)(Spiders)

??蜘蛛/爬蟲(chóng)(Spider)負(fù)責(zé)解析下載器下載的網(wǎng)頁(yè),并從中提取數(shù)據(jù)項(xiàng)(ITEM)(也稱為爬取項(xiàng))或后續(xù)的爬取請(qǐng)求。
??蜘蛛/爬蟲(chóng)(Spider)一般由用戶實(shí)現(xiàn),用來(lái)實(shí)現(xiàn)用戶的爬取邏輯。一般繼承Spider類(lèi)來(lái)定制實(shí)現(xiàn)。

1.5. 組件05:數(shù)據(jù)項(xiàng)管道(Item Pipeline)

??數(shù)據(jù)項(xiàng)管道(Item Pipeline)負(fù)責(zé)處理被蜘蛛/爬蟲(chóng)提?。ɑ蚺廊。┑臄?shù)據(jù)項(xiàng)。典型的任務(wù)包括:
????1. 清理;
????2. 驗(yàn)證;
????3. 持久存儲(chǔ)(如將數(shù)據(jù)項(xiàng)存儲(chǔ)在數(shù)據(jù)庫(kù)關(guān)系數(shù)據(jù)庫(kù)或者NoSQL數(shù)據(jù)庫(kù)中)。

1.6. 組件06:下載器中間件(Downloader middlewares)

??下載器中間件(Downloader middlewares)是位于引擎和下載器之間的特定功能的回調(diào)Hook,負(fù)責(zé)處理從引擎?zhèn)鬟f到下載器時(shí)處理請(qǐng)求,以及從下載器傳遞到引擎的響應(yīng)。
??使用下載器中間件(Downloader middlewares)的幾種情況:
????1. 在將請(qǐng)求發(fā)送給下載者之前處理該請(qǐng)求(即在Scrapy將請(qǐng)求發(fā)送到網(wǎng)站之前);
????2. 在傳遞給spider之前改變接受到的響應(yīng);
????3. 重新發(fā)送新的請(qǐng)求,而不是將收到的響應(yīng)傳遞給spider;
????4. 在沒(méi)有爬取到網(wǎng)頁(yè)的情況下,發(fā)送一個(gè)響應(yīng)給spider;
????5. 需要根據(jù)條件放棄一些請(qǐng)求。

1.7. 組件07:蜘蛛/爬蟲(chóng)中間件(Spider middlewares)

??蜘蛛/爬蟲(chóng)中間件(Spider middlewares)是位于引擎和蜘蛛之間的特定功能的Hook,負(fù)責(zé)處理Spider的輸入(響應(yīng))和輸出(數(shù)據(jù)項(xiàng)或者請(qǐng)求)。
??蜘蛛/爬蟲(chóng)中間件(Spider middlewares)的幾種情況:
????1. spider回調(diào)的輸出后的處理:包含:更改/添加/刪除請(qǐng)求或數(shù)據(jù)項(xiàng);
????2. 開(kāi)始請(qǐng)求的后處理;
????3. 處理spider異常;
????4. 對(duì)一些基于響應(yīng)內(nèi)容的請(qǐng)求調(diào)用errback,而不是回調(diào)。

2. 核心工作流程

??Scrapy的工作流程是按照爬取的數(shù)據(jù)設(shè)計(jì)的流程,并據(jù)此設(shè)計(jì)組件結(jié)構(gòu)。(下圖是來(lái)自Scrapy的官方文檔)
scrapy框架核心組件與數(shù)據(jù)流示意圖

2.1. 流程01-獲取請(qǐng)求

??引擎從蜘蛛獲取需要爬取的初始請(qǐng)求。
????源:Spider
????目標(biāo):Engine
????數(shù)據(jù):請(qǐng)求

2.2. 流程02-請(qǐng)求調(diào)度安排

??引擎調(diào)度爬取請(qǐng)求到調(diào)度器,并申請(qǐng)下一個(gè)需要爬取的爬取請(qǐng)求。
????源:Engine
????目標(biāo):Scheduler
????數(shù)據(jù):請(qǐng)求

2.3. 流程03-調(diào)度爬取請(qǐng)求

??引擎器返回下一個(gè)爬取請(qǐng)求給引擎。(為什么不直接爬取,而是需要經(jīng)過(guò)調(diào)度器處理呢?調(diào)度的好處在于:多任務(wù)爬取,還可以處理爬取請(qǐng)求與爬取過(guò)程的時(shí)間不一致的時(shí)間差。)
????源:Scheduler
????目標(biāo):Engine
????數(shù)據(jù):請(qǐng)求

2.4. 流程04-發(fā)送請(qǐng)求給下載器

??引擎將請(qǐng)求發(fā)送到下載器,并通過(guò)下載器中間軟件傳遞(process_request回調(diào)函數(shù)可以處理請(qǐng)求數(shù)據(jù))。
????源:Engine
????目標(biāo):Downloader
????數(shù)據(jù):請(qǐng)求

2.5. 流程05-下載器完成下載

??一旦頁(yè)面下載器完成頁(yè)面下載,下載器將使用下載好的頁(yè)面生成一個(gè)響應(yīng)(使用該頁(yè)面),并將其發(fā)送到引擎,通過(guò)下載器中間軟件(process_response回調(diào)函數(shù)完成下載后的頁(yè)面數(shù)據(jù)處理)。
????源:Downloader
????目標(biāo):Engine
????數(shù)據(jù):響應(yīng)

2.6. 流程06-數(shù)據(jù)項(xiàng)抽取

??引擎從下載器接收響應(yīng)并將其發(fā)送到spider進(jìn)行處理,并通過(guò)spider中間件進(jìn)行處理(process_spider_input回調(diào)函數(shù)處理爬取的網(wǎng)頁(yè)數(shù)據(jù))。
????源:Engine
????目標(biāo):Spider
????數(shù)據(jù):響應(yīng)

2.7. 流程07-返回抽取的數(shù)據(jù)與請(qǐng)求

??Spider處理響應(yīng)(從爬取的網(wǎng)頁(yè)中抽取需要的數(shù)據(jù)項(xiàng)),并通過(guò)spider中間件(process_spider_output回調(diào)函數(shù)處理Spider處理過(guò)的數(shù)據(jù))向引擎返回抽取的數(shù)據(jù)項(xiàng)或者新的附加請(qǐng)求。
????源:Spider
????目標(biāo):Engine
????數(shù)據(jù):數(shù)據(jù)項(xiàng)(附加的請(qǐng)求)

2.8. 流程08-存儲(chǔ)抽取的數(shù)據(jù)項(xiàng)

??引擎將已處理的數(shù)據(jù)項(xiàng)發(fā)送到數(shù)據(jù)項(xiàng)管道,然后將已附加的請(qǐng)求發(fā)送到調(diào)度程序,并請(qǐng)求可能的下一個(gè)請(qǐng)求進(jìn)行爬取。
????源:Engine
????目標(biāo):Item Pipeline/Scheduler
????數(shù)據(jù):數(shù)據(jù)項(xiàng)/附加請(qǐng)求

2.9. 流程09-結(jié)束爬取

??重復(fù)01-08,直到調(diào)度器中沒(méi)有請(qǐng)求調(diào)度為止。

三、Scrapy入門(mén)

??這個(gè)入門(mén)是按照官方的教程組織。
??不是上面介紹的每個(gè)組件都需要我們開(kāi)發(fā),實(shí)際只需要我們開(kāi)發(fā)業(yè)務(wù)部分,爬蟲(chóng)的通用功能部分都封裝到框架中,所以我們需要一個(gè)框架的環(huán)境,并理解整個(gè)工作流程,并關(guān)注需要開(kāi)發(fā)的部分,以及開(kāi)發(fā)部分與整個(gè)框架組件的關(guān)系。

1. 創(chuàng)建一個(gè)爬蟲(chóng)項(xiàng)目

??爬蟲(chóng)項(xiàng)目使用scrapy框架提供的一個(gè)工具創(chuàng)建:scrapy,該工具創(chuàng)建的項(xiàng)目會(huì)提供業(yè)務(wù)部分運(yùn)行的環(huán)境與配置。

1.1. scrapy工具介紹

1.1.1. 獲取scrapy工具幫助

??直接在終端輸入scrapy,可以直接輸出scrapy工具的幫助。
命令:

localhost:~ yangqiang$ scrapy 
scrapy工具幫助獲取截圖

??其中startproject命令項(xiàng)就是我們馬上要使用來(lái)創(chuàng)建項(xiàng)目的。

1.1.2. 獲取startproject命令項(xiàng)幫助

??獲取幫助的指令:


localhost:~ yangqiang$ scrapy startproject -h

創(chuàng)建爬蟲(chóng)項(xiàng)目指令startproject幫助獲取截圖

1.2. 使用scrapy工具創(chuàng)建一個(gè)爬蟲(chóng)項(xiàng)目

??創(chuàng)建爬蟲(chóng)項(xiàng)目的兩個(gè)重要參數(shù):
????1. 項(xiàng)目名稱
????2. 項(xiàng)目存放目錄(可選,默認(rèn)當(dāng)前目錄)

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


localhost:codes yangqiang$ scrapy startproject FirstScrapy

創(chuàng)建項(xiàng)目過(guò)程與結(jié)果截圖

1.2.2. 創(chuàng)建好的項(xiàng)目文件

??可以查看創(chuàng)建的項(xiàng)目,其中的文件結(jié)合上面的組件與工作流程,大致也知道其用途與作用。
創(chuàng)建的爬蟲(chóng)項(xiàng)目工程

1.3. 使用pycharn打開(kāi)創(chuàng)建的項(xiàng)目

??可以使用pycharm IDE工具打開(kāi)創(chuàng)建的爬蟲(chóng)項(xiàng)目:


使用pycharm打開(kāi)爬蟲(chóng)項(xiàng)目工程

2. 實(shí)現(xiàn)爬蟲(chóng)業(yè)務(wù)

??因?yàn)樵诳蚣茉陂_(kāi)發(fā),為了保證框架能順利工作,需要按照設(shè)計(jì)的結(jié)構(gòu),繼承scrapy.Spider類(lèi),并發(fā)起一個(gè)爬取請(qǐng)求,并處理。

2.1. 創(chuàng)建爬蟲(chóng)代碼模塊

??在項(xiàng)目的spiders包路徑下,創(chuàng)建一個(gè)python源代碼文件:spiders.home_spider.py。

使用pycharm創(chuàng)建的爬蟲(chóng)代碼模塊

2.2. 繼承Spider

??由于Spider是抽象類(lèi),需要override其中的抽象函數(shù)。


   def parse(self, response):
        raise NotImplementedError('{}.parse callback is not defined'.format(self.__class__.__name__))

??繼承Spider類(lèi)后的子類(lèi):

# coding = utf-8
import scrapy


class HomeSpider(scrapy.Spider):

    def parse(self, response):
        pass

2.3. 爬蟲(chóng)中兩個(gè)重要的屬性

2.3.1. name屬性

??name屬性,用來(lái)在執(zhí)行爬蟲(chóng)的時(shí)候指定爬蟲(chóng)。屬性類(lèi)型是字符串。

2.3.2. start_urls屬性

??start_urls屬性用來(lái)發(fā)起一個(gè)爬蟲(chóng)任務(wù)請(qǐng)求。屬性類(lèi)型是列表。

2.3.3. 屬性實(shí)現(xiàn)代碼

# coding = utf-8
import scrapy


class HomeSpider(scrapy.Spider):
    name = 'home'
    start_urls = [
        'https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1',
    ]
    
    def parse(self, response):
        pass

3. scrapy工具與運(yùn)行爬蟲(chóng)項(xiàng)目

3.1. 在項(xiàng)目目錄下的scrapy工具的幫助

??在scrapy工具創(chuàng)建的爬蟲(chóng)項(xiàng)目頂層目錄下,執(zhí)行scrapy工具獲取的幫助會(huì)更多。
??命令:


localhost:FirstScrapy yangqiang$ scrapy

??執(zhí)行效果:

在爬蟲(chóng)項(xiàng)目目錄下獲取scrapy工具與在其他地方得到的幫助內(nèi)容存在不同

??與項(xiàng)目相關(guān)的命令有(6個(gè)):
????list:列出項(xiàng)目中爬蟲(chóng)列表。
????check:檢查爬蟲(chóng)。
????crawl:?jiǎn)?dòng)爬蟲(chóng)任務(wù)。
????edit:編輯爬蟲(chóng)。
????fetch:獲取。
????parse:解析。
??上述命令的使用,使用幫助可以獲取。具體的使用在后面會(huì)介紹。

scrapy的edit指令幫助截圖

3.2. list爬蟲(chóng)

??命令:

localhost:FirstScrapy yangqiang$ scrapy list

??效果:
list指令使用截圖

3.3. edit爬蟲(chóng)

??命令:

localhost:FirstScrapy yangqiang$ scrapy edit home

??一般不使用這個(gè)指令在字符界面下編輯,而是使用IDE工具編輯。不過(guò)遠(yuǎn)程維護(hù)使用字符界面是非常方便的,

??效果:
edit指令使用截圖

3.4. crawl爬蟲(chóng)-運(yùn)行爬蟲(chóng)

??命令:

localhost:FirstScrapy yangqiang$ scrapy crawl home

??使用該命令啟動(dòng)爬蟲(chóng)任務(wù),其中home是爬蟲(chóng)程序中name指定的爬蟲(chóng)名。
??效果:

crawl指令執(zhí)行截圖

3.5. check爬蟲(chóng)

??命令:

localhost:FirstScrapy yangqiang$ scrapy check home

??檢查爬蟲(chóng)代碼中的錯(cuò)誤。

??下面是沒(méi)有錯(cuò)誤的例子:
對(duì)沒(méi)有錯(cuò)誤的爬蟲(chóng)check執(zhí)行的截圖

??下面是有錯(cuò)誤的例子(隨便在代碼中設(shè)計(jì)幾個(gè)錯(cuò)誤即可)。
對(duì)存在語(yǔ)法錯(cuò)誤的爬蟲(chóng)代碼執(zhí)行check的截圖

3.6. parse爬蟲(chóng)

??parse命令用來(lái)測(cè)試爬蟲(chóng)的parse函數(shù)。
??parse命令的幫助:

parse指令的幫助

??命令:

localhost:FirstScrapy yangqiang$ scrapy parse --spider=home https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1

??效果(當(dāng)沒(méi)有指定爬蟲(chóng)名,這會(huì)使用url直接創(chuàng)建一個(gè)爬蟲(chóng),但是scrapy存在bug,會(huì)報(bào)錯(cuò),在github上已經(jīng)有人修正這個(gè)bug,可以通過(guò)百度找到這個(gè)帖子):


parse指令執(zhí)行的截圖

3.7. fetch爬蟲(chóng)

??直接下載頁(yè)面,并顯示在終端。
??命令:

localhost:FirstScrapy yangqiang$ scrapy fetch https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1

??效果:


fetch指令執(zhí)行截圖

3.8. genspider生成爬蟲(chóng)

??genspider可以查看爬蟲(chóng)模板,創(chuàng)建爬蟲(chóng),編輯爬蟲(chóng)等功能。其幫助如下:


genspider指令的幫助

3.8.1. 查看模板

??一般默認(rèn)的模板是basic。
??命令:

localhost:FirstScrapy yangqiang$ scrapy genspider -l

??效果:


查看已有的爬蟲(chóng)模板

3.8.2. 創(chuàng)建爬蟲(chóng)

??命令:

localhost:FirstScrapy yangqiang$ scrapy genspider -t crawl  myspider https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1

??效果:
使用genspide創(chuàng)建爬蟲(chóng)的過(guò)程截圖

??創(chuàng)建后,在項(xiàng)目工程中能看見(jiàn)這個(gè)爬蟲(chóng)代碼文件:!
在pycharm中查看創(chuàng)建的爬蟲(chóng)代碼模塊

3.8.3. 創(chuàng)建并編輯爬蟲(chóng)

??命令:

localhost:FirstScrapy yangqiang$ scrapy genspider -e  myspider 'https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1'

??這個(gè)命令是先創(chuàng)建,后編輯。注意:編輯中需要項(xiàng)目的模塊導(dǎo)入,否則編輯不會(huì)正常。不過(guò)一般創(chuàng)建好以后,也不會(huì)在終端下編輯。

3.9. view查看爬取頁(yè)面

??該命令首先下載頁(yè)面,然后使用瀏覽器打開(kāi)。
??命令:

localhost:FirstScrapy yangqiang$ scrapy  view https://www.baidu.com

??效果:
view指令執(zhí)行截圖

3.10. shell交互式爬蟲(chóng)處理

??使用交互式處理爬蟲(chóng)數(shù)據(jù)處理。與代碼一樣,只是交互式開(kāi)發(fā)模式。
??命令:

localhost:FirstScrapy yangqiang$ scrapy shell 'https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1'

??效果(綠色的表示代碼輸入):
shell交互式爬蟲(chóng)處理截圖

??交互式編程示例:
交互式代碼輸入與執(zhí)行截圖

3.11. settings獲取settings.py中的配置

??settings指令用來(lái)獲取與設(shè)置settings.py中的配置值。
??命令:

localhost:FirstScrapy yangqiang$ scrapy settings --get=SPIDER_MODULES

??效果:
使用settings指令獲取settings文件中定義的配置

??下面是settings.py文件:
settings.py文件截圖

3.12. runspider運(yùn)行爬蟲(chóng)文件

??這個(gè)命令是直接執(zhí)行爬蟲(chóng)代碼文件,與crawl的區(qū)別在于,crawl執(zhí)行的是爬蟲(chóng)項(xiàng)目spiders目錄下的有爬蟲(chóng)名的爬蟲(chóng)。
??命令:

localhost:FirstScrapy yangqiang$ scrapy runspider ./FirstScrapy/spiders/home_spider.py

??效果:
獨(dú)立于爬蟲(chóng)項(xiàng)目來(lái)執(zhí)行某個(gè)爬蟲(chóng)代碼

四、爬蟲(chóng)中的數(shù)據(jù)流與數(shù)據(jù)處理

1. 創(chuàng)建一個(gè)測(cè)試項(xiàng)目

??使用scrapy工具創(chuàng)建一個(gè)爬蟲(chóng)數(shù)據(jù)流與數(shù)據(jù)處理的測(cè)試項(xiàng)目。
??創(chuàng)建命令:

localhost:codes yangqiang$ scrapy  startproject  Scra_DataFlow

??創(chuàng)建過(guò)程:
創(chuàng)建一個(gè)數(shù)據(jù)流演示項(xiàng)目工程

??下面主要關(guān)注點(diǎn)在數(shù)據(jù)上,所以其他scarpy工具等使用細(xì)節(jié)可以參考上面的幫助。
??同時(shí),scrapy爬蟲(chóng)框架提供了一些快捷實(shí)現(xiàn)方式,下面都采用傳統(tǒng)的思路實(shí)現(xiàn),這樣容易理解,快捷方式的介紹不作為重點(diǎn),甚至不在這里介紹。

2. 爬蟲(chóng)目標(biāo)

2.1. 爬取站點(diǎn)

??爬取騰訊課堂上的數(shù)據(jù)。爬取騰訊課堂基礎(chǔ)課程中的Python付費(fèi)課程信息。
爬取目標(biāo)網(wǎng)站截圖

2.2. 爬取數(shù)據(jù)

??課程名稱,培訓(xùn)機(jī)構(gòu),購(gòu)買(mǎi)人數(shù),課程價(jià)格,開(kāi)課方式等。

2.3. 理解domain與url

??domain:https://ke.qq.com
??urls:https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1

3. 使用命令創(chuàng)建爬蟲(chóng)

??使用scrapy的genspider指令創(chuàng)建spider代碼模板。
??命令:

localhost:Scra_DataFlow yangqiang$ scrapy genspider -t basic  course  ke.qq.com
使用genspider用basic模板創(chuàng)建一個(gè)爬蟲(chóng)模塊

??創(chuàng)建好的代碼如下:

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


class CourseSpider(scrapy.Spider):
    name = 'course'
    allowed_domains = ['ke.qq.com']
    start_urls = ['http://ke.qq.com/']

    def parse(self, response):
        pass


??在Pycharm中,代碼在項(xiàng)目中的截圖:


在pycharm中打開(kāi)創(chuàng)建的爬蟲(chóng)代碼

4. 爬取URL

??實(shí)際需要爬取的頁(yè)面URL為:
????https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1&page=1

??這是scrapy框架數(shù)據(jù)流的第01步。從爬蟲(chóng)發(fā)起一個(gè)請(qǐng)求。
??該請(qǐng)求,使用迭代器的方式返回給爬蟲(chóng)引擎:
????方式一:是引擎調(diào)用def start_requests(self)函數(shù),通過(guò)返回值得到請(qǐng)求。
????方式二:覆蓋父類(lèi)的start_urls屬性,用來(lái)替代默認(rèn)的 start_requests函數(shù),返回請(qǐng)求。

??然后爬蟲(chóng)引擎,把請(qǐng)求發(fā)送給調(diào)度器(流程02),根據(jù)處理資源的情況,引擎從調(diào)度器獲取請(qǐng)求(流程03),再發(fā)送給下載器(流程04)。

??下載器使用引擎發(fā)送過(guò)來(lái)的請(qǐng)求,完成下載任務(wù),并把下載的響應(yīng)返回給引擎(流程05)。引擎把響應(yīng)通過(guò)parse函數(shù)傳遞給爬蟲(chóng)程序(流程06),爬蟲(chóng)通過(guò)parse函數(shù)參數(shù),得到下載響應(yīng)。其中想用通過(guò)response對(duì)象獲取,該響應(yīng)對(duì)象的類(lèi)型是:<class 'scrapy.http.response.html.HtmlResponse'>。

??注意:流程01,02,03,04,05,06都是框架自動(dòng)完成,如果不做特別的處理,可以不干預(yù)框架的流程,直接在流程06的結(jié)束得到一個(gè)響應(yīng)對(duì)象。

??scrapy框架的數(shù)據(jù)流,注意其中的編號(hào),對(duì)應(yīng)這我們這里的一樣的編號(hào)。
為了查看方便,再顯示下官方的數(shù)據(jù)流示意圖

5. 通過(guò)parse函數(shù)的參數(shù),獲取下載響應(yīng)

5.1. scrapy.http.response.html.HtmlResponse類(lèi)

??數(shù)據(jù)成員:
????|- encoding
????|- selector
????|- text
????|- body
????|- meta
????|- url
??函數(shù)成員:
????|- body_as_unicode(self)
??????|- 返回一個(gè)unicode的body
????|- css(self, query)
????follow(self, url, callback=None, method='GET', headers=None, body=None, cookies=None, meta=None, encoding=None, priority=0, dont_filter=False, errback=None)
??????|- 返回Request類(lèi)型的對(duì)象。
????|- replace(self, *args, **kwargs)
????|- urljoin(self, url)
????|- xpath(self, query, **kwargs)

??獲取解碼后的文本頁(yè)面內(nèi)容。
????|- text
??數(shù)據(jù)解析接口:
????|- css(self, query)
????|- xpath(self, query, **kwargs)

5.2. 直接處理(中斷數(shù)據(jù)流)

5.2.1. 直接保存成文件

??這類(lèi)直接保存成html文本文件。

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


class CourseSpider(scrapy.Spider):
    name = 'course'
    allowed_domains = ['ke.qq.com']
    start_urls = ['https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1&page=1']

    def parse(self, response):
        # 2019-02-14 15:00:14 [course] DEBUG: <class 'scrapy.http.response.html.HtmlResponse'>
        # self.log(type(response))
        # utf-8
        # self.log(response.encoding)
        # <Selector xpath=None data='<html lang="zh">\n<head>\n    <meta charse'>
        # self.log(response.selector)
        # self.log(response.text)   # unicode的文本內(nèi)容
        # self.log(response.body)     # 二進(jìn)制內(nèi)容
        #  {'download_timeout': 180.0, 'download_slot': 'ke.qq.com', 'download_latency': 0.592094898223877}
        # self.log(response.meta)
        # https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1&page=1
        # self.log(response.url)
        # 保存下載的頁(yè)面到文件,護(hù)或者到數(shù)據(jù)庫(kù)(可以考慮文檔數(shù)據(jù)庫(kù)或者mysql關(guān)系數(shù)據(jù)庫(kù))
        with open('page.html', 'w') as fd:
            fd.write(response.text)

??下載后保存的文件內(nèi)容。
直接保存的下載頁(yè)面的截圖

5.2.2. 解析保存成csv文件

??從瀏覽器獲取XPATH格式(/html/body/section[1]/div/div[3]/ul):


從瀏覽器獲取xpath數(shù)據(jù)的截圖

??把瀏覽器中獲取的XPATH按照我們的需求修改:
????| - 瀏覽器XPATH:/html/body/section[1]/div/div[3]/ul
????| - 修改后XPATH://section[1]/div/div[3]/ul/li
??
??因?yàn)閄PATH是從body文檔開(kāi)始解析的,同時(shí)我們希望獲取頁(yè)面上24門(mén)課程的內(nèi)容。
??代碼如下:

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


class CourseSpider(scrapy.Spider):
    name = 'course'
    allowed_domains = ['ke.qq.com']
    start_urls = ['https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1&page=1']

    def parse(self, response):
        # 解析數(shù)據(jù),并保存成csv文件。
        result = response.xpath('//section[1]/div/div[3]/ul/li')
        self.log('XXXX:{}'.format(len(result)))

??運(yùn)行爬蟲(chóng)的輸出結(jié)果是:
使用xpath獲取到我們需要爬取得24們課程列表截圖

??xpath返回的是一個(gè)list列表,其中元素的類(lèi)型是:<class 'scrapy.selector.unified.Selector'>,該類(lèi)型的API幫助可以使用help獲取。
??下面我們可以直接解析得到需要的字段(保存到csv的實(shí)現(xiàn),這里就略掉,其中使用了xpath或者css,這個(gè)知識(shí)點(diǎn),下面專(zhuān)門(mén)講解)。

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


class CourseSpider(scrapy.Spider):
    name = 'course'
    allowed_domains = ['ke.qq.com']
    start_urls = ['https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1&page=1']

    def parse(self, response):
        # 解析數(shù)據(jù),并保存成csv文件。
        result = response.xpath('//section[1]/div/div[3]/ul/li')
        for course_ in result:
            # self.log(type(course_))
            # 課程名稱
            course_name = course_.xpath('h4[@class="item-tt"]/a/text()').get()
            self.log('課程名稱:{}'.format(course_name.strip() if course_name else ''))
            # 培訓(xùn)機(jī)構(gòu)
            course_organization = course_.xpath(
                'div[@class="item-line item-line--middle"]/span[@class="item-source"]/a/text()').get()
            self.log('培訓(xùn)機(jī)構(gòu):{}'.format(course_organization.strip() if course_organization else ''))
            # 課程連接
            course_link = course_.xpath('h4[@class="item-tt"]/a/@href').get()
            self.log('課程連接:{}'.format(course_link.strip() if course_link else ''))
            # 報(bào)名人數(shù)
            course_number = course_.xpath(
                'div[@class="item-line item-line--middle"]/span[@class="line-cell item-user"]/text()').get()
            self.log('報(bào)名人數(shù):{}'.format(course_number.strip()  if course_number else ''))
            # 課程狀態(tài)
            course_status = course_.xpath('div[@class="item-status"]/text()').get()
            self.log('課程狀態(tài):{}'.format(course_status.strip() if course_status else ''))
            # 課程價(jià)格
            course_price = course_.xpath('div[@class="item-line item-line--bottom"]/span/text()').get()
            self.log('課程價(jià)格:{}'.format(course_price.strip() if course_price else ''))

??解析的效果:
每門(mén)課程解析的結(jié)果輸出截圖

5.3. 返回?cái)?shù)據(jù)項(xiàng)到管道(流程07與流程08)

??在parse函數(shù)中,實(shí)際是可以返回?cái)?shù)據(jù)的:
????| - 返回的數(shù)據(jù)是Items,則引擎接受到數(shù)據(jù)后,會(huì)發(fā)送給管道Pipeline。
????| - 返回Request請(qǐng)求,則把Request發(fā)送給調(diào)度器,繼續(xù)爬取數(shù)據(jù)。
??
??這里只關(guān)注返回Items,返回Request的情路,后面說(shuō)明。

5.3.1. 定義Items的數(shù)據(jù)項(xiàng)

??繼承scrapy.Item類(lèi),對(duì)應(yīng)需要接續(xù)的字段定義數(shù)據(jù)項(xiàng)。


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

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class ScraDataflowItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 課程名稱
    course_name = scrapy.Field()
    # 培訓(xùn)機(jī)構(gòu)
    course_organization = scrapy.Field()
    # 課程連接
    course_link = scrapy.Field()
    # 報(bào)名人數(shù)
    course_number = scrapy.Field()
    # 課程狀態(tài)
    course_status = scrapy.Field()
    # 課程價(jià)格
    course_price = scrapy.Field()

5.3.2. 使用數(shù)據(jù)項(xiàng),緩存爬取的數(shù)據(jù)字段

??使用數(shù)據(jù)項(xiàng)比較模式化,比較容易理解。 下面是實(shí)現(xiàn)代碼:

# -*- coding: utf-8 -*-
import scrapy
from  Scra_DataFlow.items import ScraDataflowItem

class CourseSpider(scrapy.Spider):
    name = 'course'
    allowed_domains = ['ke.qq.com']
    start_urls = ['https://ke.qq.com/course/list?mt=1001&st=2002&tt=3019&price_min=1&page=1']

    def parse(self, response):
        result = response.xpath('//section[1]/div/div[3]/ul/li')
        items = []    # 數(shù)據(jù)項(xiàng)數(shù)組列表
        for course_ in result:
            # 數(shù)據(jù)項(xiàng)
            item_ = ScraDataflowItem()
            course_name = course_.xpath('h4[@class="item-tt"]/a/text()').get()
            item_['course_name'] = '{}'.format(course_name.strip() if course_name else '')
            # 培訓(xùn)機(jī)構(gòu)
            course_organization = course_.xpath(
                'div[@class="item-line item-line--middle"]/span[@class="item-source"]/a/text()').get()
            item_['course_organization'] = course_organization.strip() if course_organization else ''
            # 課程連接
            course_link = course_.xpath('h4[@class="item-tt"]/a/@href').get()
            item_['course_link'] = course_link.strip() if course_link else ''
            # 報(bào)名人數(shù)
            course_number = course_.xpath(
                'div[@class="item-line item-line--middle"]/span[@class="line-cell item-user"]/text()').get()
            item_['course_number'] = course_number.strip()  if course_number else ''
            # 課程狀態(tài)
            course_status = course_.xpath('div[@class="item-status"]/text()').get()
            item_['course_status'] = course_status.strip() if course_status else ''
            # 課程價(jià)格
            course_price = course_.xpath('div[@class="item-line item-line--bottom"]/span/text()').get()
            item_['course_price'] = course_price.strip() if course_price else ''
            items.append(item_)
        # 返回?cái)?shù)據(jù)項(xiàng)到管道
        return items

5.3.3. 使用管道保存數(shù)據(jù)

??使用命令:

localhost:Scra_DataFlow yangqiang$ scrapy crawl course  -o course.csv

??執(zhí)行過(guò)程:
輸出csv文件的爬蟲(chóng)執(zhí)行過(guò)程截圖

??提示:pycharm可以安裝csv的插件,用來(lái)顯示csv文件
pycharm的csv插件安裝截圖

??使用管道保存的文件(不使用插件在pycharm顯示):
使用管道保存的文件顯示截圖(pychar無(wú)插件文本顯示)

??使用管道保存的文件(使用插件在pycharm顯示)
使用pycharm插件顯示位表格

附錄

??需要掌握爬蟲(chóng)的高級(jí)應(yīng)用,以及一些經(jīng)典場(chǎng)景應(yīng)用,可以關(guān)注后繼內(nèi)容與馬哥教育。

最后編輯于
?著作權(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ù)。

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