擴(kuò)展框架提供一個(gè)機(jī)制,使得你能將自定義功能綁定到 Scrapy。
擴(kuò)展只是正常的類,它們?cè)?Scrapy 啟動(dòng)時(shí)被實(shí)例化、初始化。
擴(kuò)展使用 Scrapy settings 管理它們的設(shè)置,這跟其他 Scrapy 代碼一樣。
通常擴(kuò)展需要給它們的設(shè)置加上前綴,以避免跟已有(或?qū)?lái))的擴(kuò)展沖突。 比如,一個(gè)擴(kuò)展處理 Google Sitemaps,則可以使用類似 GOOGLESITEMAP_ENABLED、GOOGLESITEMAP_DEPTH 等設(shè)置。
擴(kuò)展在擴(kuò)展類被實(shí)例化時(shí)加載和激活。 因此,所有擴(kuò)展的實(shí)例化代碼必須在類的構(gòu)造函數(shù)(__init__
)中執(zhí)行。
要使得擴(kuò)展可用,需要把它添加到 Scrapy 的 EXTENSIONS
配置中。在 EXTENSIONS
中,每個(gè)擴(kuò)展都使用一個(gè)字符串表示,即擴(kuò)展類的全 Python 路徑。比如:
EXTENSIONS = {
'scrapy.contrib.corestats.CoreStats': 500,
'scrapy.telnet.TelnetConsole': 500,
}
如你所見(jiàn),EXTENSIONS
配置是一個(gè) dict,key 是擴(kuò)展類的路徑,value 是順序,它定義擴(kuò)展加載的順序。擴(kuò)展順序不像中間件的順序那么重要,而且擴(kuò)展之間一般沒(méi)有關(guān)聯(lián)。擴(kuò)展加載的順序并不重要,因?yàn)樗鼈儾⒉幌嗷ヒ蕾嚒?/p>
如果你需要添加擴(kuò)展而且它依賴別的擴(kuò)展,你就可以使用該特性了。
這也是為什么 Scrapy 的配置項(xiàng) EXTENSIONS_BASE
(它包括了所有內(nèi)置且開(kāi)啟的擴(kuò)展)定義所有擴(kuò)展的順序都相同(500
)。
并不是所有可用的擴(kuò)展都會(huì)被開(kāi)啟。一些擴(kuò)展經(jīng)常依賴一些特別的配置。 比如,HTTP Cache 擴(kuò)展是可用的但默認(rèn)是禁用的,除非 HTTPCACHE_ENABLED
配置項(xiàng)設(shè)置了。
為了禁用一個(gè)默認(rèn)開(kāi)啟的擴(kuò)展(比如,包含在 EXTENSIONS_BASE
中的擴(kuò)展),需要將其順序(order)設(shè)置為 None
。比如:
EXTENSIONS = {
'scrapy.contrib.corestats.CoreStats': None,
}
實(shí)現(xiàn)你的擴(kuò)展很簡(jiǎn)單。每個(gè)擴(kuò)展是一個(gè)單一的 Python class,它不需要實(shí)現(xiàn)任何特殊的方法。
Scrapy 擴(kuò)展(包括 middlewares 和 pipelines)的主要入口是 from_crawler
類方法,它接收一個(gè) Crawler
類的實(shí)例,該實(shí)例是控制 Scrapy crawler 的主要對(duì)象。如果擴(kuò)展需要,你可以通過(guò)這個(gè)對(duì)象訪問(wèn) settings,signals,stats,控制爬蟲(chóng)的行為。
通常來(lái)說(shuō),擴(kuò)展關(guān)聯(lián)到 signals 并執(zhí)行它們觸發(fā)的任務(wù)。
最后,如果 from_crawler
方法拋出 NotConfigured
異常,擴(kuò)展會(huì)被禁用。否則,擴(kuò)展會(huì)被開(kāi)啟。
這里我們將實(shí)現(xiàn)一個(gè)簡(jiǎn)單的擴(kuò)展來(lái)演示上面描述到的概念。 該擴(kuò)展會(huì)在以下事件時(shí)記錄一條日志:
該擴(kuò)展通過(guò) MYEXT_ENABLED
配置項(xiàng)開(kāi)啟,items 的數(shù)量通過(guò) MYEXT_ITEMCOUNT
配置項(xiàng)設(shè)置。
以下是擴(kuò)展的代碼:
from scrapy import signals
from scrapy.exceptions import NotConfigured
class SpiderOpenCloseLogging(object):
def __init__(self, item_count):
self.item_count = item_count
self.items_scraped = 0
@classmethod
def from_crawler(cls, crawler):
# first check if the extension should be enabled and raise
# NotConfigured otherwise
if not crawler.settings.getbool('MYEXT_ENABLED'):
raise NotConfigured
# get the number of items from settings
item_count = crawler.settings.getint('MYEXT_ITEMCOUNT', 1000)
# instantiate the extension object
ext = cls(item_count)
# connect the extension object to signals
crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)
crawler.signals.connect(ext.item_scraped, signal=signals.item_scraped)
# return the extension object
return ext
def spider_opened(self, spider):
spider.log("opened spider %s" % spider.name)
def spider_closed(self, spider):
spider.log("closed spider %s" % spider.name)
def item_scraped(self, item, spider):
self.items_scraped += 1
if self.items_scraped % self.item_count == 0:
spider.log("scraped %d items" % self.items_scraped)
記錄基本的統(tǒng)計(jì)信息,比如爬取的頁(yè)面和條目(items)。
如果統(tǒng)計(jì)收集器(stats collection)啟用了,該擴(kuò)展開(kāi)啟核心統(tǒng)計(jì)收集(參考數(shù)據(jù)收集(Stats Collection))。
提供一個(gè) telnet 控制臺(tái),用于進(jìn)入當(dāng)前執(zhí)行的 Scrapy 進(jìn)程的 Python 解析器,這對(duì)代碼調(diào)試非常有幫助。
telnet 控制臺(tái)通過(guò) TELNETCONSOLE_ENABLED
配置項(xiàng)開(kāi)啟,服務(wù)器會(huì)監(jiān)聽(tīng) TELNETCONSOLE_PORT
指定的端口。
注解
This extension does not work in Windows.
監(jiān)控 Scrapy 進(jìn)程內(nèi)存使用量,并且:
當(dāng)內(nèi)存用量達(dá)到 MEMUSAGE_WARNING_MB
指定的值,發(fā)送提醒郵件。當(dāng)內(nèi)存用量達(dá)到 MEMUSAGE_LIMIT_MB
指定的值,發(fā)送提醒郵件,同時(shí)關(guān)閉 spider,Scrapy 進(jìn)程退出。
該擴(kuò)展通過(guò) MEMUSAGE_ENABLED 配置項(xiàng)開(kāi)啟,可以使用以下選項(xiàng):
該擴(kuò)展用于調(diào)試內(nèi)存使用量,它收集以下信息:
開(kāi)啟該擴(kuò)展,需打開(kāi) MEMDEBUG_ENABLED
配置項(xiàng)。 信息將會(huì)存儲(chǔ)在統(tǒng)計(jì)信息(stats)中。
當(dāng)某些狀況發(fā)生,spider 會(huì)自動(dòng)關(guān)閉。每種情況使用指定的關(guān)閉原因。
關(guān)閉 spider 的情況可以通過(guò)下面的設(shè)置項(xiàng)配置:
默認(rèn)值: 0
一個(gè)整數(shù)值,單位為秒。如果一個(gè) spider 在指定的秒數(shù)后仍在運(yùn)行, 它將以 closespider_timeout
的原因被自動(dòng)關(guān)閉。如果值設(shè)置為 0(或者沒(méi)有設(shè)置),spiders 不會(huì)因?yàn)槌瑫r(shí)而關(guān)閉。
缺省值: 0
一個(gè)整數(shù)值,指定條目的個(gè)數(shù)。如果 spider 爬取條目數(shù)超過(guò)了指定的數(shù),并且這些條目通過(guò) item pipeline 傳遞,spider 將會(huì)以 closespider_itemcount
的原因被自動(dòng)關(guān)閉。
新版功能。
缺省值: 0
一個(gè)整數(shù)值,指定最大的抓取響應(yīng)(reponses)數(shù)。 如果 spider 抓取數(shù)超過(guò)指定的值,則會(huì)以 closespider_pagecount
的原因自動(dòng)關(guān)閉。 如果設(shè)置為 0(或者未設(shè)置),spiders 不會(huì)因?yàn)樽ト〉捻憫?yīng)數(shù)而關(guān)閉。
新版功能。
缺省值: 0
一個(gè)整數(shù)值,指定 spider 可以接受的最大錯(cuò)誤數(shù)。 如果 spider 生成多于該數(shù)目的錯(cuò)誤,它將以 closespider_errorcount
的原因關(guān)閉。 如果設(shè)置為 0(或者未設(shè)置),spiders 不會(huì)因?yàn)榘l(fā)生錯(cuò)誤過(guò)多而關(guān)閉。
這個(gè)簡(jiǎn)單的擴(kuò)展可用來(lái)在一個(gè)域名爬取完畢時(shí)發(fā)送提醒郵件, 包含 Scrapy 收集的統(tǒng)計(jì)信息。 郵件會(huì)發(fā)送個(gè)通過(guò) STATSMAILER_RCPTS
指定的所有接收人。
當(dāng)收到 SIGQUIT 或 SIGUSR2 信號(hào),spider 進(jìn)程的信息將會(huì)被存儲(chǔ)下來(lái)。存儲(chǔ)的信息包括:
scrapy.utils.engin.get_engine_status()
)當(dāng)堆棧信息和 engine 狀態(tài)存儲(chǔ)后,Scrapy 進(jìn)程繼續(xù)正常運(yùn)行。
該擴(kuò)展只在 POSIX 兼容的平臺(tái)上可運(yùn)行(比如不能在 Windows 運(yùn)行), 因?yàn)?SIGQUIT 和 SIGUSR2 信號(hào)在 Windows 上不可用。
至少有兩種方式可以向 Scrapy 發(fā)送 SIGQUIT 信號(hào):
在 Scrapy 進(jìn)程運(yùn)行時(shí)通過(guò)按 Ctrl-(僅 Linux 可行?)
運(yùn)行該命令(<pid>
是 Scrapy 運(yùn)行的進(jìn)程):
kill -QUIT <pid>
當(dāng)收到 SIGUSR2 信號(hào),將會(huì)在 Scrapy 進(jìn)程中調(diào)用 Python debugger。debugger 退出后,Scrapy 進(jìn)程繼續(xù)正常運(yùn)行。
更多信息參考 Debugging in Python。
該擴(kuò)展只在 POSIX 兼容平臺(tái)上工作(比如不能再 Windows 上運(yùn)行)。