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

Spiders

Spider 類定義了如何爬取某個(或某些)網(wǎng)站。包括了爬取的動作(例如:是否跟進鏈接)以及如何從網(wǎng)頁的內(nèi)容中提取結(jié)構(gòu)化數(shù)據(jù)(爬取 item)。換句話說,Spider 就是您定義爬取的動作及分析某個網(wǎng)頁(或者是有些網(wǎng)頁)的地方。

對 spider 來說,爬取的循環(huán)類似下文:

  1. 以初始的 URL 初始化 Request,并設(shè)置回調(diào)函數(shù)。當該 request 下載完畢并返回時,將生成 response,并作為參數(shù)傳給該回調(diào)函數(shù)。

    spider 中初始的 request 是通過調(diào)用 start_requests()來獲取的。start_requests()讀取 start_urls 中的 URL, 并以 parse 為回調(diào)函數(shù)生成 Request。

  2. 在回調(diào)函數(shù)內(nèi)分析返回的(網(wǎng)頁)內(nèi)容,返回 Item 對象或者 Request 或者一個包括二者的可迭代容器。 返回的 Request 對象之后會經(jīng)過 Scrapy 處理,下載相應(yīng)的內(nèi)容,并調(diào)用設(shè)置的 callback 函數(shù)(函數(shù)可相同)。

  3. 在回調(diào)函數(shù)內(nèi),您可以使用 選擇器(Selectors) (您也可以使用 BeautifulSoup, lxml 或者您想用的任何解析器) 來分析網(wǎng)頁內(nèi)容,并根據(jù)分析的數(shù)據(jù)生成 item。

  4. 最后,由 spider 返回的 item 將被存到數(shù)據(jù)庫(由某些 Item Pipeline 處理)或使用 Feed exports 存入到文件中。

雖然該循環(huán)對任何類型的 spider 都(多少)適用,但 Scrapy 仍然為了不同的需求提供了多種默認 spider。 之后將討論這些 spider。

Spider 參數(shù)

Spider 可以通過接受參數(shù)來修改其功能。 spider 參數(shù)一般用來定義初始 URL 或者指定限制爬取網(wǎng)站的部分。 您也可以使用其來配置 spider 的任何功能。

在運行 crawl 時添加-a 可以傳遞 Spider 參數(shù):

scrapy crawl myspider -a category=electronics

Spider 在構(gòu)造器(constructor)中獲取參數(shù):

import scrapy

class MySpider(Spider):
    name = 'myspider'

    def __init__(self, category=None, *args, **kwargs):
        super(MySpider, self).__init__(*args, **kwargs)
        self.start_urls = ['http://www.example.com/categories/%s' % category]
        # ...

Spider 參數(shù)也可以通過 Scrapyd 的 schedule.json API 來傳遞。 參見 Scrapyd documentation。

內(nèi)置 Spider 參考手冊

Scrapy 提供多種方便的通用 spider 供您繼承使用。 這些 spider 為一些常用的爬取情況提供方便的特性, 例如根據(jù)某些規(guī)則跟進某個網(wǎng)站的所有鏈接、根據(jù) Sitemaps 來進行爬取,或者分析 XML/CSV 源。

下面 spider 的示例中,我們假定您有個項目在 myproject.items 模塊中聲明了 TestItem

import scrapy

class TestItem(scrapy.Item):
    id = scrapy.Field()
    name = scrapy.Field()
    description = scrapy.Field()

Spider

class scrapy.spider.Spider

Spider 是最簡單的 spider。每個其他的 spider 必須繼承自該類(包括 Scrapy 自帶的其他 spider 以及您自己編寫的 spider)。Spider 并沒有提供什么特殊的功能。其僅僅請求給定的 start_urls/start_requests,并根據(jù)返回的結(jié)果(resulting responses)調(diào)用 spider 的 parse 方法。

name

定義 spider 名字的字符串(string)。spider 的名字定義了 Scrapy 如何定位(并初始化)spider,所以其必須是唯一的。 不過您可以生成多個相同的 spider 實例(instance),這沒有任何限制。 name 是 spider 最重要的屬性,而且是必須的。

