Scrapy框架學習

本文但願達到如下目標:css

  1. 簡要介紹Scarpy
  2. 閱讀官網入門文檔並實現文檔中的範例
  3. 使用Scarpy優豆瓣爬蟲的抓取
  4. 制定下一步學習目標

初學Scrapy, 若有翻譯不當, 或者代碼錯誤, 請指出, 很是感謝html

1. Scrapy簡介


Scrapy是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。 能夠應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。
其最初是爲了頁面抓取 (更確切來講, 網絡抓取 )所設計的, 也能夠應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。Scrapy用途普遍,能夠用於數據挖掘、監測和自動化測試python

Scrapy 使用了 Twisted異步網絡庫來處理網絡通信。總體架構大體以下正則表達式

Scrapy

Scrapy數據庫

Scrapy主要包括瞭如下組件:json

  • 引擎(Scrapy): 用來處理整個系統的數據流處理, 觸發事務(框架核心)
  • 調度器(Scheduler): 用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 能夠想像成一個URL(抓取網頁的網址或者說是連接)的優先隊列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址
  • 下載器(Downloader): 用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是創建在twisted這個高效的異步模型上的)
  • 爬蟲(Spiders): 爬蟲是主要幹活的, 用於從特定的網頁中提取本身須要的信息, 即所謂的實體(Item)。用戶也能夠從中提取出連接,讓Scrapy繼續抓取下一個頁面
  • 項目管道(Pipeline): 負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證明體的有效性、清除不須要的信息。當頁面被爬蟲解析後,將被髮送到項目管道,並通過幾個特定的次序處理數據。
  • 下載器中間件(Downloader Middlewares): 位於Scrapy引擎和下載器之間的框架,主要是處理Scrapy引擎與下載器之間的請求及響應。
  • 爬蟲中間件(Spider Middlewares): 介於Scrapy引擎和爬蟲之間的框架,主要工做是處理蜘蛛的響應輸入和請求輸出。
  • 調度中間件(Scheduler Middewares): 介於Scrapy引擎和調度之間的中間件,從Scrapy引擎發送到調度的請求和響應。

Scrapy運行流程大概以下:瀏覽器

  1. 首先,引擎從調度器中取出一個連接(URL)用於接下來的抓取
  2. 引擎把URL封裝成一個請求(Request)傳給下載器,下載器把資源下載下來,並封裝成應答包(Response)
  3. 而後,爬蟲解析Response
  4. 如果解析出實體(Item),則交給實體管道進行進一步的處理。
  5. 如果解析出的是連接(URL),則把URL交給Scheduler等待抓取

2. 安裝Scrapy


使用如下命令:網絡

sudo pip install virtualenv  #安裝虛擬環境工具
virtualenv ENV  #建立一個虛擬環境目錄
source ./ENV/bin/active  #激活虛擬環境
pip install Scrapy
#驗證是否安裝成功
pip list
#輸出以下
cffi (0.8.6)
cryptography (0.6.1)
cssselect (0.9.1)
lxml (3.4.1)
pip (1.5.6)
pycparser (2.10)
pyOpenSSL (0.14)
queuelib (1.2.2)
Scrapy (0.24.4)
setuptools (3.6)
six (1.8.0)
Twisted (14.0.2)
w3lib (1.10.0)
wsgiref (0.1.2)
zope.interface (4.1.1)

更多虛擬環境的操做能夠查看個人博文架構

3. Scrapy Tutorial


在抓取以前, 你須要新建一個Scrapy工程. 進入一個你想用來保存代碼的目錄,而後執行:app

$ scrapy startproject tutorial

這個命令會在當前目錄下建立一個新目錄 tutorial, 它的結構以下:

.
├── scrapy.cfg
└── tutorial
    ├── __init__.py
    ├── items.py
    ├── pipelines.py
    ├── settings.py
    └── spiders
        └── __init__.py

