Python——Scrapy初學


Scrapy是一個為了爬取網站數據,提取結構性數據而編寫的應用框架。可以應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。Scrapy最初是為了頁面抓取(更確切來說, 網絡抓取)所設計的,也可以應用在獲取API所返回的數據(例如Amazon Associates Web Services)或者通用的網絡爬蟲。

1 安裝

簡要說明下Scrapy的安裝:

下載網址:http://www.lfd.uci.edu/~gohlke/pythonlibs/

下載后綴名為whl的scrapy文件,在cmd中進入Scripts所在的位置,輸入pip install scrapy文件名.whl(可參考《Python初學基礎》中的7.1 模塊安裝),注意scrapy依賴twiste,同樣使用whl格式的包進行安裝。安裝完這兩個模塊后我在進行爬蟲操作的時候提示沒有win32api,該文件為exe,下載地址為https://sourceforge.net/projects/pywin32/files/pywin32/Build%20220/。

在安裝好模塊后要注意環境變量的配置,以我自己的安裝目錄為例,應當將D:\Program Files (x86)\Python\Scripts以及D:\Program Files (x86)\Python\Lib\site-packages加入環境變量中,否則模塊只能在安裝目錄下運行,在別的目錄下運行時會提示不是內部或者外部命令。在cmd下輸入scrapy查看是否安裝成功。

上述簡單介紹了scrapy的安裝,在安裝的過程中不要着急,如果安裝出錯,要注意查看錯誤信息,根據這些信息一個一個去解決。

2 Scrapy架構及組件介紹

使用Scrapy抓取一個網站一共需要四個步驟:

1. 創建一個Scrapy項目;

2. 定義Item容器;

3. 編寫爬蟲;

4. 存儲內容

學習怎么使用Scrapy之前,我們需要先來了解一下Scrapy的架構以及組件之間的交互。下圖展現的是Scrapy的架構,包括組件及在系統中發生的數據流(圖中綠色箭頭)。

下面對每個組件都做了簡單介紹:

Scrapy Engine

Scrapy引擎是爬蟲工作的核心,負責控制數據流在系統中所有組件中流動,並在相應動作發生時觸發事件。

調度器(Scheduler)

調度器從引擎接受request並將他們入隊,以便之后引擎請求他們時提供給引擎。

下載器(Downloader)

下載器負責獲取頁面數據並提供給引擎,而后提供給spider。

Spiders

Spider是Scrapy用戶編寫用於分析由下載器返回的response,並提取出item和額外跟進的URL的類。 Item Pipeline Item Pipeline負責處理被spider提取出來的item。典型的處理有清理、驗證及持久化(例如存取到數據庫中)。

接下來是兩個中間件,它們用於提供一個簡便的機制,通過插入自定義代碼來擴展Scrapy的功能。

下載器中間件(Downloader middlewares)

下載器中間件是在引擎及下載器之間的特定鈎子(specific hook),處理Downloader傳遞給引擎的response。

Spider中間件(Spider middlewares)

Spider中間件是在引擎及Spider之間的特定鈎子(specific hook),處理spider的輸入(就是接收來自下載器的response)和輸出(就是發送items給item pipeline以及發送requests給調度器)。

3 Scrapy爬蟲框架入門實例

例程參考《scrapy爬蟲框架入門實例》,該例子是抓取慕課網(http://blog.csdn.net/zjiang1994/article/details/52779537)。慕課網的頁面結構已經變了,所以說該案例實際上已經不能達到抓取目的。但是關於scrapy爬蟲框架整體的使用方式和流程目前還是正確的,可以進行參考。根據慕課網現有的頁面結構做了一些改動可以成功實現。

要抓取的內容是全部的課程名稱,課程圖片,課程人數,課程簡介,課程URL:

右鍵審查元素查看

#如果response是網頁資源的話,下面的代碼可以幫助我們獲得div
divs = response.xpath('//div[@class="course-card-container"]')

所以如果div已經獲得的話通過如下獲得信息(詳解介紹見下文):

#獲取每個div中的課程路徑
item['url'] = 'http://www.imooc.com' + box.xpath('.//@href').extract()[0]
#獲取div中的課程標題 item['title'] = box.xpath('.//h3[@class="course-card-name"]/text()').extract()[0].strip() #獲取div中的標題圖片地址 item['image_url'] = 'http:' + box.xpath('.//@src').extract()[0] #獲取div中的學生人數 item['student'] = box.xpath('.//span/text()').extract()[1].strip() #獲取div中的課程簡介 item['introduction'] = box.xpath('.//p/text()').extract()[0].strip()

工作流程

Scrapy框架抓取的基本流程是這樣:
這里寫圖片描述

當然了,還有一些中間件等等,這里是入門例子,所以不涉及。

1)創建一個Scrapy項目