如果該 spider 爬取單個網(wǎng)站(single domain),一個常見的做法是以該網(wǎng)站(domain)(加或不加后綴)來命名 spider。 例如,如果 spider 爬取 mywebsite.com,該 spider 通常會被命名為 mywebsite 。

allowed_domains

可選。包含了 spider 允許爬取的域名(domain)列表(list)。 當 OffsiteMiddleware 啟用時, 域名不在列表中的 URL 不會被跟進。

start_urls

URL 列表。當沒有制定特定的 URL 時,spider 將從該列表中開始進行爬取。 因此,第一個被獲取到的頁面的 URL 將是該列表之一。后續(xù)的 URL 將會從獲取到的數(shù)據(jù)中提取。

start_requests()

該方法必須返回一個可迭代對象(iterable)。該對象包含了 spider 用于爬取的第一個 Request。

當 spider 啟動爬取并且未制定 URL 時,該方法被調(diào)用。 當指定了 URL 時,make_requests_from_url()將被調(diào)用來創(chuàng)建 Request 對象。 該方法僅僅會被 Scrapy 調(diào)用一次,因此您可以將其實現(xiàn)為生成器。

該方法的默認實現(xiàn)是使用 start_urls 的 url 生成 Request。

如果您想要修改最初爬取某個網(wǎng)站的 Request 對象,您可以重寫(override)該方法。 例如,如果您需要在啟動時以 POST 登錄某個網(wǎng)站,你可以這么寫:

def start_requests(self):
    return [scrapy.FormRequest("http://www.example.com/login",
                               formdata={'user': 'john', 'pass': 'secret'},
                               callback=self.logged_in)]

def logged_in(self, response):
    # here you would extract links to follow and return Requests for
    # each of them, with another callback
    pass
make_requests_from_url(url)

該方法接受一個 URL 并返回用于爬取的 Request 對象。該方法在初始化 request 時被 start_requests()調(diào)用,也被用于轉(zhuǎn)化 url 為 request。

默認未被復寫(overridden)的情況下,該方法返回的 Request 對象中,parse()作為回調(diào)函數(shù),dont_filter 參數(shù)也被設(shè)置為開啟。(詳情參見 Request)。

parse(response)

當 response 沒有指定回調(diào)函數(shù)時,該方法是 Scrapy 處理下載的 response 的默認方法。

parse 負責處理 response 并返回處理的數(shù)據(jù)以及(/或)跟進的 URL。 Spider 對其他的 Request 的回調(diào)函數(shù)也有相同的要求。

該方法及其他的 Request 回調(diào)函數(shù)必須返回一個包含 Request 及(或) Item 的可迭代的對象。

參數(shù): response (Response) – 用于分析的 response

log(message[, level, component])

使用 scrapy.log.msg() 方法記錄(log)message。log 中自動帶上該 spider 的 name 屬性。 更多數(shù)據(jù)請參見 Logging。

closed(reason)

當 spider 關(guān)閉時,該函數(shù)被調(diào)用。 該方法提供了一個替代調(diào)用 signals.connect()來監(jiān)聽 spider_closed 信號的快捷方式。

Spider 樣例

讓我們來看一個例子:

import scrapy

class 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):
        self.log('A response from %s just arrived!' % response.url)

另一個在單個回調(diào)函數(shù)中返回多個 Request 以及 Item 的例子:

import scrapy
from myproject.items import MyItem

class 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)

CrawlSpider

class scrapy.contrib.spiders.CrawlSpider

爬取一般網(wǎng)站常用的 spider。其定義了一些規(guī)則(rule)來提供跟進 link 的方便的機制。 也許該 spider 并不是完全適合您的特定網(wǎng)站或項目,但其對很多情況都使用。因此您可以以其為起點,根據(jù)需求修改部分方法。當然您也可以實現(xiàn)自己的 spider。

