1. 程式人生 > 其它 >Scrapy爬蟲框架,入門案例

Scrapy爬蟲框架,入門案例

目錄

一、概述

二、Scrapy五大基本構成:

三、整體架構圖

四、Scrapy安裝以及生成專案

五、日誌等級與日誌儲存

六、匯出為json或scv格式

七、一個完整的案例


一、概述

Scrapy,Python開發的一個快速、高層次的螢幕抓取和web抓取框架,用於抓取web站點並從頁面中提取結構化的資料。Scrapy用途廣泛,可以用於資料探勘、監測和自動化測試.

其最初是為了頁面抓取 (更確切來說, 網路抓取 )所設計的, 後臺也應用在獲取API所返回的資料(例如 Amazon Associates Web Services ) 或者通用的網路爬蟲.

Scrapy吸引人的地方在於它是一個框架,任何人都可以根據需求方便的修改。它也提供了多種型別爬蟲的基類,如BaseSpider、sitemap爬蟲等,最新版本又提供了web2.0爬蟲的支援.

二、Scrapy五大基本構成:

Scrapy框架主要由五大元件組成,它們分別是排程器(Scheduler)、下載器(Downloader)、爬蟲(Spider)和實體管道(Item Pipeline)、Scrapy引擎(Scrapy Engine)。下面我們分別介紹各個元件的作用。

(1)、排程器(Scheduler):

排程器,說白了把它假設成為一個URL(抓取網頁的網址或者說是連結)的優先佇列,由它來決定下一個要抓取的網址是 什麼,同時去除重複的網址(不做無用功)。使用者可以自己的需求定製排程器。

(2)、下載器(Downloader):

下載器,是所有元件中負擔最大的,它用於高速地下載網路上的資源。Scrapy的下載器程式碼不會太複雜,但效率高,主要的原因是Scrapy下載器是建立在twisted這個高效的非同步模型上的(其實整個框架都在建立在這個模型上的)。

(3)、 爬蟲(Spider):

爬蟲,是使用者最關心的部份。使用者定製自己的爬蟲(通過定製正則表示式等語法),用於從特定的網頁中提取自己需要的資訊,即所謂的實體(Item)。 使用者也可以從中提取出連結,讓Scrapy繼續抓取下一個頁面。

(4)、 實體管道(Item Pipeline):

實體管道,用於處理爬蟲(spider)提取的實體。主要的功能是持久化實體、驗證實體的有效性、清除不需要的資訊。

(5)、Scrapy引擎(Scrapy Engine):

Scrapy引擎是整個框架的核心.它用來控制偵錯程式、下載器、爬蟲。實際上,引擎相當於計算機的CPU,它控制著整個流程。

三、整體架構圖

本圖按順序說明整個程式執行時候發生的順序。

注意在呼叫下載器時,往往有一個下載器中介軟體,使下載速度提速。

官網架構圖

四、Scrapy安裝以及生成專案

新建一個專案,該專案的結構如下:

執行命令,widows和ubuntu命令格式是一樣的:

下載方式

ubuntu,開啟一個終端,輸入pip install scrapy(或pip3install scrapy)

widows ,開啟一個cmd,輸入pip install scrapy,前提是你裝了pip

詳細安裝請點這

scrapy startproject 專案名

scrapy genspider 爬蟲名 域名

scrapy crawl 爬蟲名

我使用的是widows版本,下面演示建立專案的例子

開啟cmd,輸入(預設是在C:\Users\Administrator>這個目錄下,你可以自行切換)

scrapy startproject myfirstPj

cd my firstPj

scrapy genspider baidu www.baidu.com

建立後目錄大致頁如下

|-ProjectName #專案資料夾

|-ProjectName #專案目錄

|-items.py #定義資料結構

|-middlewares.py #中介軟體

|-pipelines.py #資料處理

|-settings.py #全域性配置

|-spiders

|-__init__.py #爬蟲檔案

|-baidu.py

|-scrapy.cfg #專案基本配置檔案

spiders下的baidu.py是scrapy自動為我們生成的

下面再看一下spdier專案的配置檔案,開啟檔案settings.py

BOT_NAME:專案名

USER_AGENT:預設是註釋的,這個東西非常重要,如果不寫很容易被判斷為電腦,簡單點洗一個Mozilla/5.0即可

ROBOTSTXT_OBEY:是否遵循機器人協議,預設是true,需要改為false,否則很多東西爬不了

CONCURRENT_REQUESTS:最大併發數,很好理解,就是同時允許開啟多少個爬蟲執行緒

DOWNLOAD_DELAY:下載延遲時間,單位是秒,控制爬蟲爬取的頻率,根據你的專案調整,不要太快也不要太慢,預設是3秒,即爬一個停3秒,設定為1秒價效比較高,如果要爬取的檔案較多,寫零點幾秒也行

COOKIES_ENABLED:是否儲存COOKIES,預設關閉,開機可以記錄爬取過程中的COKIE,非常好用的一個引數