這些文件主要是:

  • scrapy.cfg: 項目配置文件
  • tutorial/: 項目python模塊, 以後您將在此加入代碼
  • tutorial/items.py: 項目items文件
  • tutorial/pipelines.py: 項目管道文件
  • tutorial/settings.py: 項目配置文件
  • tutorial/spiders: 放置spider的目錄

3.1. 定義Item

Items是將要裝載抓取的數據的容器,它工做方式像 python 裏面的字典,但它提供更多的保護,好比對未定義的字段填充以防止拼寫錯誤

經過建立scrapy.Item類, 而且定義類型爲 scrapy.Field 的類屬性來聲明一個Item.
咱們經過將須要的item模型化,來控制從 dmoz.org 得到的站點數據,好比咱們要得到站點的名字,url 和網站描述,咱們定義這三種屬性的域。在 tutorial 目錄下的 items.py 文件編輯

from scrapy.item import Item, Field


class DmozItem(Item):
    # define the fields for your item here like:
    name = Field()
    description = Field()
    url = Field()

3.2. 編寫Spider

Spider 是用戶編寫的類, 用於從一個域(或域組)中抓取信息, 定義了用於下載的URL的初步列表, 如何跟蹤連接,以及如何來解析這些網頁的內容用於提取items。

要創建一個 Spider,繼承 scrapy.Spider 基類,並肯定三個主要的、強制的屬性:

  • name:爬蟲的識別名,它必須是惟一的,在不一樣的爬蟲中你必須定義不一樣的名字.
  • start_urls:包含了Spider在啓動時進行爬取的url列表。所以,第一個被獲取到的頁面將是其中之一。後續的URL則從初始的URL獲取到的數據中提取。咱們能夠利用正則表達式定義和過濾須要進行跟進的連接。
  • parse():是spider的一個方法。被調用時,每一個初始URL完成下載後生成的 Response 對象將會做爲惟一的參數傳遞給該函數。該方法負責解析返回的數據(response data),提取數據(生成item)以及生成須要進一步處理的URL的 Request 對象。
    這個方法負責解析返回的數據、匹配抓取的數據(解析爲 item )並跟蹤更多的 URL。

在 /tutorial/tutorial/spiders 目錄下建立 dmoz_spider.py

import scrapy

class DmozSpider(scrapy.Spider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
    ]

    def parse(self, response):
        filename = response.url.split("/")[-2]
        with open(filename, 'wb') as f:
            f.write(response.body)

3.3. 爬取

當前項目結構

├── scrapy.cfg
└── tutorial
    ├── __init__.py
    ├── items.py
    ├── pipelines.py
    ├── settings.py
    └── spiders
        ├── __init__.py
        └── dmoz_spider.py

到項目根目錄, 而後運行命令:

$ scrapy crawl dmoz

運行結果:

2014-12-15 09:30:59+0800 [scrapy] INFO: Scrapy 0.24.4 started (bot: tutorial)
2014-12-15 09:30:59+0800 [scrapy] INFO: Optional features available: ssl, http11
2014-12-15 09:30:59+0800 [scrapy] INFO: Overridden settings: {'NEWSPIDER_MODULE': 'tutorial.spiders', 'SPIDER_MODULES': ['tutorial.spiders'], 'BOT_NAME': 'tutorial'}
2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled extensions: LogStats, TelnetConsole, CloseSpider, WebService, CoreStats, SpiderState
2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, RedirectMiddleware, CookiesMiddleware, ChunkedTransferMiddleware, DownloaderStats
2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware
2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled item pipelines:
2014-12-15 09:30:59+0800 [dmoz] INFO: Spider opened
2014-12-15 09:30:59+0800 [dmoz] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2014-12-15 09:30:59+0800 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2014-12-15 09:30:59+0800 [scrapy] DEBUG: Web service listening on 127.0.0.1:6080
2014-12-15 09:31:00+0800 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None)
2014-12-15 09:31:00+0800 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None)
2014-12-15 09:31:00+0800 [dmoz] INFO: Closing spider (finished)
2014-12-15 09:31:00+0800 [dmoz] INFO: Dumping Scrapy stats:
    {'downloader/request_bytes': 516,
     'downloader/request_count': 2,
     'downloader/request_method_count/GET': 2,
     'downloader/response_bytes': 16338,
     'downloader/response_count': 2,
     'downloader/response_status_count/200': 2,
     'finish_reason': 'finished',
     'finish_time': datetime.datetime(2014, 12, 15, 1, 31, 0, 666214),
     'log_count/DEBUG': 4,
     'log_count/INFO': 7,
     'response_received_count': 2,
     'scheduler/dequeued': 2,
     'scheduler/dequeued/memory': 2,
     'scheduler/enqueued': 2,
     'scheduler/enqueued/memory': 2,
     'start_time': datetime.datetime(2014, 12, 15, 1, 30, 59, 533207)}