在開始爬取之前,您必須創建一個新的Scrapy項目。

進入您打算存儲代碼的目錄中,運行下列命令: scrapy startproject tutorial

該命令將會創建包含下列內容的tutorial目錄:

tutorial/ scrapy.cfg tutorial/
        __init__.py items.py pipelines.py settings.py spiders/
            __init__.py ... 

這些文件構成Scrapy爬蟲框架,它們分別的作用是:

scrapy.cfg – 項目的配置文件

tutorial/ – 該項目的python模塊,之后您將在此加入代碼

tutorial/items.py – 項目中的item文件

tutorial/pipelines.py – 項目中的pipelines文件

tutorial/settings.py – 項目的設置文件

tutorial/spiders/ – 放置spider代碼的目錄

2)定義Item容器

Item是保存爬取到的數據的容器,其使用方法和python字典類似, 並且提供了額外保護機制來避免拼寫錯誤導致的未定義字段錯誤。

首先根據需要獲取到的數據對item進行建模。比如我們需要從慕課網中獲取課程名稱,課程圖片,課程人數,課程簡介,課程URL。對此,我們需要在item中定義相應的字段。

我們在工程目錄下可以看到一個items文件,我們可以更改這個文件或者創建一個新的文件來定義我們的item。將item.py中的內容修改如下:

#引入文件
import scrapy class CourseItem(scrapy.Item): #課程標題
    title = scrapy.Field() #課程url
    url = scrapy.Field() #課程標題圖片
    image_url = scrapy.Field() #課程描述
    introduction = scrapy.Field() #學習人數
    student = scrapy.Field() image_path = scrapy.Field()

根據如上的代碼,我們創建了一個名為item的容器,用來保存、抓取的信息, title->課程標題, url->課程url, image_url->課程標題圖片, introduction->課程描述, student->學習人數。在創建完item文件后我們可以通過類似於詞典(dictionary-like)的API以及用於聲明可用字段的簡單語法。常用方法如下:

#定義一個item
course = CourseItem() #賦值
course['title'] = "語文"
#取值
course['title'] course.get('title') #獲取全部鍵
course.keys() #獲取全部值
course.items()

3) 創建一個爬蟲

我們要編寫爬蟲,首先是創建一個Spider我們在tutorial/spiders/目錄下創建一個文件MySpider.py

文件包含一個MySpider類,它必須繼承scrapy.Spider類。

同時它必須定義一下三個屬性:

-name: 用於區別Spider。 該名字必須是唯一的,您不可以為不同的Spider設定相同的名字。
-start_urls: 包含了Spider在啟動時進行爬取的url列表。 因此,第一個被獲取到的頁面將是其中之一。 后續的URL則從初始的URL獲取到的數據中提取。
-parse() 是spider的一個方法。 被調用時,每個初始URL完成下載后生成的 Response 對象將會作為唯一的參數傳遞給該函數。 該方法負責解析返回的數據(response data),提取數據(生成item)以及生成需要進一步處理的URL的 Request 對象。

創建完成后MySpider.py的代碼如下

#引入文件
import scrapy class MySpider(scrapy.Spider): #用於區別Spider
    name = "MySpider"
    #允許訪問的域
    allowed_domains = [] #爬取的地址
    start_urls = [] #爬取方法
    def parse(self, response): pass

 

為了簡單清晰,我們先抓取一個頁面中的信息。

首先我們編寫爬取代碼。我們在上文說過,爬取的部分在MySpider類的parse()方法中進行。 parse()方法負責處理response並返回處理的數據以及(/或)跟進的URL。該方法及其他的Request回調函數必須返回一個包含 Request 及(或) Item 的可迭代的對象。

在網頁中提取我們所需要的數據,之前所學習的是根據正則表達式來獲取,在Scrapy中是使用一種基於Xpath和CSS的表達式機制:Scrapy Selectors。

