Scrapy是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。能夠應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。Scrapy最初是爲了頁面抓取(更確切來講, 網絡抓取)所設計的,也能夠應用在獲取API所返回的數據(例如Amazon Associates Web Services)或者通用的網絡爬蟲。css
簡要說明下Scrapy的安裝:html
下載網址:http://www.lfd.uci.edu/~gohlke/pythonlibs/python
下載後綴名爲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查看是否安裝成功。shell
上述簡單介紹了scrapy的安裝,在安裝的過程當中不要着急,若是安裝出錯,要注意查看錯誤信息,根據這些信息一個一個去解決。數據庫
使用Scrapy抓取一個網站一共須要四個步驟:json
1. 建立一個Scrapy項目;api
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給調度器)。
例程參考《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框架抓取的基本流程是這樣:
固然了,還有一些中間件等等,這裏是入門例子,因此不涉及。
在開始爬取以前,您必須建立一個新的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代碼的目錄
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()
咱們要編寫爬蟲,首先是建立一個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跟進了。