除了從 Spider 繼承過來的(您必須提供的)屬性外,其提供了一個新的屬性:

rules

一個包含一個(或多個) Rule 對象的集合(list)。 每個 Rule 對爬取網(wǎng)站的動作定義了特定表現(xiàn)。 Rule 對象在下邊會介紹。 如果多個 rule 匹配了相同的鏈接,則根據(jù)他們在本屬性中被定義的順序,第一個會被使用。

該 spider 也提供了一個可復寫(overrideable)的方法:

parse_start_url(response)

當 start_url 的請求返回時,該方法被調(diào)用。 該方法分析最初的返回值并必須返回一個 Item 對象或者 一個 Request 對象或者 一個可迭代的包含二者對象。

爬取規(guī)則(Crawling rules)

class scrapy.contrib.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)

link_extractor 是一個 Link Extractor 對象。其定義了如何從爬取到的頁面提取鏈接。

callback 是一個 callable 或 string(該 spider 中同名的函數(shù)將會被調(diào)用)。從 link_extractor 中每獲取到鏈接時將會調(diào)用該函數(shù)。該回調(diào)函數(shù)接受一個 response 作為其第一個參數(shù),并返回一個包含 Item 以及(或) Request 對象(或者這兩者的子類)的列表(list)。

警告

當編寫爬蟲規(guī)則時,請避免使用 parse 作為回調(diào)函數(shù)。 由于 CrawlSpider 使用 parse 方法來實現(xiàn)其邏輯,如果 您覆蓋了 parse 方法,crawl spider 將會運行失敗。

cb_kwargs 包含傳遞給回調(diào)函數(shù)的參數(shù)(keyword argument)的字典。

follow 是一個布爾(boolean)值,指定了根據(jù)該規(guī)則從 response 提取的鏈接是否需要跟進。 如果 callback 為 None,follow 默認設(shè)置為 True,否則默認為 False。

process_links 是一個 callable 或 string(該 spider 中同名的函數(shù)將會被調(diào)用)。 從 link_extractor 中獲取到鏈接列表時將會調(diào)用該函數(shù)。該方法主要用來過濾。

process_request 是一個 callable 或 string(該 spider 中同名的函數(shù)將會被調(diào)用)。 該規(guī)則提取到每個 request 時都會調(diào)用該函數(shù)。該函數(shù)必須返回一個 request 或者 None。 (用來過濾 request)

CrawlSpider 樣例

接下來給出配合 rule 使用 CrawlSpider 的例子:

import scrapy
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors import LinkExtractor

class MySpider(CrawlSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com']

    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'),
    )

    def parse_item(self, response):
        self.log('Hi, this is an item page! %s' % response.url)

        item = scrapy.Item()
        item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (\d+)')
        item['name'] = response.xpath('//td[@id="item_name"]/text()').extract()
        item['description'] = response.xpath('//td[@id="item_description"]/text()').extract()
        return item

該 spider 將從 example.com 的首頁開始爬取,獲取 category 以及 item 的鏈接并對后者使用 parse_item 方法。當 item 獲得返回(response)時,將使用 XPath 處理 HTML 并生成一些數(shù)據(jù)填入 Item 中。

XMLFeedSpider

class scrapy.contrib.spiders.XMLFeedSpider

XMLFeedSpider 被設(shè)計用于通過迭代各個節(jié)點來分析 XML 源(XML feed)。迭代器可以從 iternodes,xml,html 選擇。鑒于 xml 以及 html 迭代器需要先讀取所有 DOM 再分析而引起的性能問題,一般還是推薦使用 iternodes。不過使用 html 作為迭代器能有效應(yīng)對錯誤的 XML。

您必須定義下列類屬性來設(shè)置迭代器以及標簽名(tag name):

iterator