2014-12-15 09:31:00+0800 [dmoz] INFO: Spider closed (finished)

3.4. 提取Items

3.4.1. 介紹Selector

從網頁中提取數據有不少方法。Scrapy使用了一種基於 XPath 或者 CSS 表達式機制: Scrapy Selectors

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

  • /html/head/title: 選擇HTML文檔中 <head> 標籤內的 <title> 元素
  • /html/head/title/text(): 選擇 <title> 元素內的文本
  • //td: 選擇全部的 <td> 元素
  • //div[@class="mine"]: 選擇全部具備class="mine" 屬性的 div 元素

等多強大的功能使用能夠查看XPath tutorial

爲了方便使用 XPaths,Scrapy 提供 Selector 類, 有四種方法 :

  • xpath():返回selectors列表, 每個selector表示一個xpath參數表達式選擇的節點.
  • css() : 返回selectors列表, 每個selector表示CSS參數表達式選擇的節點
  • extract():返回一個unicode字符串,該字符串爲XPath選擇器返回的數據
  • re(): 返回unicode字符串列表,字符串做爲參數由正則表達式提取出來

3.4.2. 取出數據

首先使用谷歌瀏覽器開發者工具, 查看網站源碼, 來看本身須要取出的數據形式(這種方法比較麻煩), 更簡單的方法是直接對感興趣的東西右鍵審查元素, 能夠直接查看網站源碼

在查看網站源碼後, 網站信息在第二個<ul>

<ul class="directory-url" style="margin-left:0;">

 <li><a href="http://www.pearsonhighered.com/educator/academic/product/0,,0130260363,00%2Ben-USS_01DBC.html" class="listinglink">Core Python Programming</a> 
- By Wesley J. Chun; Prentice Hall PTR, 2001, ISBN 0130260363. For experienced developers to improve extant skills; professional level examples. Starts by introducing syntax, objects, error handling, functions, classes, built-ins. [Prentice Hall]
<div class="flag"><a href="/public/flag?cat=Computers%2FProgramming%2FLanguages%2FPython%2FBooks&url=http%3A%2F%2Fwww.pearsonhighered.com%2Feducator%2Facademic%2Fproduct%2F0%2C%2C0130260363%2C00%252Ben-USS_01DBC.html"><img src="/img/flag.png" alt="[!]" title="report an issue with this listing"></a></div>
</li>
...省略部分...
</ul>

那麼就能夠經過一下方式進行提取數據

#經過以下命令選擇每一個在網站中的 <li> 元素:
sel.xpath('//ul/li') 

#網站描述:
sel.xpath('//ul/li/text()').extract()

#網站標題:
sel.xpath('//ul/li/a/text()').extract()

#網站連接:
sel.xpath('//ul/li/a/@href').extract()

#如前所述,每一個 xpath() 調用返回一個 selectors 列表,因此咱們能夠結合 xpath() 去挖掘更深的節點。咱們將會用到這些特性,因此:

for sel in response.xpath('//ul/li')
    title = sel.xpath('a/text()').extract()
    link = sel.xpath('a/@href').extract()
    desc = sel.xpath('text()').extract()
    print title, link, desc

在已有的爬蟲文件中修改代碼

import scrapy

class DmozSpider(scrapy.Spider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
    ]

    def parse(self, response):
        for sel in response.xpath('//ul/li'):
            title = sel.xpath('a/text()').extract()
            link = sel.xpath('a/@href').extract()
            desc = sel.xpath('text()').extract()
            print title, link, desc

3.4.3. 使用item

Item對象是自定義的python字典,可使用標準的字典語法來獲取到其每一個字段的值(字段便是咱們以前用Field賦值的屬性)

>>> item = DmozItem()
>>> item['title'] = 'Example title'
>>> item['title']
'Example title'

通常來講,Spider將會將爬取到的數據以 Item 對象返回, 最後修改爬蟲類,使用 Item 來保存數據,代碼以下

from scrapy.spider import Spider
from scrapy.selector import Selector
from tutorial.items import DmozItem 


class DmozSpider(Spider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/",
    ]

    def parse(self, response):
        sel = Selector(response)
        sites = sel.xpath('//ul[@class="directory-url"]/li')
        items = []

        for site in sites:
            item = DmozItem()
            item['name'] = site.xpath('a/text()').extract()
            item['url'] = site.xpath('a/@href').extract()
            item['description'] = site.xpath('text()').re('-\s[^\n]*\\r')
            items.append(item)
        return items

3.5. 使用Item Pipeline

當Item在Spider中被收集以後,它將會被傳遞到Item Pipeline,一些組件會按照必定的順序執行對Item的處理。
每一個item pipeline組件(有時稱之爲ItemPipeline)是實現了簡單方法的Python類。他們接收到Item並經過它執行一些行爲,同時也決定此Item是否繼續經過pipeline,或是被丟棄而再也不進行處理。
如下是item pipeline的一些典型應用:

  • 清理HTML數據
  • 驗證爬取的數據(檢查item包含某些字段)
  • 查重(並丟棄)
  • 將爬取結果保存,如保存到數據庫、XML、JSON等文件中

 編寫你本身的item pipeline很簡單,每一個item pipeline組件是一個獨立的Python類,同時必須實現如下方法:

process_item(item, spider)  #每一個item pipeline組件都須要調用該方法,這個方法必須返回一個 Item (或任何繼承類)對象,或是拋出 DropItem異常,被丟棄的item將不會被以後的pipeline組件所處理。
#參數:
item: 由 parse 方法返回的 Item 對象(Item對象)
spider: 抓取到這個 Item 對象對應的爬蟲對象(Spider對象)

open_spider(spider)  #當spider被開啓時,這個方法被調用。
#參數: 
spider : (Spider object) – 被開啓的spider
  
close_spider(spider)  #當spider被關閉時,這個方法被調用,能夠再爬蟲關閉後進行相應的數據處理。
#參數: 
spider : (Spider object) – 被關閉的spider

爲JSON文件編寫一個items

from scrapy.exceptions import DropItem

class TutorialPipeline(object):

    # put all words in lowercase
    words_to_filter = ['politics', 'religion']

    def process_item(self, item, spider):
        for word in self.words_to_filter:
            if word in unicode(item['description']).lower():
                raise DropItem("Contains forbidden word: %s" % word)
        else:
            return item

在 settings.py 中設置ITEM_PIPELINES激活item pipeline,其默認爲[]

ITEM_PIPELINES = {'tutorial.pipelines.FilterWordsPipeline': 1}

3.6. 存儲數據

使用下面的命令存儲爲json文件格式

scrapy crawl dmoz -o items.json


 

文/Andrew_liu(簡書做者) 原文連接:http://www.jianshu.com/p/078ad2067419 著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。

相關文章
相關標籤/搜索