Selector是一個選擇器,它有四個基本的方法:

xpath() – 傳入xpath表達式,返回該表達式所對應的所有節點的selector list列表 。

css() – 傳入CSS表達式,返回該表達式所對應的所有節點的selector list列表。

extract() – 序列化該節點為unicode字符串並返回list。

re() – 根據傳入的正則表達式對數據進行提取,返回unicode字符串list列表。

在Shell中嘗試Selector選擇器

為了介紹Selector的使用方法,接下來我們將要使用內置的Scrapy shell。

你需要先進入項目的根目錄,執行下列命令來啟動Scrapy shell:

scrapy shell “http://www.imooc.com/course/list”

shell的輸出類似:

在Shell載入后,你將獲得response回應,存儲在本地變量response中。

所以如果你輸入response.body,你將會看到response的body部分,也就是抓取到的頁面內容,或者輸入response.headers 來查看它的 header部分。現在就像是一大堆沙子握在手里,里面有我們想要的金子,所以下一步我們就要用篩子把沙子去掉,淘出金子。selector選擇器就是這樣一個篩子,正如我們剛才講到的,你可以使用response.selector.xpath()、response.selector.css()、response.selector.extract()和response.selector.re()這四個基本方法。

使用XPath

什么是XPath?XPath是一門在網頁中查找特定信息的語言。所以用XPath來篩選數據,要比使用正則表達式容易些。

這里給出XPath表達式的例子及對應的含義:

/html/head/title – 選擇HTML文檔中<head>標簽內的<title>元素

/html/head/title/text() – 選擇上面提到的<title>元素的文字

//td – 選擇所有的<td>元素

//div[@class=”mine”] – 選擇所有具有class=”mine”屬性的div元素

上邊僅僅是幾個簡單的XPath例子,XPath實際上要比這遠遠強大的多。如果你想了解更多關於XPath的內容,推薦學習這篇文章http://www.w3school.com.cn/xpath/

值得一提的是,response.xpath()、response.css()已經被映射到response.selector.xpath()、response.selector.css(),所以直接使用response.xpath()即可。

在Python編寫時,由於沒有學習過Xpath,所以我先在cmd中編寫試驗得到正確的返回結果后再寫入代碼中,注意shell根據response的類型自動為我們初始化了變量sel,我們可以直接使用。

例如獲取每個div中的課程路徑:

此外,我們希望Spiders將爬取並篩選后的數據存放到item容器中,所以我們MySpider.py的代碼應該是這樣的:

import scrapy #引入容器
from tutorial.items import CourseItem class MySpider(scrapy.Spider): #設置name
    name = "MySpider"
    #設定域名
    allowed_domains = ["imooc.com"] #填寫爬取地址
    start_urls = ["http://www.imooc.com/course/list"] #編寫爬取方法
    def parse(self, response): #實例一個容器保存爬取的信息
        item = CourseItem() #這部分是爬取部分,使用xpath的方式選擇信息,具體方法根據網頁結構而定
        #先獲取每個課程的div
        for box in response.xpath('//div[@class="course-card-container"]'): #獲取每個div中的課程路徑
            item['url'] = 'http://www.imooc.com' + box.xpath('.//@href').extract()[0] #獲取div中的課程標題
            item['title'] = box.xpath('.//h3[@class="course-card-name"]/text()').extract()[0].strip() #獲取div中的標題圖片地址
            item['image_url'] =  'http:' + box.xpath('.//@src').extract()[0] #獲取div中的學生人數
            item['student'] = box.xpath('.//span/text()').extract()[1].strip() #獲取div中的課程簡介
            item['introduction'] = box.xpath('.//p/text()').extract()[0].strip() #返回信息
            yield item

 

在parse()方法中response參數返回一個下載好的網頁信息,我們然后通過xpath來尋找我們需要的信息。
在scrapy框架中,可以使用多種選擇器來尋找信息,這里使用的是xpath,同時我們也可以使用BeautifulSoup,lxml等擴展來選擇,而且框架本身還提供了一套自己的機制來幫助用戶獲取信息,就是Selectors。 

在執行完以上步驟之后,我們可以運行一下爬蟲,看看是否出錯。

在命令行下進入工程文件夾,然后運行:

scrapy crawl MySpider

