Python的爬蟲程式編寫架構Scrapy入門學習教程

來源:互聯網
上載者:User
1. Scrapy簡介
Scrapy是一個為了爬取網站資料,提取結構性資料而編寫的應用程式框架。 可以應用在包括資料採礦,資訊處理或儲存曆史資料等一系列的程式中。
其最初是為了頁面抓取 (更確切來說, 網路抓取 )所設計的, 也可以應用在擷取API所返回的資料(例如 Amazon Associates Web Services ) 或者通用的網路爬蟲。Scrapy用途廣泛,可以用於資料採礦、監測和自動化測試
Scrapy 使用了 Twisted非同步網路程式庫來處理網路通訊。整體架構大致如下

Scrapy主要包括了以下組件:

(1)引擎(Scrapy): 用來處理整個系統的資料流處理, 觸發事務(架構核心)

(2)調度器(Scheduler): 用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 可以想像成一個URL(抓取網頁的網址或者說是連結)的優先隊列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址

(3)下載器(Downloader): 用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效的非同步模型上的)

(4)爬蟲(Spiders): 爬蟲是主要幹活的, 用於從特定的網頁中提取自己需要的資訊, 即所謂的實體(Item)。使用者也可以從中提取出連結,讓Scrapy繼續抓取下一個頁面

項目管道(Pipeline): 負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證實體的有效性、清除不需要的資訊。當頁面被爬蟲解析後,將被發送到項目管道,並經過幾個特定的次序處理資料。

(5)下載器中介軟體(Downloader Middlewares): 位於Scrapy引擎和下載器之間的架構,主要是處理Scrapy引擎與下載器之間的請求及響應。

(6)爬蟲中介軟體(Spider Middlewares): 介於Scrapy引擎和爬蟲之間的架構,主要工作是處理蜘蛛的響應輸入和請求輸出。

(7)調度中介軟體(Scheduler Middewares): 介於Scrapy引擎和調度之間的中介軟體,從Scrapy引擎發送到調度的請求和響應。

Scrapy運行流程大概如下:

首先,引擎從調度器中取出一個連結(URL)用於接下來的抓取
引擎把URL封裝成一個請求(Request)傳給下載器,下載器把資源下載下來,並封裝成應答包(Response)
然後,爬蟲解析Response
若是解析出實體(Item),則交給實體管道進行進一步的處理。
若是解析出的是連結(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工程. 進入一個你想用來儲存代碼的目錄,然後執行:

$ scrapy startproject tutorial

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

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

這些檔案主要是:

(1)scrapy.cfg: 項目設定檔
(2)tutorial/: 項目python模組, 之後您將在此加入代碼
(3)tutorial/items.py: 項目items檔案
(4)tutorial/pipelines.py: 項目管道檔案
(5)tutorial/settings.py: 項目設定檔
(6)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, Fieldclass 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擷取到的資料中提取。我們可以利用Regex定義和過濾需要進行跟進的連結。
parse():是spider的一個方法。被調用時,每個初始URL完成下載後產生的 Response 對象將會作為唯一的參數傳遞給該函數。該方法負責解析返回的資料(response data),提取資料(產生item)以及產生需要進一步處理的URL的 Request 對象。
這個方法負責解析返回的資料、匹配抓取的資料(解析為 item )並跟蹤更多的 URL。
在 /tutorial/tutorial/spiders 目錄下建立 dmoz_spider.py

import scrapyclass 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, http112014-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, SpiderState2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, RedirectMiddleware, CookiesMiddleware, ChunkedTransferMiddleware, DownloaderStats2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled item pipelines:2014-12-15 09:30:59+0800 [dmoz] INFO: Spider opened2014-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:60232014-12-15 09:30:59+0800 [scrapy] DEBUG: Web service listening on 127.0.0.1:60802014-12-15 09:31:00+0800 [dmoz] DEBUG: Crawled (200)  (referer: None)2014-12-15 09:31:00+0800 [dmoz] DEBUG: Crawled (200)  (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文檔中 標籤內的 元素</li>
  • /html/head/title/text(): 選擇 元素內的文本</li>
  • //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字串列表,字串作為參數由Regex提取出來

3.4.2. 取出資料

  • 首先使用Google瀏覽器開發人員工具, 查看網站源碼, 來看自己需要取出的資料形式(這種方法比較麻煩), 更簡單的方法是直接對感興趣的東西右鍵審查元素, 可以直接查看網站源碼

在查看網站源碼後, 網站資訊在第二個

      
    • Core Python Programming - 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]
    • ...省略部分...

    那麼就可以通過一下方式進行提取資料

    #通過如下命令選擇每個在網站中的 
  • 元素: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 scrapyclass 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 Spiderfrom scrapy.selector import Selectorfrom tutorial.items import DmozItemclass 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類,同時必須實現以下方法:

    (1)process_item(item, spider) #每個item pipeline組件都需要調用該方法,這個方法必須返回一個 Item (或任何繼承類)對象,或是拋出 DropItem異常,被丟棄的item將不會被之後的pipeline組件所處理。

    #參數:

    item: 由 parse 方法返回的 Item 對象(Item對象)

    spider: 抓取到這個 Item 對象對應的爬蟲對象(Spider對象)

    (2)open_spider(spider) #當spider被開啟時,這個方法被調用。

    #參數:

    spider : (Spider object) – 被開啟的spider

    (3)close_spider(spider) #當spider被關閉時,這個方法被調用,可以再爬蟲關閉後進行相應的資料處理。

    #參數:

    spider : (Spider object) – 被關閉的spider

    為JSON檔案編寫一個items

    from scrapy.exceptions import DropItemclass 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

    4.樣本
    4.1最簡單的spider(預設的Spider)
    用執行個體屬性start_urls中的URL構造Request對象
    架構負責執行request
    將request返回的response對象傳遞給parse方法做分析

    簡化後的源碼:

    class Spider(object_ref): """Base class for scrapy spiders. All spiders must inherit from this class. """  name = None  def __init__(self, name=None, **kwargs):  if name is not None:   self.name = name  elif not getattr(self, 'name', None):   raise ValueError("%s must have a name" % type(self).__name__)  self.__dict__.update(kwargs)  if not hasattr(self, 'start_urls'):   self.start_urls = []  def start_requests(self):  for url in self.start_urls:   yield self.make_requests_from_url(url)  def make_requests_from_url(self, url):  return Request(url, dont_filter=True)  def parse(self, response):  raise NotImplementedError  BaseSpider = create_deprecated_class('BaseSpider', Spider)

    一個回呼函數返回多個request的例子

    import scrapyfrom myproject.items import MyItemclass MySpider(scrapy.Spider): name = 'example.com' allowed_domains = ['example.com'] start_urls = [  'http://www.example.com/1.html',  'http://www.example.com/2.html',  'http://www.example.com/3.html', ]  def parse(self, response):  sel = scrapy.Selector(response)  for h3 in response.xpath('//h3').extract():   yield MyItem(title=h3)   for url in response.xpath('//a/@href').extract():   yield scrapy.Request(url, callback=self.parse)

    構造一個Request對象只需兩個參數: URL和回呼函數

    4.2CrawlSpider
    通常我們需要在spider中決定:哪些網頁上的連結需要跟進, 哪些網頁到此為止,無需跟進裡面的連結。CrawlSpider為我們提供了有用的抽象——Rule,使這類爬取任務變得簡單。你只需在rule中告訴scrapy,哪些是需要跟進的。
    回憶一下我們爬行mininova網站的spider.

    class MininovaSpider(CrawlSpider): name = 'mininova' allowed_domains = ['mininova.org'] start_urls = ['http://www.mininova.org/yesterday'] rules = [Rule(LinkExtractor(allow=['/tor/\d+']), 'parse_torrent')]  def parse_torrent(self, response):  torrent = TorrentItem()  torrent['url'] = response.url  torrent['name'] = response.xpath("//h1/text()").extract()  torrent['description'] = response.xpath("//div[@id='description']").extract()  torrent['size'] = response.xpath("//div[@id='specifications']/p[2]/text()[2]").extract()  return torrent

    上面代碼中 rules的含義是:匹配/tor/\d+的URL返回的內容,交給parse_torrent處理,並且不再跟進response上的URL。
    官方文檔中也有個例子:

     rules = (  # 提取匹配 'category.php' (但不匹配 'subsection.php') 的連結並跟進連結(沒有callback意味著follow預設為True)  Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),   # 提取匹配 'item.php' 的連結並使用spider的parse_item方法進行分析  Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item'), )

    除了Spider和CrawlSpider外,還有XMLFeedSpider, CSVFeedSpider, SitemapSpider

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.