2015-10-15 54 views
0

我需要使用Scrapy报废每个项目的数据(http://example.com/itemview)。我有一个itemID列表,我需要通过example.com中的表单传递它。 每个项目都没有网址更改。因此,对于我的蜘蛛中的每个请求,url将始终是相同的。但内容会有所不同。如何在调用item_scraped scrapy信号后开始新请求?

我不想为处理每个请求的for循环。所以我按照下面提到的步骤。

  • 开始蜘蛛与上述URL
  • 加入item_scraped和spider_closed信号
  • 通过几个功能传递
  • 通过刮下数据到管道
  • trigerred的item_scraped信号

之后它会自动调用spider_closed信号。但是我想要继续上述步骤直到完成itemID。

class ExampleSpider(scrapy.Spider): 
    name = "example" 
    allowed_domains = ["example.com"] 
    itemIDs = [11111,22222,33333] 
    current_item_num = 0 

    def __init__(self, itemids=None, *args, **kwargs): 
    super(ExampleSpider, self).__init__(*args, **kwargs) 
    dispatcher.connect(self.item_scraped, signals.item_scraped) 
    dispatcher.connect(self.spider_closed, signals.spider_closed) 

    def spider_closed(self, spider): 
    self.driver.quit() 

    def start_requests(self): 
    request = self.make_requests_from_url('http://example.com/itemview') 
    yield request 

def parse(self,response): 
    self.driver = webdriver.PhantomJS() 
    self.driver.get(response.url) 
    first_data = self.driver.find_element_by_xpath('//div[@id="itemview"]').text.strip() 
    yield Request(response.url,meta={'first_data':first_data},callback=self.processDetails,dont_filter=True) 

def processDetails(self,response): 
    itemID = self.itemIDs[self.current_item_num] 
    ..form submission with the current itemID goes here... 
    ...the content of the page is updated with the given itemID... 
    yield Request(response.url,meta={'first_data':response.meta['first_data']},callback=self.processData,dont_filter=True) 

def processData(self,response): 
    ...some more scraping goes here... 
    item = ExamplecrawlerItem() 
    item['first_data'] = response.meta['first_data'] 
    yield item 

def item_scraped(self,item,response,spider): 
    self.current_item_num += 1 
    #i need to call the processDetails function here for the next itemID 
    #and the process needs to contine till the itemID finishes 
    self.parse(response) 

我piepline:

class ExampleDBPipeline(object): 
    def process_item(self, item, spider): 
    MYCOLLECTION.insert(dict(item)) 
    return 

回答

0

我希望我有一个优雅的解决了这一点。但相反,这是一种调用底层类的方式。

   self.crawler.engine.slot.scheduler.enqueue_request(scrapy.Request(url,self.yourCallBack)) 

但是,您可以在产生该项目并让它回调到self.processDetails后产生请求。只需添加到您的过程数据功能:

yield item 
    self.counter += 1 
    yield scrapy.Request(response.url,callback=self.processDetails,dont_filter=True, meta = {"your":"Dictionary"} 

此外,PhantomJS可真好,让您的生活更轻松,但它比常规的连接要慢得多。如果可能的话,找到对json数据的请求或任何使页面在没有JS的情况下无法解析的请求。为此,打开chrome,右键单击,单击检查,转到网络选项卡,然后在表单中输入ID,然后查看XHR或JS选项卡,查找包含所需数据或下一个网址的JSON。大多数情况下,通过添加ID会产生一些URL,如果你能找到它,你可以直接连接你的url并直接调用它,而不需要JS渲染的代价。有时它是随机的,或不在那里,但我已经取得了公平的成功。然后,您可以同时使用它来产生很多请求,而不必担心phantomJS会一次尝试做两件事,或者不得不初始化它的许多实例。你可以使用标签,但这是一个痛苦。

此外,我会使用您的ID队列,以确保线程安全。否则,你可以在同一个ID上调用两次processDetails,尽管在你的程序的逻辑中,一切似乎都是线性的,这意味着你没有使用Scrapy的并发能力,你的程序会变得更慢。要使用Queue,请添加:

import Queue 
#go inside class definition and add 
itemIDQueue = Queue.Queue() 
#within __init__ add 
[self.itemIDQueue.put(ID) for ID in self.itemID] 
#within processDetails replace itemID = self.itemIDs[self.current_item_num] with 
itemID = self.itemIDQueue.get() 

然后不需要增加计数器,并且程序是线程安全的。