如何在Python中完成递归异步作业后调用函数?

时间:2016-05-10 14:46:00

标签: python-2.7 recursion web-scraping scrapy web-crawler

我使用scrapy来抓取这个site

我想保存数组中的所有子类别,然后获取相应的页面(分页)

我有第一步

def start_requests(self):
        yield Request(start_urls[i], callback=self.get_sous_cat)

get_sous_cat是一个获取网站所有子类别的函数,然后以异步方式启动作业,以递归方式探索子子类别。

   def get_sous_cat(self,response):
    #Put all the categgories in a array
    catList = response.css('div.categoryRefinementsSection')
    if (catList):
        for category in catList.css('a::attr(href)').extract():
            category = 'https://www.amazon.fr' + category
            print category
            self.arrayCategories.append(category)
            yield Request(category, callback=self.get_sous_cat)

当发送了所有相应的请求后,我需要调用此终止函数:

def pagination(self,response):
    for i in range(0, len(self.arrayCategories[i])):
        #DO something with each sub-category

我试过了

def start_requests(self):

    yield Request(start_urls[i], callback=self.get_sous_cat)

    for subCat in range(0,len(self.arrayCategories)):
       yield Request(self.arrayCategories[subCat], callback=self.pagination)

2 个答案:

答案 0 :(得分:3)

干得好,这是一个很好的问题!两件小事:

a)使用集合而不是数组。这样你就不会有重复 b)场地结构将每月/每年更换一次。您可能会更频繁地抓取。把蜘蛛分成两个; 1.创建类别URL列表并按月运行的那个和2.以start_urls的形式获取由第一个生成的文件

现在,如果您真的想按照现在的方式进行操作,请挂钩spider_idle信号(请参阅此处:Scrapy: How to manually insert a request from a spider_idle event callback?)。当没有其他网址可以执行时,会调用此方法,并允许您注入更多内容。设置一个标志或重置你的列表,以便蜘蛛第二次空闲(在爬行所有东西之后),它不会重新注入相同的类别网址。

如果在您的情况下,您似乎不希望对网址进行一些奇特的处理,而只是在其他网址之前抓取类别,则这就是请求优先级属性(http://doc.scrapy.org/en/latest/topics/request-response.html#topics-request-response-ref-request-subclasses)。只需将其设置为例如1为您的类别URL,然后它将在处理任何非类别链接之前遵循这些链接。这样做效率更高,因为它不会像当前实现那样加载那些类别页面两次。

答案 1 :(得分:1)

这不是"递归"它是异步作业。你需要的是一个全局计数器(受一个锁保护),如果为0,你可以完成:

from threading import Lock

class JobCounter(object):
   def __init__(self, completion_callback, *args, **kwargs):
      self.c = 0
      self.l = Lock()
      self.completion = (completion_callback, args, kwargs)
   def __iadd__(self, n):
      b = false
      with self.l:
         self.c += n
         if self.c <= 0:
            b = true
      if b:
         f, args, kwargs = self.completion
         f(*args, **kwargs)
   def __isub__(self, n):
      self.__iadd__(-n)

每次启动工作时,请执行counter += 1

每次工作完成后,执行counter -= 1

注意:这会在最后一个调用作业的线程中完成。如果您想在特定线程中执行此操作,请使用Condition而不是Lock,并执行notify()而不是调用。