鍍金池/ 教程/ Python/ Scrapy 入門教程
Benchmarking
命令行工具(Command line tools)
下載器中間件(Downloader Middleware)
信號(hào)(Signals)
Telnet 終端(Telnet Console)
初窺 Scrapy
數(shù)據(jù)收集(Stats Collection)
Scrapyd
通用爬蟲(Broad Crawls)
Item Loaders
試驗(yàn)階段特性
Scrapy 入門教程
自動(dòng)限速(AutoThrottle)擴(kuò)展
Settings
Scrapy 終端(Scrapy shell)
下載項(xiàng)目圖片
DjangoItem
調(diào)試(Debugging)Spiders
選擇器(Selectors)
Feed exports
Spiders Contracts
借助 Firefox 來爬取
Logging
Spiders
Ubuntu 軟件包
實(shí)踐經(jīng)驗(yàn)(Common Practices)
安裝指南
Item Exporters
擴(kuò)展(Extensions)
Items
Spider 中間件(Middleware)
異常(Exceptions)
例子
發(fā)送 email
架構(gòu)概覽
常見問題(FAQ)
Jobs:暫停,恢復(fù)爬蟲
核心 API
使用 Firebug 進(jìn)行爬取
Item Pipeline
Link Extractors
Web Service
調(diào)試內(nèi)存溢出

Scrapy 入門教程

在本篇教程中,我們假定您已經(jīng)安裝好 Scrapy。 如若不然,請(qǐng)參考安裝指南

接下來以 Open Directory Project(dmoz) (dmoz) 為例來講述爬取。

本篇教程中將帶您完成下列任務(wù):

  1. 創(chuàng)建一個(gè) Scrapy 項(xiàng)目
  2. 定義提取的 Item
  3. 編寫爬取網(wǎng)站的 spider 并提取 Item
  4. 編寫 Item Pipeline 來存儲(chǔ)提取到的 Item(即數(shù)據(jù))

Scrapy 由 Python 編寫。如果您剛接觸并且好奇這門語(yǔ)言的特性以及 Scrapy 的詳情,對(duì)于已經(jīng)熟悉其他語(yǔ)言并且想快速學(xué)習(xí) Python 的編程老手,我們推薦 Learn Python The Hard Way,對(duì)于想從 Python 開始學(xué)習(xí)的編程新手,非程序員的 Python 學(xué)習(xí)資料列表將是您的選擇。

創(chuàng)建項(xiàng)目

在開始爬取之前,您必須創(chuàng)建一個(gè)新的 Scrapy 項(xiàng)目。 進(jìn)入您打算存儲(chǔ)代碼的目錄中,運(yùn)行下列命令:

scrapy startproject tutorial

該命令將會(huì)創(chuàng)建包含下列內(nèi)容的 tutorial 目錄:

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

這些文件分別是:

  • scrapy.cfg: 項(xiàng)目的配置文件
  • tutorial/: 該項(xiàng)目的 python 模塊。之后您將在此加入代碼。
  • tutorial/items.py: 項(xiàng)目中的 item 文件。
  • tutorial/pipelines.py: 項(xiàng)目中的 pipelines 文件。
  • tutorial/settings.py: 項(xiàng)目的設(shè)置文件。
  • tutorial/spiders/: 放置 spider 代碼的目錄。

定義 Item

Item 是保存爬取到的數(shù)據(jù)的容器;其使用方法和 python 字典類似, 并且提供了額外保護(hù)機(jī)制來避免拼寫錯(cuò)誤導(dǎo)致的未定義字段錯(cuò)誤。

類似在 ORM 中做的一樣,您可以通過創(chuàng)建一個(gè) scrapy.Item 類, 并且定義類型為 scrapy.Field 的類屬性來定義一個(gè) Item。 (如果不了解 ORM, 不用擔(dān)心,您會(huì)發(fā)現(xiàn)這個(gè)步驟非常簡(jiǎn)單)

首先根據(jù)需要從 dmoz.org 獲取到的數(shù)據(jù)對(duì) item 進(jìn)行建模。 我們需要從 dmoz 中獲取名字,url,以及網(wǎng)站的描述。 對(duì)此,在 item 中定義相應(yīng)的字段。編輯 tutorial 目錄中的 items.py 文件:

import scrapy

class DmozItem(scrapy.Item):
    title = scrapy.Field()
    link = scrapy.Field()
    desc = scrapy.Field()

一開始這看起來可能有點(diǎn)復(fù)雜,但是通過定義 item, 您可以很方便的使用 Scrapy 的其他方法。而這些方法需要知道您的 item 的定義。