用于確定使用哪個迭代器的 string??蛇x項有:

  • 'iternodes' -一個高性能的基于正則表達式的迭代器
  • 'html' -使用 Selector 的迭代器。 需要注意的是該迭代器使用 DOM 進行分析,其需要將所有的 DOM 載入內(nèi)存, 當數(shù)據(jù)量大的時候會產(chǎn)生問題。
  • 'xml' -使用 Selector 的迭代器。 需要注意的是該迭代器使用 DOM 進行分析,其需要將所有的DOM 載入內(nèi)存,當數(shù)據(jù)量大的時候會產(chǎn)生問題。

默認值為 iternodes 。

itertag

一個包含開始迭代的節(jié)點名的 string。例如:

itertag = 'product'
namespaces

一個由 (prefix, url) 元組(tuple)所組成的 list。 其定義了在該文檔中會被 spider 處理的可用的 namespace。 prefix 及 uri 會被自動調(diào)用 register_namespace()生成 namespace。

您可以通過在 itertag 屬性中制定節(jié)點的 namespace。

例如:

class YourSpider(XMLFeedSpider):

    namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')]
    itertag = 'n:url'
    # ...

除了這些新的屬性之外,該 spider 也有以下可以覆蓋(overrideable)的方法:

adapt_response(response)

該方法在 spider 分析 response 前被調(diào)用。您可以在 response 被分析之前使用該函數(shù)來修改內(nèi)容(body)。該方法接受一個 response 并返回一個 response(可以相同也可以不同)。

parse_node(response, selector)

當節(jié)點符合提供的標簽名時(itertag)該方法被調(diào)用。 接收到的 response 以及相應(yīng)的 Selector 作為參數(shù)傳遞給該方法。 該方法返回一個 Item 對象或者 Request 對象 或者一個包含二者的可迭代對象(iterable)。

process_results(response, results)

當 spider 返回結(jié)果(item 或 request)時該方法被調(diào)用。設(shè)定該方法的目的是在結(jié)果返回給框架核心(framework core)之前做最后的處理,例如設(shè)定 item 的 ID。其接受一個結(jié)果的列表(list of results)及對應(yīng)的 response。其結(jié)果必須返回一個結(jié)果的列表(list of results)(包含 Item 或者 Request 對象)。

XMLFeedSpider 例子

該 spider 十分易用。下邊是其中一個例子:

from scrapy import log
from scrapy.contrib.spiders import XMLFeedSpider
from myproject.items import TestItem

