2014-12-27 2 views
2

누구나 scrapy 호출을 호출하고 요청의 콜백 함수 결과를 처리하는 방법을 설명 할 수 있습니까?요청의 콜백 함수가 어떻게 처리됩니까?

본인은 치료가 Object (Request, BaseItem, None) 또는 Iterable의 결과를 허용 할 수 있음을 이해합니다. 예를 들면 :

1. 객체 반환 (요청 또는 BASEITEM 또는 없음) 객체의

def parse(self, response): 
    ... 
    return scrapy.Request(...) 

2. 반환의 Iterable

def parse(self, response): 
    ... 
    for url in self.urls: 
     yield scrapy.Request(...) 

나는 그들이 같이 처리됩니다 생각하고 있어요 이것은 치료의 코드 어딘가에 있습니다.

# Assumed process_callback_result is a function that called after 
# a Request's callback function has been executed. 
# The "result" parameter is the callback's returned value 

def process_callback_result(self, result): 

    if isinstance(result, scrapy.Request): 
     self.process_request(result) 

    elif isinstance(result, scrapy.BaseItem): 
     self.process_item(result) 

    elif result is None: 
     pass 

    elif isinstance(result, collections.Iterable): 
     for obj in result: 
      self.process_callback_result(obj) 
    else: 
     # show error message 
     # ... 

나는 _process_spidermw_output 기능에 <PYTHON_HOME>/Lib/site-packages/scrapy/core/scraper.py에서 해당 코드를 발견 :

def _process_spidermw_output(self, output, request, response, spider): 
    """Process each Request/Item (given in the output parameter) returned 
    from the given spider 
    """ 
    if isinstance(output, Request): 
     self.crawler.engine.crawl(request=output, spider=spider) 
    elif isinstance(output, BaseItem): 
     self.slot.itemproc_size += 1 
     dfd = self.itemproc.process_item(output, spider) 
     dfd.addBoth(self._itemproc_finished, output, response, spider) 
     return dfd 
    elif output is None: 
     pass 
    else: 
     typename = type(output).__name__ 
     log.msg(format='Spider must return Request, BaseItem or None, ' 
         'got %(typename)r in %(request)s', 
       level=log.ERROR, spider=spider, request=request, typename=typename) 

하지만 elif isinstance(result, collections.Iterable): 로직의 일부를 찾을 수 없습니다.

답변

6

_process_spidermw_output은 단일 항목/개체의 처리기이기 때문에 그럴 것입니다. scrapy.utils.defer.parallel에서 호출됩니다. 이 거미의 출력을 처리하는 기능입니다 :

소스
def handle_spider_output(self, result, request, response, spider): 
     if not result: 
      return defer_succeed(None) 
     it = iter_errback(result, self.handle_spider_error, request, response, spider) 
     dfd = parallel(it, self.concurrent_items, 
      self._process_spidermw_output, request, response, spider) 
     return dfd 

: 당신이 볼 수 있듯이 https://github.com/scrapy/scrapy/blob/master/scrapy/core/scraper.py#L163-L169

, 그것은 parallel를 호출하고 인수로 그것을 _process_spidermw_output 기능에 대한 핸들을 제공합니다. 인수 이름은 callable이며 거미 결과가 포함 된 iterable의 각 요소에 대해 호출됩니다. parallel 기능은 다음과 같습니다

def parallel(iterable, count, callable, *args, **named): 
    """Execute a callable over the objects in the given iterable, in parallel, 
    using no more than ``count`` concurrent calls. 
    Taken from: http://jcalderone.livejournal.com/24285.html 
    """ 
    coop = task.Cooperator() 
    work = (callable(elem, *args, **named) for elem in iterable) 
    return defer.DeferredList([coop.coiterate(work) for i in xrange(count)]) 

출처 : https://github.com/scrapy/scrapy/blob/master/scrapy/utils/defer.py#L50-L58

기본적으로, 프로세스는 다음과 같이 진행됩니다 enqueue_scrape가 호출 될 때
, 그것은 slot.add_response_request를 호출하여 slot.queuerequestresponse을 추가합니다. 그런 다음 queue_scrape_next으로 처리되며 self._scrape을 호출합니다. _scrape 함수는 handle_spider_output을 반복자의 항목을 처리하는 콜백 함수로 정의합니다. 마침내

def iterate_spider_output(result): 
    return [result] if isinstance(result, BaseItem) else arg_to_iter(result) 

, 실제로 반복자에 단일 항목 없음 또는 반복자로 변환하는 기능을 : 반복자는 한 지점에서이 scrapy.utils.spider.iterate_spider_output에 콜백을 등록하는 기능 call_spider를 호출 할 때 _scrape2가 호출 될 때 생성된다 (DEF가 구문 분석 예) 항상의 Iterable로 설정되어 콜백 함수에서 반환 된 값 (요청, BASEITEM)는

def arg_to_iter(arg): 
    """Convert an argument to an iterable. The argument can be a None, single 
    value, or an iterable. 
    Exception: if arg is a dict, [arg] will be returned 
    """ 
    if arg is None: 
     return [] 
    elif not isinstance(arg, _ITERABLE_SINGLE_VALUES) and hasattr(arg, '__iter__'): 
     return arg 
    else: 
     return [arg] 
+0

그래서 의미 다음 scrapy.utils.misc.arg_to_iter()은? Request/BaseItem을 Iterable로 변환하는 코드는 어디에 있습니까? 'handle_spider_output'에서'_scrape' 함수를 추적 할 수만 있었지만 내부의 코드는 실제로 이해할 수 없습니다. – null

+1

내 대답을 더 자세히 업데이트했습니다. – bosnjak

+0

놀라운! 놀라운 대답! 내가 2 번 upvote 할 수 있으면 좋겠다. – null

관련 문제