目录
数据收集(Stats Collection)
Scrapy提供了方便的收集数据的机制。数据以key/value方式存储,值大多是计数值。 该机制叫做数据收集器(Stats Collector),可以通过 Crawler API 的属性 stats 来使用。在下面的章节 常见数据收集器使用方法 将给出例子来说明。
无论数据收集(stats collection)开启或者关闭,数据收集器永远都是可用的。 因此您可以import进自己的模块并使用其API(增加值或者设置新的状态键(stat keys))。 该做法是为了简化数据收集的方法: 您不应该使用超过一行代码来收集您的spider,Scrpay扩展或任何您使用数据收集器代码里头的状态。
数据收集器的另一个特性是(在启用状态下)很高效,(在关闭情况下)非常高效(几乎察觉不到)。
数据收集器对每个spider保持一个状态表。当spider启动时,该表自动打开,当spider关闭时,自动关闭。
这个东西大部分用在计数上,比如成功多少次,失败多少次,收集了多少item之类。
常见数据收集器使用方法
通过 stats 属性来使用数据收集器。 下面是在扩展中使用状态的例子:
1 2 3 4 5 6 7 8 |
class ExtensionThatAccessStats(object): def __init__(self, stats): self.stats = stats @classmethod def from_crawler(cls, crawler): return cls(crawler.stats) |
设置数据:
1 |
stats.set_value('hostname', socket.gethostname()) |
增加数据值:
1 |
stats.inc_value('pages_crawled') |
当新的值比原来的值大时设置数据:
1 |
stats.max_value('max_items_scraped', value) |
当新的值比原来的值小时设置数据:
1 |
stats.min_value('min_free_memory_percent', value) |
获取数据:
1 2 |
>>> stats.get_value('pages_crawled') 8 |
获取所有数据:
1 2 |
>>> stats.get_stats() {'pages_crawled': 1238, 'start_time': datetime.datetime(2009, 7, 14, 21, 47, 28, 977139)} |
可用的数据收集器
除了基本的 StatsCollector
,Scrapy 也提供了基于 StatsCollector
的数据收集器。 您可以通过 STATS_CLASS
设置来选择。默认使用的是 MemoryStatsCollector
。
MemoryStatsCollector
class scrapy.statscol.MemoryStatsCollector
一个简单的数据收集器。其在 spider 运行完毕后将其数据保存在内存中。数据可以通过 spider_stats 属性访问。该属性是一个以 spider 名字为键(key)的字典。
这是 Scrapy 的默认选择。
spider_stats
保存了每个 spider 最近一次爬取的状态的字典(dict)。该字典以 spider 名字为键,值也是字典。
DummyStatsCollector
class scrapy.statscol.DummyStatsCollector
该数据收集器并不做任何事情但非常高效。您可以通过设置 STATS_CLASS 启用这个收集器,来关闭数据收集,提高效率。 不过,数据收集的性能负担相较于 Scrapy 其他的处理(例如分析页面)来说是非常小的。
使用实例,统计处理404页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class JobboleSpider(scrapy.Spider): name = "jobbole" allowed_domains = ["blog.jobbole.com"] start_urls = ['http://blog.jobbole.com/all-posts/'] handle_httpstatus_list = [404] def __init__(self, **kwargs): self.fail_urls = [] def parse(self, response): if response.status == 404: self.fail_urls.append(response.url) self.crawler.stats.inc_value("failed_url") # 可能会影响统计因素 # SPIDER_MIDDLEWARES = { # 'ArticleSpider.middlewares.ArticlespiderSpiderMiddleware': 543, # } |
信号(Signals)
Scrapy 使用信号来通知事情发生。您可以在您的 Scrapy 项目中捕捉一些信号(使用 extension)来完成额外的工作或添加额外的功能,扩展 Scrapy。
虽然信号提供了一些参数,不过处理函数不用接收所有的参数 – 信号分发机制(singal dispatching mechanism)仅仅提供处理器(handler)接受的参数。
您可以通过信号(Signals) API 来连接(或发送您自己的)信号。
延迟的信号处理器(Deferred signal handlers)
有些信号支持从处理器返回 Twisted deferreds,参考下边的内置信号参考手册(Built-in signals reference)
来了解哪些支持。
内置信号参考手册(Built-in signals reference)
以下给出 Scrapy 内置信号的列表及其意义。
engine_started
scrapy.signals.engine_started()
当 Scrapy 引擎启动爬取时发送该信号。
该信号支持返回 deferreds。
注解
该信号可能会在信号
spider_opened
之后被发送,取决于 spider 的启动方式。 所以不要 依赖 该信号会比spider-opened
更早被发送。
engine_stopped
scrapy.signals.engine_stopped()
当 Scrapy 引擎停止时发送该信号(例如,爬取结束)。
该信号支持返回 deferreds。
item_scraped
scrapy.signals.item_scraped(item, response, spider)
当 item 被爬取,并通过所有 Item Pipeline 后(没有被丢弃(dropped),发送该信号。
该信号支持返回 deferreds。
参数:
- item (
Item
对象) – 爬取到的 item - spider (
Spider
对象) – 爬取 item 的 spider - response (
Response
对象) – 提取 item 的 response
item_dropped
scrapy.signals.item_dropped(item, exception, spider)
当 item 通过 Item Pipeline,有些 pipeline 抛出 DropItem 异常,丢弃 item 时,该信号被发送。
该信号支持返回 deferreds。
参数:
- item (
Item
对象) – 爬取到的 item - spider (
Spider
对象) – 爬取 item 的 spider - exception (
DropItem
异常) – 导致 item 被丢弃的异常(必须是 DropItem 的子类)
spider_closed
scrapy.signals.spider_closed(spider, reason)
当某个 spider 被关闭时,该信号被发送。该信号可以用来释放每个 spider 在 spider_opened
时占用的资源。
该信号支持返回 deferreds。
参数:
- spider (
Spider
对象) – 关闭的 spider - reason (str) – 描述 spider 被关闭的原因的字符串。如果 spider 是由于完成爬取而被关闭,则其为
'finished'
。否则,如果 spider 是被引擎的close_spider
方法所关闭,则其为调用该方法时传入的reason
参数(默认为'cancelled'
)。如果引擎被关闭(例如, 输入 Ctrl-C),则其为'shutdown'
。
spider_opened
scrapy.signals.spider_opened(spider)
当 spider 开始爬取时发送该信号。该信号一般用来分配 spider 的资源,不过其也能做任何事。
该信号支持返回 deferreds。
参数:
- spider (
Spider 对象
) – 开启的 spider
spider_idle
scrapy.signals.spider_idle(spider)
当 spider 进入空闲(idle)状态时该信号被发送。空闲意味着:
- requests 正在等待被下载
- requests 被调度
- items 正在 item pipeline 中被处理
当该信号的所有处理器(handler)被调用后,如果 spider 仍然保持空闲状态,引擎将会关闭该 spider。当 spider 被关闭后,spider_close
d 信号将被发送。
您可以,比如,在 spider_idle
处理器中调度某些请求来避免 spider 被关闭。
该信号不支持返回 deferreds。
参数:
- spider (
Spider
对象) – 空闲的 spider
spider_error
scrapy.signals.spider_error(failure, response, spider)
当 spider 的回调函数产生错误时(例如,抛出异常),该信号被发送。
参数:
- failure (Failure 对象) – 以 Twisted Failure 对象抛出的异常
- response (
Response 对象
) – 当异常被抛出时被处理的 response - spider (
Spider 对象
) – 抛出异常的 spider
request_scheduled
scrapy.signals.request_scheduled(request, spider)
当引擎调度一个 Request 对象用于下载时,该信号被发送。
该信号 不支持 返回 deferreds。
参数:
- request (
Request
对象) – 到达调度器的 request - spider (
Spider
对象) – 产生该 request 的 spider
request_dropped
scrapy.signals.request_dropped(request, spider)
当调度器拒绝稍后将要下载的请求时发送。
该信号 不支持 返回deferreds。
参数:
- request (
Request
object) – the request that reached the scheduler - spider (
Spider
object) – the spider that yielded the request
response_received
scrapy.signals.response_received(response, request, spider)
当引擎从 downloader 获取到一个新的 Response 时发送该信号。
该信号 不支持 返回 deferreds。
参数:
- response (
Response
对象) – 接收到的 response - request (
Request
对象) – 生成 response 的 request - spider (
Spider 对象
) – response 所对应的 spider
response_downloaded
scrapy.signals.response_downloaded(response, request, spider)
当一个 HTTPResponse 被下载时,由 downloader 发送该信号。
该信号 不支持 返回 deferreds。
参数:
- response (
Response
对象) – \下载的 response - request (
Request
对象) – 生成 response 的 request - spider (
Spider 对象
) – response 所对应的 spider
示例
我将这段代码放到中间件里,本来应该是,再与setting同级的目录下创建一个py文件,然后来写的。
然后再在setting的 EXTENSIONS 里启用并给出等级,就像piplin和middleware一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
class QianlongwangSpiderMiddleware(object): # Not all methods need to be defined. If a method is not defined, # scrapy acts as if the spider middleware does not modify the # passed objects. @classmethod def from_crawler(cls, crawler): # This method is used by Scrapy to create your spiders. s = cls() crawler.signals.connect(s.spider_opened, signal=signals.spider_opened) crawler.signals.connect(s.item_scraped, signal=signals.item_scraped) crawler.signals.connect(s.spider_closed, signal=signals.spider_closed) crawler.signals.connect(s.spider_error, signal=signals.spider_error) crawler.signals.connect(s.spider_idle, signal=signals.spider_idle) return s # 当spider开始爬取时发送该信号。该信号一般用来分配spider的资源,不过其也能做任何事。 def spider_opened(self, spider): spider.logger.info('pa chong kai shi le: %s' % spider.name) print('start','1') def item_scraped(self,item, response, spider): global hahaha hahaha += 1 # 当某个spider被关闭时,该信号被发送。该信号可以用来释放每个spider在 spider_opened 时占用的资源。 def spider_closed(self,spider, reason): print('-------------------------------all over------------------------------------------') global hahaha prnit(spider.name,' closed') # 当spider的回调函数产生错误时(例如,抛出异常),该信号被发送。 def spider_error(self,failure, response, spider): code = response.status print('spider error') # 当spider进入空闲(idle)状态时该信号被发送。空闲意味着: # requests正在等待被下载 # requests被调度 # items正在item pipeline中被处理 def spider_idle(self,spider): for i in range(10): print(spider.name) |
扩展(Extensions)
扩展框架提供一个机制,使得你能将自定义功能绑定到 Scrapy。
扩展只是正常的类,它们在 Scrapy 启动时被实例化、初始化。
扩展设置(Extension settings)
扩展使用 Scrapy settings 管理它们的设置,这跟其他 Scrapy 代码一样。
通常扩展需要给它们的设置加上前缀,以避免跟已有(或将来)的扩展冲突。 比如,一个扩展处理 Google Sitemaps,则可以使用类似 GOOGLESITEMAP_ENABLED、GOOGLESITEMAP_DEPTH 等设置。
加载和激活扩展
扩展在扩展类被实例化时加载和激活。 因此,所有扩展的实例化代码必须在类的构造函数(__init__
)中执行。
要使得扩展可用,需要把它添加到 Scrapy 的 EXTENSIONS
配置中。在 EXTENSIONS
中,每个扩展都使用一个字符串表示,即扩展类的全 Python 路径。比如:
1 2 3 4 |
EXTENSIONS = { 'scrapy.contrib.corestats.CoreStats': 500, 'scrapy.telnet.TelnetConsole': 500, } |
如你所见,EXTENSIONS
配置是一个 dict,key 是扩展类的路径,value 是顺序,它定义扩展加载的顺序。扩展顺序不像中间件的顺序那么重要,而且扩展之间一般没有关联。扩展加载的顺序并不重要,因为它们并不相互依赖。
如果你需要添加扩展而且它依赖别的扩展,你就可以使用该特性了。
这也是为什么 Scrapy 的配置项 EXTENSIONS_BASE
(它包括了所有内置且开启的扩展)定义所有扩展的顺序都相同(500
)。
可用的(Available)、开启的(enabled)和禁用的(disabled)的扩展
并不是所有可用的扩展都会被开启。一些扩展经常依赖一些特别的配置。 比如,HTTP Cache 扩展是可用的但默认是禁用的,除非 HTTPCACHE_ENABLED
配置项设置了。
禁用扩展(Disabling an extension)
为了禁用一个默认开启的扩展(比如,包含在 EXTENSIONS_BASE
中的扩展),需要将其顺序(order)设置为 None
。比如:
1 2 3 |
EXTENSIONS = { 'scrapy.contrib.corestats.CoreStats': None, } |
实现你的扩展
实现你的扩展很简单。每个扩展是一个单一的 Python class,它不需要实现任何特殊的方法。
Scrapy 扩展(包括 middlewares 和 pipelines)的主要入口是 from_crawler
类方法,它接收一个 Crawler
类的实例,该实例是控制 Scrapy crawler 的主要对象。如果扩展需要,你可以通过这个对象访问 settings,signals,stats,控制爬虫的行为。
通常来说,扩展关联到 signals 并执行它们触发的任务。
最后,如果 from_crawler
方法抛出 NotConfigured
异常,扩展会被禁用。否则,扩展会被开启。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class MyCustomStatsExtension(object): """ 这个extension专门用来定期搜集一次stats """ def __init__(self, stats): self.stats = stats self.time = 60.0 @classmethod def from_crawler(cls, crawler, *args, **kwargs): instance = cls(crawler.stats) crawler.signals.connect(instance.spider_opened, signal=signals.spider_opened) crawler.signals.connect(instance.spider_closed, signal=signals.spider_closed) return instance def spider_opened(self): self.tsk = task.LoopingCall(self.collect) self.tsk.start(self.time, now=True) def spider_closed(self): if self.tsk.running: self.tsk.stop() def collect(self): #这里收集stats并写入相关的储存。 #目前展示是输出到终端 print u'将展示收集到的数据' print self.stats.get_stats() |
解释
Scrapy API的主要入口是 Crawler 的实例对象, 通过类方法 from_crawler 将它传递给扩展(extensions),详细解释参见这里。
所以第一步通过from_crawler
类方法获取到Crawler对象。
1 2 3 4 5 6 |
@classmethod def from_crawler(cls, crawler, *args, **kwargs): instance = cls(crawler.stats) crawler.signals.connect(instance.spider_opened, signal=signals.spider_opened) crawler.signals.connect(instance.spider_closed, signal=signals.spider_closed) return instance |
- 我的理解是当项目启动后就开启了一个抓取的行为这个行为通过Crawler对象来管理,表现为对spider的控制和状态指示。
之后通过from_crawler
类方法获取的crawler注册信号处理方法:
1 2 |
crawler.signals.connect(instance.spider_opened, signal=signals.spider_opened) crawler.signals.connect(instance.spider_closed, signal=signals.spider_closed) |
这里对应的信号spider_opened
注册为instance
的spider_opened
方法,信号spider_closed
注册为instance
的spider_closed
方法。
- spider_opened 信号在爬虫开启时由spider发送。
- spider_closed 信号在爬虫结束时由spider发送。
而 instance 是通过instance = cls(crawler.stats)
实例化的本扩展的一个实例。作用在于将crawler.stats
传递给本扩展,并暴露自己的方法用于crawler信号的注册。
这样启动项目后spider发送spider_opened
信号,本扩展会接收到这个信号执行绑定的spider_opened
方法:
1 2 3 |
def spider_opened(self): self.tsk = task.LoopingCall(self.collect) self.tsk.start(self.time, now=True) |
通过这个方法打开一个定时任务,间隔60秒执行一次本扩展的collect方法打印spider的状态:
1 2 3 4 5 |
def collect(self): #这里收集stats并写入相关的储存。 #目前展示是输出到终端 print u'将展示收集到的数据' print self.stats.get_stats() |
同理,spider关闭后,扩展接收到spider_closed信号,执行本扩展的spider_closed方法关闭这个定时任务。
1 2 3 |
def spider_closed(self): if self.tsk.running: self.tsk.stop() |