DEFAULT_REQUEST_HEADERS:預設請求頭,上面寫了一個USER_AGENT,其實這個東西就是放在請求頭裡面的,這個東西可以根據你爬取的內容做相應設定。

ITEM_PIPELINES:專案管道,300為優先順序,越低越爬取的優先度越高

比如我的pipelines.py裡面寫了兩個管道,一個爬取網頁的管道,一個存資料庫的管道,我調整了他們的優先順序,如果有爬蟲資料,優先執行存庫操作。

  1. ITEM_PIPELINES = {
  2. ‘scrapyP1.pipelines.BaiduPipeline’: 300,
  3. ‘scrapyP1.pipelines.BaiduMysqlPipeline’: 200,
  4. }

到這裡我們嘗試用scrapy做一下爬取,開啟spider.py下的baidu.py(取決於你scrapy genspider 爬蟲名 域名時輸入的爬蟲名)

輸入一下程式碼,我們使用xpath提取百度首頁的標題title

  1. importscrapy
  2. classBaiduSpider(scrapy.Spider):
  3. name =‘baidu’
  4. allowed_domains = [‘www.baidu.com’]
  5. start_urls = [‘http://www.baidu.com/’]
  6. defparse(self, response):
  7. tile=response.xpath(‘//html/head/title/text()’)
  8. print(tile)

開啟一個終端cmd,輸入scrapy crawl baidu(爬蟲名),就可以看到一大堆輸出資訊,而其中就包括我們要的內容

使用終端執行太麻煩了,而且不能提取資料,我們一個寫一個run檔案作為程式的入口,splite是必須寫的,目的是把字串轉為列表形式,第一個引數是scrapy,第二個crawl,第三個baidu

  1. fromscrapyimportcmdline
  2. cmdline.execute(‘scrapy crawl baidu’.split())

可以在編輯器中輸出了

五、日誌等級與日誌儲存

在setting.py裡面可以設定日誌的等級與日誌存放的路徑

相關變數

LOG_LEVEL= “”

LOG_FILE=”日誌名.log”

日誌等級分為

1.DEBUG 除錯資訊

2.INFO一般資訊

3.WARNING 警告

4.ERROR 普通錯誤

5.CRITICAL 嚴重錯誤

如果設定

LOG_LEVEL=”WARNING”,就只會WARNING等級之下的ERROR和CRITICAL

預設等級是1

六、匯出為json或scv格式

執行爬蟲檔案時新增-o選項即可

scrapy crawl 專案名 -o *.csv

scrapy crawl 專案名 -o *.json

對於json檔案,在setting.js檔案裡新增,設定編碼格式,否則會亂碼:

FEED_EXPORT_ENCODING=’utf-8′

示例:

  1. fromscrapyimportcmdline
  2. cmdline.execute(‘scrapy crawl baidu -o baidu.csv’.split())

七、一個完整的案例

這個專案我們的主題是爬騰訊視訊的電影資訊,包括電影名和描述

1.建立專案

開啟一個終端輸入(建議放到合適的路徑下,預設是C盤)

scrapy startproject TXmovies

cdTXmovies

scrapy genspider txms v.qq.com

2.修改setting

修改三項內容,第一個是不遵循機器人協議,第二個是下載間隙,由於下面的程式要下載多個頁面,所以需要給一個間隙(不給也可以,只是很容易被偵測到),第三個是請求頭,新增一個User-Agent,第四個是開啟一個管道

  1. ROBOTSTXT_OBEY =False
  2. DOWNLOAD_DELAY =1
  3. DEFAULT_REQUEST_HEADERS = {
  4. ‘Accept’:‘text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8’,
  5. ‘Accept-Language’:‘en’,
  6. ‘User-Agent’:‘Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36’
  7. }
  8. ITEM_PIPELINES = {
  9. ‘TXmovies.pipelines.TxmoviesPipeline’:300,
  10. }

3.確認要提取的資料,item項

item定義你要提取的內容(定義資料結構),比如我提取的內容為電影名和電影描述,我就建立兩個變數。Field方法實際上的做法是建立一個字典,給字典新增一個建,暫時不賦值,等待提取資料後再賦值。下面item的結構可以表示為:{‘name’:”,’descripition’:”}。

  1. # -*- coding: utf-8 -*-
  2. # Define here the models for your scraped items
  3. #
  4. # See documentation in:
  5. # https://docs.scrapy.org/en/latest/topics/items.html
  6. importscrapy
  7. classTxmoviesItem(scrapy.Item):
  8. # define the fields for your item here like:
  9. # name = scrapy.Field()
  10. name = scrapy.Field()
  11. description = scrapy.Field()

4.寫爬蟲程式

我們要寫的部分是parse方法裡的內容,重點在於如何寫xpath,關於xpath我不多講,有興趣可以看看我另一篇文章,XPATH教程

引入剛剛寫好的item,剛剛說了item裡面建立的變數就是字典的鍵值,可以直接進行賦值。賦值後交給管道處理。

簡單講一下這一段程式碼的思路,首先騰訊視訊的url為https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset=0&pagesize=30

我們注意到offset這一項,第一頁的offset為0,第二頁為30,依次推列。在程式中這一項用於控制抓取第一頁,但是也要給一個範圍,不可能無限大,否則會報錯,可以去看看騰訊一共有多少頁視訊,也可以寫一個異常捕獲機制,捕捉到請求出錯則退出。我這裡僅僅是示範,所以只給了120,也就是4頁。

yield

程式裡一共有兩個yield,我比較喜歡叫它中斷,當然中斷只在CPU中發生,它的作用是移交控制權,在本程式中,我們對item封裝資料後,就呼叫yield把控制權給管道,管道拿到處理後return返回,又回到該程式。這是對第一個yield的解釋。

第二個yield稍微複雜點,這條程式裡利用了一個回撥機制,即callback,回撥的物件是parse,也就是當前方法,通過不斷的回撥,程式將陷入迴圈,如果不給程式加條件,就會陷入死迴圈,如本程式我把if去掉,那就是死迴圈了。

yield scrapy.Request(url=url,callback=self.parse)

xpath

還有一個要注意的是如何提取xpathl裡的資料,我們的寫法有四種,第一種寫法拿到selector選擇器,也就是原資料,裡面有一些我們用不到的東西。第二個extract(),將選擇器序列號為字串。第三個和第四個一樣,拿到字串裡的第一個資料,也就是我們要的資料。

items[‘name’]=i.xpath(‘./a/@title’)[0]

items[‘name’]=i.xpath(‘./a/@title’).extract()

items[‘name’]=i.xpath(‘./a/@title’).extract_first()

items[‘name’]=i.xpath(‘./a/@title’).get()

  1. # -*- coding: utf-8 -*-
  2. importscrapy
  3. from..itemsimportTxmoviesItem
  4. classTxmsSpider(scrapy.Spider):
  5. name =‘txms’
  6. allowed_domains = [‘v.qq.com’]
  7. start_urls = [‘https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset=0&pagesize=30’]
  8. offset=0
  9. defparse(self, response):
  10. items=TxmoviesItem()
  11. lists=response.xpath(‘//div[@class=”list_item”]’)
  12. foriinlists:
  13. items[‘name’]=i.xpath(‘./a/@title’).get()
  14. items[‘description’]=i.xpath(‘./div/div/@title’).get()
  15. yielditems
  16. ifself.offset <120:
  17. self.offset +=30
  18. url =‘https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset={}&pagesize=30’.format(
  19. str(self.offset))
  20. yieldscrapy.Request(url=url,callback=self.parse)

5.交給管道輸出

管道可以處理提取的資料,如存資料庫。我們這裡僅輸出。

  1. # -*- coding: utf-8 -*-
  2. # Define your item pipelines here
  3. #
  4. # Don’t forget to add your pipeline to the ITEM_PIPELINES setting
  5. # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
  6. classTxmoviesPipeline(object):
  7. defprocess_item(self, item, spider):
  8. print(item)
  9. returnitem

6.run,執行專案

  1. fromscrapyimportcmdline
  2. cmdline.execute(‘scrapy crawl txms’.split())

7.測試結果

白色的管道輸出的結果,紅色的除錯資訊

8.流程梳理

新建專案-》進入專案-》新建爬蟲檔案-》明確抓取的內容,寫item-》寫爬蟲程式,爬取資料-》交給管道處理資料-》調整全域性配置setting-》執行爬蟲程式,可以通過終端或者在程式裡寫一個run程式

9.提速:多執行緒爬取

如果你實現了上面的實驗,不難發現其爬取速度是非常慢,根本的原因就是因為它是順序執行的,你可以從結果中看出,總是前面一頁的內容被輸出,再輸出後面的內容。不適合處理資料量較大的情況,一個好的方式是採用多執行緒的方法,這裡的多執行緒是基於方法的多執行緒,並不是通過建立Thread物件來實現,是在一個方法中,一次性把請求交給排程器。

我們通過重寫start_requests方法來實現我們的想法(這個方法的原始碼在__init__.py下面,有興趣可以看一下)

  1. # -*- coding: utf-8 -*-
  2. importscrapy
  3. from..itemsimportTxmoviesItem
  4. classTxmsSpider(scrapy.Spider):
  5. name =‘txms’
  6. allowed_domains = [‘v.qq.com’]
  7. url=‘https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset={}&pagesize=30’
  8. offset=0
  9. defstart_requests(self):
  10. foriinrange(0,121,30):
  11. url=self.url.format(i)
  12. yieldscrapy.Request(
  13. url=url,
  14. callback=self.parse
  15. )
  16. defparse(self, response):
  17. items=TxmoviesItem()
  18. lists=response.xpath(‘//div[@class=”list_item”]’)
  19. foriinlists:
  20. items[‘name’]=i.xpath(‘./a/@title’).get()
  21. items[‘description’]=i.xpath(‘./div/div/@title’).get()
  22. yielditems