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(或pip3  install 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裏面寫了兩個管道,一個爬取網頁的管道,一個存數據庫的管道,我調整了他們的優先級,如果有爬蟲數據,優先執行存庫操作。

ITEM_PIPELINES = {
    'scrapyP1.pipelines.BaiduPipeline': 300,
    'scrapyP1.pipelines.BaiduMysqlPipeline': 200,
}

 

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

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

import scrapy


class BaiduSpider(scrapy.Spider):
    name = 'baidu'
    allowed_domains = ['www.baidu.com']
    start_urls = ['http://www.baidu.com/']

    def parse(self, response):
        tile=response.xpath('//html/head/title/text()')
        print(tile)

 

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

 

使用終端運行太麻煩了,而且不能提取數據,我們一個寫一個run文件作爲程序的入口,splite是必須寫的,目的是把字符串轉爲列表形式,第一個參數是scrapy,第二個crawl,第三個baidu

from scrapy import cmdline

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'

示例:

from scrapy import cmdline

cmdline.execute('scrapy crawl baidu -o baidu.csv'.split())

 

七、一個完整的案例

這個項目我們的主題是爬騰訊視頻的電影信息,包括電影名和描述

 

1.創建項目

打開一個終端輸入(建議放到合適的路徑下,默認是C盤)

scrapy startproject TXmovies

cd TXmovies

scrapy genspider txms v.qq.com

2.修改setting

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

ROBOTSTXT_OBEY = False

DOWNLOAD_DELAY = 1

DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
  'User-Agent':'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36'
}

ITEM_PIPELINES = {
   'TXmovies.pipelines.TxmoviesPipeline': 300,
}

 

3.確認要提取的數據,item項

item定義你要提取的內容(定義數據結構),比如我提取的內容爲電影名和電影描述,我就創建兩個變量。Field方法實際上的做法是創建一個字典,給字典添加一個建,暫時不賦值,等待提取數據後再賦值。下面item的結構可以表示爲:{'name':'','descripition':''}。

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

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

import scrapy


class TxmoviesItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    name = scrapy.Field()
    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()

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

class TxmsSpider(scrapy.Spider):
    name = 'txms'
    allowed_domains = ['v.qq.com']
    start_urls = ['https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset=0&pagesize=30']
    offset=0

    def parse(self, response):
        items=TxmoviesItem()
        lists=response.xpath('//div[@class="list_item"]')
        for i in lists:
            items['name']=i.xpath('./a/@title').get()
            items['description']=i.xpath('./div/div/@title').get()

            yield items

        if self.offset < 120:
            self.offset += 30
            url = 'https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset={}&pagesize=30'.format(
                str(self.offset))

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

5.交給管道輸出

管道可以處理提取的數據,如存數據庫。我們這裏僅輸出。

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

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


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

6.run,執行項目

from scrapy import cmdline

cmdline.execute('scrapy crawl txms'.split())

7.測試結果

白色的管道輸出的結果,紅色的調試信息

 

8.流程梳理

新建項目-》進入項目-》新建爬蟲文件-》明確抓取的內容,寫item-》寫爬蟲程序,爬取數據-》交給管道處理數據-》調整全局配置setting-》執行爬蟲程序,可以通過終端或者在程序裏寫一個run程序

 

9.提速:多線程爬取

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

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

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

class TxmsSpider(scrapy.Spider):
    name = 'txms'
    allowed_domains = ['v.qq.com']
    url='https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset={}&pagesize=30'
    offset=0

    def start_requests(self):
        for i in range(0,121,30):
            url=self.url.format(i)
            yield scrapy.Request(
                url=url,
                callback=self.parse
            )

    def parse(self, response):
        items=TxmoviesItem()
        lists=response.xpath('//div[@class="list_item"]')
        for i in lists:
            items['name']=i.xpath('./a/@title').get()
            items['description']=i.xpath('./div/div/@title').get()

            yield items

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章