如果操作正確會顯示如下信息:

上面信息表示,我們已經獲取了信息,接下來我們開始進行信息的儲存。

最簡單存儲爬取的數據的方式是使用Feed exports,主要可以導出四種格式:JSON,JSON lines,CSV和XML。

我們這里將結果導出為最常用的JSON格式:

scrapy crawl dmoz -o items.json -t json

-o 后邊是導出的文件名,-t 指定導出類型 成功執行命令后,根目錄出現了一個叫 items.json 的文件,內容如下:

或者使用Pipeline處理數據:

當我們成功獲取信息后,要進行信息的驗證、儲存等工作,這里以儲存為例。
當Item在Spider中被收集之后,它將會被傳遞到Pipeline,一些組件會按照一定的順序執行對Item的處理。
Pipeline經常進行以下一些操作:
清理HTML數據
驗證爬取的數據(檢查item包含某些字段)
查重(並丟棄)
將爬取結果保存到數據庫中

這里只進行簡單的將數據儲存在json文件的操作。

改寫在tutorial/目錄下文件pipelines.py的代碼如下:

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

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

#引入文件
from scrapy.exceptions import DropItem import json class MyPipeline(object): def __init__(self): #打開文件
        self.file = open('data.json', 'w', encoding='utf-8') #該方法用於處理數據
    def process_item(self, item, spider): #讀取item中的數據
        line = json.dumps(dict(item), ensure_ascii=False) + "\n"
        #寫入文件
 self.file.write(line) #返回item
        return item #該方法在spider被開啟時被調用。
    def open_spider(self, spider): pass
    #該方法在spider被關閉時被調用。
    def close_spider(self, spider): pass

要使用Pipeline,首先要注冊Pipeline

找到settings.py文件,這個文件時爬蟲的配置文件

在其中添加:

ITEM_PIPELINES = { 'tutorial.pipelines.MyPipeline': 1, }

上面的代碼用於注冊Pipeline,其中'tutorial.pipelines.MyPipeline為你要注冊的類,右側的’1’為該Pipeline的優先級,范圍1~1000,越小越先執行。

進行完以上操作,我們的一個最基本的爬取操作就完成了

這時我們再運行:

scrapy crawl MySpider

就可以在項目根目錄下發現data.json文件,里面存儲着爬取的課程信息。

上面的代碼只進行了比較簡單的爬取,並沒有完成爬取慕課網全部課程的目標。
下面進行一些簡單的擴展完成我們的目標。

url跟進

在上面我們介紹了如何進行簡單的單頁面爬取,但是我們可以發現慕課網的課程是分布在去多個頁面的,所以為了完整的爬取信息課程信息,我們需要進行url跟進。

為了完成這個目標需要對MySpider.py文件進行如下更改

import scrapy #引入容器
from tutorial.items import CourseItem class MySpider(scrapy.Spider): #設置name
    name = "MySpider"
    #設定域名
    allowed_domains = ["imooc.com"] #填寫爬取地址
    start_urls = ["http://www.imooc.com/course/list"] #編寫爬取方法
    def parse(self, response): #實例一個容器保存爬取的信息
        item = CourseItem() #這部分是爬取部分,使用xpath的方式選擇信息,具體方法根據網頁結構而定
        #先獲取每個課程的div
        for box in response.xpath('//div[@class="course-card-container"]'): #獲取每個div中的課程路徑
            item['url'] = 'http://www.imooc.com' + box.xpath('.//@href').extract()[0] #獲取div中的課程標題
            item['title'] = box.xpath('.//h3[@class="course-card-name"]/text()').extract()[0].strip() #獲取div中的標題圖片地址
            item['image_url'] =  'http:' + box.xpath('.//@src').extract()[0] #獲取div中的學生人數
            item['student'] = box.xpath('.//span/text()').extract()[1].strip() #獲取div中的課程簡介
            item['introduction'] = box.xpath('.//p/text()').extract()[0].strip() #返回信息
            yield item #url跟進開始
        #獲取下一頁的url信息
        url = response.xpath("//a[contains(text(),'下一頁')]/@href").extract() if url : #將信息組合成下一頁的url
            page = 'http://www.imooc.com' + url[0] #返回url
            yield scrapy.Request(page, callback=self.parse) #url跟進結束

修改成功后就可以自動進行url跟進了。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM