Added reasons when closing domains ('reason' argument to engine close_domain method), replaced old 'status' parameter by new 'reason' parameter in domain_closed signal. updated and improved signals doc

This commit is contained in:
Pablo Hoffman 2009-06-21 22:00:16 -03:00
parent ab2dd764e0
commit 400a54bf7c
8 changed files with 160 additions and 111 deletions

View File

@ -23,131 +23,181 @@ Here's a list of signals used in Scrapy and their meaning, in alphabetical
order.
.. signal:: domain_closed
.. function:: domain_closed(domain, spider, status)
.. function:: domain_closed(domain, spider, reason)
Sent right after a spider/domain has been closed.
Sent right after a spider/domain has been closed.
``domain`` is a string which contains the domain of the spider which has been closed
``spider`` is the spider which has been closed
``status`` is a string which can have two values: ``'finished'`` if the domain
has finished successfully, or ``'cancelled'`` if the domain was cancelled (for
example, by hitting Ctrl-C, by calling the engine ``stop()`` method or by
explicitly closing the domain).
:param domain: a string which contains the domain of the spider which has
been closed
:type domain: str
:param spider: the spider which has been closed
:type spider: :class:`~scrapy.spider.BaseSpider` object
:param reason: a string which describes the reason why the domain was closed. If
it was closed because the domain has completed scraping, it the reason
is ``'finished'``. Otherwise, if the domain was manually closed by
calling the ``close_domain`` engine method, then the reason is the one
passed in the ``reason`` argument of that method (which defaults to
``'cancelled'``). If the engine was shutdown (for example, by hitting
Ctrl-C to stop it) the reason will be ``'shutdown'``.
:type reason: str
.. signal:: domain_open
.. function:: domain_open(domain, spider)
Sent right before a spider has been opened for crawling.
Sent right before a spider has been opened for crawling.
``domain`` is a string which contains the domain of the spider which is about
to be opened
``spider`` is the spider which is about to be opened
:param domain: a string which contains the domain of the spider which is about
to be opened
:type domain: str
:param spider: the spider which is about to be opened
:type spider: :class:`~scrapy.spider.BaseSpider` object
.. signal:: domain_opened
.. function:: domain_opened(domain, spider)
Sent right after a spider has been opened for crawling.
Sent right after a spider has been opened for crawling.
``domain`` is a string with the domain of the spider which has been opened
``spider`` is the spider which has been opened
:param domain: a string with the domain of the spider which has been opened
:type domain: str
:param spider: the spider which has been opened
:type spider: :class:`~scrapy.spider.BaseSpider` object
.. signal:: domain_idle
.. function:: domain_idle(domain, spider)
Sent when a domain has no further:
* requests waiting to be downloaded
* requests scheduled
* items being processed in the item pipeline
Sent when a domain has gone idle, which means the spider has no further:
* requests waiting to be downloaded
* requests scheduled
* items being processed in the item pipeline
``domain`` is a string with the domain of the spider which has gone idle
``spider`` is the spider which has gone idle
:param domain: is a string with the domain of the spider which has gone idle
:type domain: str
If any handler of this signals raises a :exception:`DontCloseDomain` the domain
won't be closed at this time and will wait until another idle signal is sent.
Otherwise (if no handler raises :exception:`DontCloseDomain`) the domain will
be closed immediately after all handlers of ``domain_idle`` have finished, and
a :signal:`domain_closed` will thus be sent.
:param spider: the spider which has gone idle
:type spider: :class:`~scrapy.spider.BaseSpider` object
If any handler of this signal handlers raises a
:exception:`DontCloseDomain` the domain won't be closed this time and will
wait until another idle signal is sent. Otherwise (if no handler raises
:exception:`DontCloseDomain`) the domain will be closed immediately after
all handlers of ``domain_idle`` have finished, and a
:signal:`domain_closed` will thus be sent.
.. signal:: engine_started
.. function:: engine_started()
Sent when the Scrapy engine is started (for example, when a crawling
process has started).
Sent when the Scrapy engine is started (for example, when a crawling
process has started).
.. signal:: engine_stopped
.. function:: engine_stopped()
Sent when the Scrapy engine is stopped (for example, when a crawling
process has finished).
.. signal:: request_received
.. function:: request_received(request, spider, response)
Sent when the engine receives a :class:`~scrapy.http.Request` from a spider.
``request`` is the :class:`~scrapy.http.Request` received
``spider`` is the spider which generated the request
``response`` is the :class:`~scrapy.http.Response` fed to the spider which
generated the request
.. signal:: request_uploaded
.. function:: request_uploaded(request, spider)
Sent right after the download has sent a :class:`~scrapy.http.Request`.
``request`` is the :class:`~scrapy.http.Request` uploaded/sent
``spider`` is the spider which generated the request
.. signal:: response_received
.. function:: response_received(response, spider)
``response`` is the :class:`~scrapy.http.Response` received
``spider`` is the spider for which the response is intended
Sent when the engine receives a new :class:`~scrapy.http.Response` from the
downloader.
.. signal:: response_downloaded
.. function:: response_downloaded(response, spider)
Sent by the downloader right after a ``HTTPResponse`` is downloaded.
``response`` is the ``HTTPResponse`` downloaded
``spider`` is the spider for which the response is intended
Sent when the Scrapy engine is stopped (for example, when a crawling
process has finished).
.. signal:: item_scraped
.. function:: item_scraped(item, spider, response)
Sent when the engine receives a new scraped item from the spider, and right
before the item is sent to the :ref:`topics-item-pipeline`.
Sent when the engine receives a new scraped item from the spider, and right
before the item is sent to the :ref:`topics-item-pipeline`.
``item`` is the item scraped
``spider`` is the spider which scraped the item
``response`` is the :class:`~scrapy.http.Response` from which the item was
scraped
:param item: is the item scraped
:type item: :class:`~scrapy.item.ScrapedItem` object
:param spider: the spider which scraped the item
:type spider: :class:`~scrapy.spider.BaseSpider` object
:param response: the response from which the item was scraped
:type response: :class:`~scrapy.http.Response` object
.. signal:: item_passed
.. function:: item_passed(item, spider, response, pipe_output)
Sent after an item has passed al the :ref:`topics-item-pipeline` stages without
being dropped.
Sent after an item has passed al the :ref:`topics-item-pipeline` stages without
being dropped.
``item`` is the item which passed the pipeline
``spider`` is the spider which scraped the item
``response`` is the :class:`~scrapy.http.Response` from which the item was scraped
``pipe_output`` is the output of the item pipeline. Typically, this points to
the same ``item`` object, unless some pipeline stage created a new item.
:param item: the item which passed the pipeline
:type item: :class:`~scrapy.item.ScrapedItem` object
:param spider: the spider which scraped the item
:type spider: :class:`~scrapy.spider.BaseSpider` object
:param response: the response from which the item was scraped
:type response: :class:`~scrapy.http.Response` object
:param pipe_output: the output of the item pipeline. This is typically the
same :class:`~scrapy.item.ScrapedItem` object received in the ``item``
parameter, unless some pipeline stage created a new item.
.. signal:: item_dropped
.. function:: item_dropped(item, spider, response, exception)
Sent after an item has dropped from the :ref:`topics-item-pipeline` when some stage
raised a :exception:`DropItem` exception.
Sent after an item has dropped from the :ref:`topics-item-pipeline` when some stage
raised a :exception:`DropItem` exception.
``item`` is the item dropped from the :ref:`topics-item-pipeline`
``spider`` is the spider which scraped the item
``response`` is the :class:`~scrapy.http.Response` from which the item was scraped
``exception`` is the (:exception:`DropItem` child) exception that caused the
item to be dropped
:param item: the item dropped from the :ref:`topics-item-pipeline`
:type item: :class:`~scrapy.item.ScrapedItem` object
:param spider: the spider which scraped the item
:type spider: :class:`~scrapy.spider.BaseSpider` object
:param response: the response from which the item was scraped
:type response: :class:`~scrapy.http.Response` object
:param exception: the exception (which must be a :exception:`DropItem`
subclass) which caused the item to be dropped
:type exception: :exception:`DropItem` exception
.. signal:: request_received
.. function:: request_received(request, spider, response)
Sent when the engine receives a :class:`~scrapy.http.Request` from a spider.
:param request: the request received
:type request: :class:`~scrapy.http.Request` object
:param spider: the spider which generated the request
:type spider: :class:`~scrapy.spider.BaseSpider` object
:param response: the :class:`~scrapy.http.Response` fed to the spider which
generated the request later
:type response: :class:`~scrapy.http.Response` object
.. signal:: request_uploaded
.. function:: request_uploaded(request, spider)
Sent right after the download has sent a :class:`~scrapy.http.Request`.
:param request: the request uploaded/sent
:type request: :class:`~scrapy.http.Request` object
:param spider: the spider which generated the request
:type spider: :class:`~scrapy.spider.BaseSpider` object
.. signal:: response_received
.. function:: response_received(response, spider)
:param response: the response received
:type response: :class:`~scrapy.http.Response` object
:param spider: the spider for which the response is intended
:type spider: :class:`~scrapy.spider.BaseSpider` object
Sent when the engine receives a new :class:`~scrapy.http.Response` from the
downloader.
.. signal:: response_downloaded
.. function:: response_downloaded(response, spider)
Sent by the downloader right after a ``HTTPResponse`` is downloaded.
:param response: the response downloaded
:type response: :class:`~scrapy.http.Response` object
:param spider: the spider for which the response is intended
:type spider: :class:`~scrapy.spider.BaseSpider` object

View File

@ -68,8 +68,8 @@ class ItemSamplerPipeline(object):
if self.empty_domains:
log.msg("No products sampled for: %s" % " ".join(self.empty_domains), level=log.WARNING)
def domain_closed(self, domain, spider, status):
if status == 'finished' and not stats.getpath("%s/items_sampled" % domain):
def domain_closed(self, domain, spider, reason):
if reason == 'finished' and not stats.getpath("%s/items_sampled" % domain):
self.empty_domains.add(domain)
self.domains_count += 1
log.msg("Sampled %d domains so far (%d empty)" % (self.domains_count, len(self.empty_domains)), level=log.INFO)

View File

@ -47,6 +47,6 @@ class DNSCache(object):
self._cache[host] = ips
return ips[0]
def domain_closed(self, domain, spider, status):
def domain_closed(self, domain, spider, reason):
if domain in self._cache:
del self._cache[domain]

View File

@ -47,7 +47,7 @@ class ExecutionEngine(object):
def __init__(self):
self.configured = False
self.keep_alive = False
self.cancelled = set() # domains in cancelation state
self.closing = {} # domains being closed
self.debug_mode = settings.getbool('ENGINE_DEBUG')
self.tasks = []
self.ports = []
@ -135,7 +135,7 @@ class ExecutionEngine(object):
self.running = False
for domain in self.open_domains:
spider = spiders.fromdomain(domain)
signals.send_catch_log(signal=signals.domain_closed, sender=self.__class__, domain=domain, spider=spider, status='cancelled')
signals.send_catch_log(signal=signals.domain_closed, sender=self.__class__, domain=domain, spider=spider, reason='shutdown')
for tsk, _, _ in self.tasks: # stop looping calls
if tsk.running:
tsk.stop()
@ -186,7 +186,7 @@ class ExecutionEngine(object):
return
# backout enqueing downloads if domain needs it
if domain in self.cancelled or self.downloader.needs_backout(domain):
if domain in self.closing or self.downloader.needs_backout(domain):
return
# Next pending request from scheduler
@ -235,7 +235,7 @@ class ExecutionEngine(object):
signals.send_catch_log(signal=signals.item_passed, sender=self.__class__, item=item, spider=spider, response=response, pipe_output=pipe_result)
self.next_request(spider)
if domain in self.cancelled:
if domain in self.closing:
return
elif isinstance(output, ScrapedItem):
log.msg("Scraped %s in <%s>" % (output, request.url), log.INFO, domain=domain)
@ -346,7 +346,7 @@ class ExecutionEngine(object):
spider = spider or spiders.fromdomain(domain)
self.next_request(spider)
self.cancelled.discard(domain)
self.closing.pop(domain, None)
self.downloader.open_domain(domain)
self.pipeline.open_domain(domain)
self._scraping[domain] = set()
@ -379,11 +379,11 @@ class ExecutionEngine(object):
if self.is_idle() and not self.keep_alive:
self.stop()
def close_domain(self, domain):
def close_domain(self, domain, reason='cancelled'):
"""Close (cancel) domain and clear all its outstanding requests"""
if domain not in self.cancelled:
log.msg("Closing domain", domain=domain)
self.cancelled.add(domain)
if domain not in self.closing:
log.msg("Closing domain (%s)" % reason, domain=domain)
self.closing[domain] = reason
self._close_domain(domain)
def _close_domain(self, domain):
@ -400,10 +400,10 @@ class ExecutionEngine(object):
self.scheduler.close_domain(domain)
self.pipeline.close_domain(domain)
del self._scraping[domain]
status = 'cancelled' if domain in self.cancelled else 'finished'
signals.send_catch_log(signal=signals.domain_closed, sender=self.__class__, domain=domain, spider=spider, status=status)
log.msg("Domain closed (%s)" % status, domain=domain)
self.cancelled.discard(domain)
reason = self.closing.get(domain, 'finished')
signals.send_catch_log(signal=signals.domain_closed, sender=self.__class__, domain=domain, spider=spider, reason=reason)
log.msg("Domain closed (%s)" % reason, domain=domain)
self.closing.pop(domain, None)
self._mainloop()
def getstatus(self):
@ -426,7 +426,7 @@ class ExecutionEngine(object):
]
domain_tests = [
"self.domain_is_idle(domain)",
"domain in self.cancelled",
"self.closing.get(domain)",
"self.scheduler.domain_has_pending_requests(domain)",
"len(self.scheduler.pending_requests[domain])",
"len(self.downloader.sites[domain].queue)",

View File

@ -30,8 +30,7 @@ domain_opened = object()
domain_idle = object()
# After a domain has been closed
# args: domain, spider, status
# status is a string and its possible values are: "finished" or "cancelled"
# args: domain, spider, reason
domain_closed = object()
# New request received from spiders

View File

@ -32,10 +32,10 @@ class CoreStats(object):
stats.setpath('%s/envinfo' % domain, stats.getpath('_envinfo'))
stats.incpath('_global/domain_count/opened')
def stats_domain_closing(self, domain, spider, status):
def stats_domain_closing(self, domain, spider, reason):
stats.setpath('%s/finish_time' % domain, datetime.datetime.now())
stats.setpath('%s/finish_status' % domain, 'OK' if status == 'finished' else status)
stats.incpath('_global/domain_count/%s' % status)
stats.setpath('%s/finish_status' % domain, 'OK' if reason == 'finished' else reason)
stats.incpath('_global/domain_count/%s' % reason)
def item_scraped(self, item, spider):
stats.incpath('%s/item_scraped_count' % spider.domain_name)

View File

@ -66,12 +66,12 @@ class StatsCollector(dict):
def _domain_open(self, domain, spider):
dispatcher.send(signal=self.domain_open, sender=self.__class__, domain=domain, spider=spider)
def _domain_closed(self, domain, spider, status):
dispatcher.send(signal=self.domain_closing, sender=self.__class__, domain=domain, spider=spider, status=status)
def _domain_closed(self, domain, spider, reason):
dispatcher.send(signal=self.domain_closing, sender=self.__class__, domain=domain, spider=spider, reason=reason)
if self.debug:
log.msg(pprint.pformat(self[domain]), domain=domain, level=log.DEBUG)
if self.db:
self.db.put(domain, self[domain])
if self.cleanup:
del self[domain]
dispatcher.send(signal=self.domain_closed, sender=self.__class__, domain=domain, spider=spider, status=status)
dispatcher.send(signal=self.domain_closed, sender=self.__class__, domain=domain, spider=spider, reason=reason)

View File

@ -186,7 +186,7 @@ class EngineTest(unittest.TestCase):
session.signals_catched[signals.domain_opened])
self.assertEqual({'domain': session.domain, 'spider': session.spider},
session.signals_catched[signals.domain_idle])
self.assertEqual({'domain': session.domain, 'spider': session.spider, 'status': 'finished'},
self.assertEqual({'domain': session.domain, 'spider': session.spider, 'reason': 'finished'},
session.signals_catched[signals.domain_closed])
if __name__ == "__main__":