編寫第一個(gè)爬蟲(Spider)

Spider 是用戶編寫用于從單個(gè)網(wǎng)站(或者一些網(wǎng)站)爬取數(shù)據(jù)的類。

其包含了一個(gè)用于下載的初始 URL,如何跟進(jìn)網(wǎng)頁(yè)中的鏈接以及如何分析頁(yè)面中的內(nèi)容, 提取生成 item 的方法。

為了創(chuàng)建一個(gè) Spider,您必須繼承 scrapy.Spider 類, 且定義以下三個(gè)屬性:

  • name: 用于區(qū)別 Spider。 該名字必須是唯一的,您不可以為不同的 Spider 設(shè)定相同的名字。
  • start_urls: 包含了 Spider 在啟動(dòng)時(shí)進(jìn)行爬取的 url 列表。 因此,第一個(gè)被獲取到的頁(yè)面將是其中之一。 后續(xù)的 URL 則從初始的 URL 獲取到的數(shù)據(jù)中提取。
  • parse() 是 spider 的一個(gè)方法。 被調(diào)用時(shí),每個(gè)初始 URL 完成下載后生成的 Response 對(duì)象將會(huì)作為唯一的參數(shù)傳遞給該函數(shù)。 該方法負(fù)責(zé)解析返回的數(shù)據(jù)(response data),提取數(shù)據(jù)(生成 item)以及生成需要進(jìn)一步處理的 URL 的 `Request 對(duì)象。

以下為我們的第一個(gè) Spider 代碼,保存在 tutorial/spiders 目錄下的 dmoz_spider.py 文件中:

import scrapy

class DmozSpider(scrapy.spiders.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)

爬取

進(jìn)入項(xiàng)目的根目錄,執(zhí)行下列命令啟動(dòng) spider:

scrapy crawl dmoz

crawl dmoz 啟動(dòng)用于爬取 dmoz.org 的 spider,您將得到類似的輸出:

2014-01-23 18:13:07-0400 [scrapy] INFO: Scrapy started (bot: tutorial)
2014-01-23 18:13:07-0400 [scrapy] INFO: Optional features available: ...
2014-01-23 18:13:07-0400 [scrapy] INFO: Overridden settings: {}
2014-01-23 18:13:07-0400 [scrapy] INFO: Enabled extensions: ...
2014-01-23 18:13:07-0400 [scrapy] INFO: Enabled downloader middlewares: ...
2014-01-23 18:13:07-0400 [scrapy] INFO: Enabled spider middlewares: ...
2014-01-23 18:13:07-0400 [scrapy] INFO: Enabled item pipelines: ...
2014-01-23 18:13:07-0400 [dmoz] INFO: Spider opened
2014-01-23 18:13:08-0400 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None)
2014-01-23 18:13:09-0400 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None)
2014-01-23 18:13:09-0400 [dmoz] INFO: Closing spider (finished)

查看包含 [dmoz] 的輸出,可以看到輸出的 log 中包含定義在 start_urls 的初始 URL,并且與 spider 中是一一對(duì)應(yīng)的。在 log 中可以看到其沒有指向其他頁(yè)面( (referer:None) )。

除此之外,更有趣的事情發(fā)生了。就像我們 parse 方法指定的那樣,有兩個(gè)包含 url 所對(duì)應(yīng)的內(nèi)容的文件被創(chuàng)建了: Book,Resources 。

剛才發(fā)生了什么?

Scrapy 為 Spider 的 start_urls 屬性中的每個(gè) URL 創(chuàng)建了 scrapy.Request 對(duì)象,并將 parse 方法作為回調(diào)函數(shù)(callback)賦值給了 Request。

Request 對(duì)象經(jīng)過調(diào)度,執(zhí)行生成 scrapy.http.Response 對(duì)象并送回給 spider parse() 方法。

提取 Item

Selectors 選擇器簡(jiǎn)介

從網(wǎng)頁(yè)中提取數(shù)據(jù)有很多方法。Scrapy 使用了一種基于 XPathCSS 表達(dá)式機(jī)制: Scrapy Selectors。 關(guān)于 selector 和其他提取機(jī)制的信息請(qǐng)參考 Selector 文檔。

這里給出 XPath 表達(dá)式的例子及對(duì)應(yīng)的含義:

  • /html/head/title: 選擇 HTML 文檔中<head>標(biāo)簽內(nèi)的<title>元素
  • /html/head/title/text(): 選擇上面提到的<title>元素的文字
  • //td: 選擇所有的<td>元素
  • //div[@class="mine"]: 選擇所有具有 class="mine"屬性的 div 元素

上邊僅僅是幾個(gè)簡(jiǎn)單的 XPath 例子,XPath 實(shí)際上要比這遠(yuǎn)遠(yuǎn)強(qiáng)大的多。 如果您想了解的更多,我們推薦這篇 XPath 教程

為了配合 XPath,Scrapy 除了提供了 Selector 之外,還提供了方法來避免每次從 response 中提取數(shù)據(jù)時(shí)生成 selector 的麻煩。

Selector 有四個(gè)基本的方法(點(diǎn)擊相應(yīng)的方法可以看到詳細(xì)的 API 文檔):

  • xpath(): 傳入 xpath 表達(dá)式,返回該表達(dá)式所對(duì)應(yīng)的所有節(jié)點(diǎn)的 selector list 列表 。
  • css(): 傳入 CSS 表達(dá)式,返回該表達(dá)式所對(duì)應(yīng)的所有節(jié)點(diǎn)的 selector list 列表.
  • extract(): 序列化該節(jié)點(diǎn)為 unicode 字符串并返回 list。
  • re(): 根據(jù)傳入的正則表達(dá)式對(duì)數(shù)據(jù)進(jìn)行提取,返回 unicode 字符串 list 列表。

在 Shell 中嘗試 Selector 選擇器

為了介紹 Selector 的使用方法,接下來我們將要使用內(nèi)置的 Scrapy shell。Scrapy Shell 需要您預(yù)裝好 IPython(一個(gè)擴(kuò)展的 Python 終端)。

您需要進(jìn)入項(xiàng)目的根目錄,執(zhí)行下列命令來啟動(dòng) shell:

scrapy shell "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/"

注解

當(dāng)您在終端運(yùn)行 Scrapy 時(shí),請(qǐng)一定記得給 url 地址加上引號(hào),否則包含參數(shù)的 url(例如 & 字符)會(huì)導(dǎo)致 Scrapy 運(yùn)行失敗。

shell 的輸出類似:

[ ... Scrapy log here ... ]

2015-01-07 22:01:53+0800 [domz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None)
[s] Available Scrapy objects:
[s]   crawler    <scrapy.crawler.Crawler object at 0x02CE2530>
[s]   item       {}
[s]   request    <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>
[s]   response   <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>
[s]   sel        <Selector xpath=None data=u'<html lang="en">\r\n<head>\r\n<meta http-equ'>
[s]   settings   <CrawlerSettings module=<module 'tutorial.settings' from 'tutorial\settings.pyc'>>
[s]   spider     <DomzSpider 'domz' at 0x302e350>
[s] Useful shortcuts:
[s]   shelp()           Shell help (print this help)
[s]   fetch(req_or_url) Fetch request (or URL) and update local objects
[s]   view(response)    View response in a browser

>>>

當(dāng) shell 載入后,您將得到一個(gè)包含 response 數(shù)據(jù)的本地 response 變量。輸入 response.body 將輸出 response 的包體,輸出 response.headers 可以看到 response 的包頭。

更為重要的是,當(dāng)輸入 response.selector 時(shí), 您將獲取到一個(gè)可以用于查詢返回?cái)?shù)據(jù)的 selector(選擇器), 以及映射到 response.selector.xpath() 、response.selector.css() 的 快捷方法(shortcut): response.xpath() 和 response.css() 。

同時(shí),shell 根據(jù) response 提前初始化了變量 sel 。該 selector 根據(jù) response 的類型自動(dòng)選擇最合適的分析規(guī)則(XML vs HTML)。

讓我們來試試:

In [1]: sel.xpath('//title')
Out[1]: [<Selector xpath='//title' data=u'<title>Open Directory - Computers: Progr'>]

In [2]: sel.xpath('//title').extract()
Out[2]: [u'<title>Open Directory - Computers: Programming: Languages: Python: Books</title>']

In [3]: sel.xpath('//title/text()')
Out[3]: [<Selector xpath='//title/text()' data=u'Open Directory - Computers: Programming:'>]

In [4]: sel.xpath('//title/text()').extract()
Out[4]: [u'Open Directory - Computers: Programming: Languages: Python: Books']

In [5]: sel.xpath('//title/text()').re('(\w+):')
Out[5]: [u'Computers', u'Programming', u'Languages', u'Python']

提取數(shù)據(jù)

現(xiàn)在,我們來嘗試從這些頁(yè)面中提取些有用的數(shù)據(jù)。

您可以在終端中輸入 response.body 來觀察 HTML 源碼并確定合適的 XPath 表達(dá)式。不過,這任務(wù)非常無聊且不易。您可以考慮使用 Firefox 的 Firebug 擴(kuò)展來使得工作更為輕松。詳情請(qǐng)參考 使用 Firebug 進(jìn)行爬取借助 Firefox 來爬取。

在查看了網(wǎng)頁(yè)的源碼后,您會(huì)發(fā)現(xiàn)網(wǎng)站的信息是被包含在 第二個(gè) <ul> 元素中。

我們可以通過這段代碼選擇該頁(yè)面中網(wǎng)站列表里所有 <li> 元素:

sel.xpath('//ul/li')

網(wǎng)站的描述:

sel.xpath('//ul/li/text()').extract()

網(wǎng)站的標(biāo)題:

sel.xpath('//ul/li/a/text()').extract()

以及網(wǎng)站的鏈接:

sel.xpath('//ul/li/a/@href').extract()

之前提到過,每個(gè).xpath()調(diào)用返回 selector 組成的 list,因此我們可以拼接更多的.xpath()來進(jìn)一步獲取某個(gè)節(jié)點(diǎn)。我們將在下邊使用這樣的特性:

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

注解

關(guān)于嵌套 selctor 的更多詳細(xì)信息,請(qǐng)參考 嵌套選擇器(selectors) 以及 選擇器(Selectors) 文檔中的 使用相對(duì) XPaths 部分。

在我們的 spider 中加入這段代碼:

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

現(xiàn)在嘗試再次爬取 dmoz.org,您將看到爬取到的網(wǎng)站信息被成功輸出:

scrapy crawl dmoz

使用 item

Item 對(duì)象是自定義的 python 字典。您可以使用標(biāo)準(zhǔn)的字典語(yǔ)法來獲取到其每個(gè)字段的值。(字段即是我們之前用 Field 賦值的屬性):

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

一般來說,Spider 將會(huì)將爬取到的數(shù)據(jù)以 Item 對(duì)象返回。所以為了將爬取的數(shù)據(jù)返回,我們最終的代碼將是:

import scrapy

from tutorial.items import DmozItem

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'):
            item = DmozItem()
            item['title'] = sel.xpath('a/text()').extract()
            item['link'] = sel.xpath('a/@href').extract()
            item['desc'] = sel.xpath('text()').extract()
            yield item

注解

您可以在 dirbot 項(xiàng)目中找到一個(gè)具有完整功能的 spider。該項(xiàng)目可以通過 https://github.com/scrapy/dirbot 找到。

現(xiàn)在對(duì) dmoz.org 進(jìn)行爬取將會(huì)產(chǎn)生 DmozItem 對(duì)象:

[dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>
     {'desc': [u' - By David Mertz; Addison Wesley. Book in progress, full text, ASCII format. Asks for feedback. [author website, Gnosis Software, Inc.\n],
      'link': [u'http://gnosis.cx/TPiP/'],
      'title': [u'Text Processing in Python']}
[dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>
     {'desc': [u' - By Sean McGrath; Prentice Hall PTR, 2000, ISBN 0130211192, has CD-ROM. Methods to build XML applications fast, Python tutorial, DOM and SAX, new Pyxie open source XML processing library. [Prentice Hall PTR]\n'],
      'link': [u'http://www.informit.com/store/product.aspx?isbn=0130211192'],
      'title': [u'XML Processing with Python']}

保存爬取到的數(shù)據(jù)

最簡(jiǎn)單存儲(chǔ)爬取的數(shù)據(jù)的方式是使用 Feed exports:

scrapy crawl dmoz -o items.json

該命令將采用 JSON 格式對(duì)爬取的數(shù)據(jù)進(jìn)行序列化,生成 items.json 文件。

在類似本篇教程里這樣小規(guī)模的項(xiàng)目中,這種存儲(chǔ)方式已經(jīng)足夠。 如果需要對(duì)爬取到的 item 做更多更為復(fù)雜的操作,您可以編寫 Item Pipeline 。 類似于我們?cè)趧?chuàng)建項(xiàng)目時(shí)對(duì) Item 做的,用于您編寫自己的 tutorial/pipelines.py 也被創(chuàng)建。 不過如果您僅僅想要保存 item,您不需要實(shí)現(xiàn)任何的 pipeline。

下一步

本篇教程僅介紹了 Scrapy 的基礎(chǔ),還有很多特性沒有涉及。請(qǐng)查看 初窺 Scrapy 章節(jié)中的 還有什么? 部分,大致瀏覽大部分重要的特性。

接著,我們推薦您把玩一個(gè)例子(查看例子),而后繼續(xù)閱讀基本概念