2015-10-15 30 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() 

然後不需要增加計數器,並且程序是線程安全的。

相關問題