Scrapy入門,Spiders

Spiders

Spider類定義瞭如何爬取某個(或某些)網站。包括了爬取的動作(例如:是否跟進鏈接)以及如何從網頁的內容中提取結構化數據(爬取item)。 換句話說,Spider就是您定義爬取的動作及分析某個網頁(或者是有些網頁)的地方。

對spider來說,爬取的循環類似下文:

  1. 以初始的URL初始化Request,並設置回調函數。 當該request下載完畢並返回時,將生成response,並作爲參數傳給該回調函數。

    spider中初始的request是通過調用 start_requests() 來獲取的。 start_requests() 讀取 start_urls 中的URL, 並以 parse 爲回調函數生成 Request 。

  2. 在回調函數內分析返回的(網頁)內容,返回 Item 對象或者 Request 或者一個包括二者的可迭代容器。 返回的Request對象之後會經過Scrapy處理,下載相應的內容,並調用設置的callback函數(函數可相同)。

  3. 在回調函數內,您可以使用 選擇器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 來分析網頁內容,並根據分析的數據生成item。

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

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

Spider參數

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

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

scrapy crawl myspider -a category=electronics

Spider在構造器(constructor)中獲取參數:

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參數也可以通過Scrapyd的 schedule.json API來傳遞。 參見 Scrapyd documentation.

內置Spider參考手冊

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

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

import scrapy

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

Spider

classscrapy.spider.Spider

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

name

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

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

allowed_domains

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

start_urls

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

start_requests()

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

當spider啓動爬取並且未制定URL時,該方法被調用。 當指定了URL時,make_requests_from_url() 將被調用來創建Request對象。 該方法僅僅會被Scrapy調用一次,因此您可以將其實現爲生成器。

該方法的默認實現是使用 start_urls 的url生成Request。

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

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() 調用,也被用於轉化url爲request。

默認未被複寫(overridden)的情況下,該方法返回的Request對象中, parse() 作爲回調函數,dont_filter參數也被設置爲開啓。 (詳情參見 Request).

parse(response)

當response沒有指定回調函數時,該方法是Scrapy處理下載的response的默認方法。

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

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

參數: response (Response) – 用於分析的response

log(message[, levelcomponent])

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

closed(reason)

當spider關閉時,該函數被調用。 該方法提供了一個替代調用signals.connect()來監聽 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)

另一個在單個回調函數中返回多個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

classscrapy.contrib.spiders.CrawlSpider

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

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

rules

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

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

parse_start_url(response)

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

爬取規則(Crawling rules)

classscrapy.contrib.spiders.Rule(link_extractorcallback=Nonecb_kwargs=Nonefollow=Noneprocess_links=Noneprocess_request=None)

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

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

警告

當編寫爬蟲規則時,請避免使用 parse 作爲回調函數。 由於 CrawlSpider 使用 parse 方法來實現其邏輯,如果 您覆蓋了 parse 方法,crawl spider 將會運行失敗。

cb_kwargs 包含傳遞給回調函數的參數(keyword argument)的字典。

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

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

process_request 是一個callable或string(該spider中同名的函數將會被調用)。 該規則提取到每個request時都會調用該函數。該函數必須返回一個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並生成一些數據填入 Item 中。

XMLFeedSpider

classscrapy.contrib.spiders.XMLFeedSpider

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

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

iterator

用於確定使用哪個迭代器的string。可選項有:

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

默認值爲 iternodes 。

itertag

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

itertag = 'product'

namespaces

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

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

例如:

class YourSpider(XMLFeedSpider):

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

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

adapt_response(response)

該方法在spider分析response前被調用。您可以在response被分析之前使用該函數來修改內容(body)。 該方法接受一個response並返回一個response(可以相同也可以不同)。

parse_node(responseselector)

當節點符合提供的標籤名時(itertag)該方法被調用。 接收到的response以及相應的 Selector 作爲參數傳遞給該方法。 該方法返回一個 Item 對象或者 Request 對象 或者一個包含二者的可迭代對象(iterable)。

process_results(responseresults)

當spider返回結果(item或request)時該方法被調用。 設定該方法的目的是在結果返回給框架核心(framework core)之前做最後的處理, 例如設定item的ID。其接受一個結果的列表(list of results)及對應的response。 其結果必須返回一個結果的列表(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

簡單來說,我們在這裏創建了一個spider,從給定的 start_urls 中下載feed, 並迭代feed中每個 item 標籤,輸出,並在 Item 中存儲有些隨機數據。

CSVFeedSpider

classscrapy.contrib.spiders.CSVFeedSpider

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

delimiter

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

headers

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

parse_row(responserow)

該方法接收一個response對象及一個以提供或檢測出來的header爲鍵的字典(代表每行)。 該spider中,您也可以覆蓋 adapt_response 及 process_results 方法來進行預處理(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 = ';'
    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

classscrapy.contrib.spiders.SitemapSpider

SitemapSpider使您爬取網站時可以通過 Sitemaps 來發現爬取的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的處理函數。 callback 可以是一個字符串(spider中方法的名字)或者是callable。

例如:

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

規則按順序進行匹配,之後第一個匹配纔會被應用。

如果您忽略該屬性,sitemap中發現的所有url將會被 parse 函數處理。

sitemap_follow

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

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

sitemap_alternate_links

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

例如:

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

當 sitemap_alternate_links 設置時,兩個URL都會被獲取。 當 sitemap_alternate_links 關閉時,只有 http://example.com/ 會被獲取。

默認 sitemap_alternate_links 關閉。

SitemapSpider樣例

簡單的例子: 使用 parse 處理通過sitemap發現的所有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 ...

用特定的函數處理某些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 ...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章