class MySpider(XMLFeedSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com/feed.xml']
    iterator = 'iternodes' # This is actually unnecessary, since it's the default value
    itertag = 'item'

    def parse_node(self, response, node):
        log.msg('Hi, this is a <%s> node!: %s' % (self.itertag, ''.join(node.extract())))

        item = TestItem()
        item['id'] = node.xpath('@id').extract()
        item['name'] = node.xpath('name').extract()
        item['description'] = node.xpath('description').extract()
        return item

簡單來說,我們在這里創(chuàng)建了一個 spider,從給定的 start_urls 中下載 feed,并迭代 feed 中每個 item 標簽,輸出,并在 Item 中存儲有些隨機數(shù)據(jù)。

CSVFeedSpider

class scrapy.contrib.spiders.CSVFeedSpider

該 spider 除了其按行遍歷而不是節(jié)點之外其他和 XMLFeedSpider 十分類似。 而其在每次迭代時調(diào)用的是 parse_row()

delimiter

在 CSV 文件中用于區(qū)分字段的分隔符。類型為 string。 默認為 ',' (逗號)。

quotechar

A string with the enclosure character for each field in the CSV file Defaults to '"' (quotation mark).

headers

在 CSV 文件中包含的用來提取字段的行的列表。參考下邊的例子。

parse_row(response, row)

該方法接收一個 response 對象及一個以提供或檢測出來的 header 為鍵的字典(代表每行)。 該 spider 中,您也可以覆蓋 adapt_responseprocess_results 方法來進行預(yù)處理(pre-processing)及后(post-processing)處理。

CSVFeedSpider 例子

下面的例子和之前的例子很像,但使用了 CSVFeedSpider:

from scrapy import log
from scrapy.contrib.spiders import CSVFeedSpider
from myproject.items import TestItem

class MySpider(CSVFeedSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com/feed.csv']
    delimiter = ';'
    quotechar = "'"
    headers = ['id', 'name', 'description']

    def parse_row(self, response, row):
        log.msg('Hi, this is a row!: %r' % row)

        item = TestItem()
        item['id'] = row['id']
        item['name'] = row['name']
        item['description'] = row['description']
        return item

SitemapSpider

class scrapy.contrib.spiders.SitemapSpider

SitemapSpider 使您爬取網(wǎng)站時可以通過 Sitemaps 來發(fā)現(xiàn)爬取的 URL。

其支持嵌套的 sitemap,并能從 robots.txt 中獲取 sitemap 的 url。

sitemap_urls

包含您要爬取的 url 的 sitemap 的 url 列表(list)。您也可以指定為一個 robots.txt,spider 會從中分析并提取 url。

sitemap_rules

一個包含 (regex, callback) 元組的列表(list):

  • regex 是一個用于匹配從 sitemap 提供的 url 的正則表達式。regex 可以是一個字符串或者編譯的正則對象(compiled regex object)。
  • callback 指定了匹配正則表達式的 url 的處理函數(shù)。callback 可以是一個字符串(spider 中方法的名字)或者是 callable。

例如:

sitemap_rules = [('/product/', 'parse_product')]

規(guī)則按順序進行匹配,之后第一個匹配才會被應(yīng)用。

如果您忽略該屬性,sitemap 中發(fā)現(xiàn)的所有 url 將會被 parse 函數(shù)處理。

sitemap_follow

一個用于匹配要跟進的 sitemap 的正則表達式的列表(list)。其僅僅被應(yīng)用在 使用 Sitemap index files 來指向其他 sitemap 文件的站點。

默認情況下所有的 sitemap 都會被跟進。

sitemap_alternate_links

指定當一個 url 有可選的鏈接時,是否跟進。 有些非英文網(wǎng)站會在一個 url 塊內(nèi)提供其他語言的網(wǎng)站鏈接。

例如:

<url>
    <loc>http://example.com/</loc>
    <xhtml:link rel="alternate" hreflang="de" />
</url>

sitemap_alternate_links 設(shè)置時,兩個 URL 都會被獲取。 當 sitemap_alternate_links 關(guān)閉時,只有 http://example.com/ 會被獲取。

默認 sitemap_alternate_links 關(guān)閉。

SitemapSpider 樣例

簡單的例子:使用 parse 處理通過 sitemap 發(fā)現(xiàn)的所有 url:

from scrapy.contrib.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/sitemap.xml']

    def parse(self, response):
        pass # ... scrape item here ...

用特定的函數(shù)處理某些 url,其他的使用另外的 callback:

from scrapy.contrib.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/sitemap.xml']
    sitemap_rules = [
        ('/product/', 'parse_product'),
        ('/category/', 'parse_category'),
    ]

    def parse_product(self, response):
        pass # ... scrape product ...

    def parse_category(self, response):
        pass # ... scrape category ...

跟進 robots.txt 文件定義的 sitemap 并只跟進包含有..sitemap_shop 的 url:

from scrapy.contrib.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/robots.txt']
    sitemap_rules = [
        ('/shop/', 'parse_shop'),
    ]
    sitemap_follow = ['/sitemap_shops']

    def parse_shop(self, response):
        pass # ... scrape shop here ...

在 SitemapSpider 中使用其他 url:

from scrapy.contrib.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/robots.txt']
    sitemap_rules = [
        ('/shop/', 'parse_shop'),
    ]

    other_urls = ['http://www.example.com/about']

    def start_requests(self):
        requests = list(super(MySpider, self).start_requests())
        requests += [scrapy.Request(x, self.parse_other) for x in self.other_urls]
        return requests

    def parse_shop(self, response):
        pass # ... scrape shop here ...

    def parse_other(self, response):
        pass # ... scrape other here ...
上一篇:核心 API下一篇:DjangoItem