2016-09-29 220 views
2

我有一個大型的相關URL文件,我想用Scrapy來抓取,並且我已經編寫了一些代碼逐行讀取這個文件併爲我的蜘蛛構建請求進行解析。以下是一些示例代碼。如何在start_requests方法完成之前讓Scrapy執行回調?

蜘蛛:

def start_requests(self): 
    with open(self._file) as infile: 
     for line in infile: 
      inlist = line.replace("\n","").split(",") 
      item = MyItem(data = inlist[0]) 

      request = scrapy.Request(
       url = "http://foo.org/{0}".format(item["data"]), 
       callback = self.parse_some_page 
      ) 
      request.meta["item"] 
      yield request 


def parse_some_page(self,response): 
    ... 
    request = scrapy.Request(
     url = "http://foo.org/bar", 
     callback = self.parse_some_page2 
    ) 
    yield request 

這工作得很好,但有一個大的輸入文件,我看到的是parse_some_page2不調用直到start_requests完成產生所有初始請求。有什麼方法可以讓Scrapy開始調用回調嗎?最終,我不想等待一百萬個請求,然後纔開始看到項目在流水線中流動。

+0

我可能是錯的,但我認爲這種情況發生是因爲你在''parse_some_page()''中產生了''result''。當你擺脫收益時會發生什麼? –

+0

@ notorious.no我不確定如果我選擇省略「yield」,該如何處理新的請求。 – Ci3

回答

2

我想出了2個解決方案。 1)如果大型站點太多,請在單獨的進程中運行spider。 2)通過扭曲使用延期和回調(請不要跑掉,這不會太可怕)。我將討論如何使用第二種方法,因爲第一種方法可以簡單地用Google搜索。

執行yield request的每個函數都會「阻塞」,直到結果可用。因此,您的parse_some_page()函數會生成scrapy響應對象,並且在返回響應之前不會繼續下一個URL。我設法找到了一些需要一段時間才能獲取的網站(主要是外國政府網站),並希望它能模擬出您遇到的類似情況。這裏是一個快速和簡單的例子:

# spider/stackoverflow_spider.py 

from twisted.internet import defer 
import scrapy 

class StackOverflow(scrapy.Spider): 

    name = 'stackoverflow' 

    def start_requests(self): 
     urls = [ 
      'http://www.gob.cl/en/', 
      'http://www.thaigov.go.th/en.html', 
      'https://www.yahoo.com', 
      'https://www.stackoverflow.com', 
      'https://swapi.co/', 
     ] 

     for index, url in enumerate(urls): 
      # create callback chain after a response is returned 
      deferred = defer.Deferred() 
      deferred.addCallback(self.parse_some_page) 
      deferred.addCallback(self.write_to_disk, url=url, filenumber=index+1) 
      # add callbacks and errorbacks as needed 

      yield scrapy.Request(
       url=url, 
       callback=deferred.callback)  # this func will start the callback chain AFTER a response is returned 

    def parse_some_page(self, response): 
     print('[1] Parsing %s' % (response.url)) 
     return response.body # this will be passed to the next callback 

    def write_to_disk(self, content, url, filenumber): 
     print('[2] Writing %s content to disk' % (url)) 
     filename = '%d.html' % filenumber 
     with open(filename, 'wb') as f: 
      f.write(content) 
     # return what you want to pass to the next callback function 
     # or raise an error and start Errbacks chain 

我已經改變了稍微有點更容易閱讀和運行。在start_requests()中首先要注意的是對象被創建並且回調函數在urls循環內被鏈接(通過addCallback())。現在來看看callback參數scrapy.Request

yield scrapy.Request(
    url=url, 
    callback=deferred.callback) 

什麼這個片段將要做的就是開始scrapy.Response變爲可用從請求後,立即回調鏈。在Twisted中,Deferreds只有在執行Deferred.callback(result)後纔開始運行回調鏈。

提供響應後,parse_some_page()函數將以Response作爲參數運行。你要做的是從這裏提取你需要的東西,並將它傳遞給下一個回調(即我的例子中的write_to_disk())。如有必要,您可以在循環中向Deferred添加更多回調。

所以這個答案和你最初做什麼之間的區別是,你使用yield先等待所有的響應,然後執行回調。在我的方法中,使用Deferred.callback()作爲每個請求的回調,以便每個響應都能立即處理。

希望這有助於(和/或工作)。

參考

PS

我不知道這是否會真正爲你工作,因爲我無法找到一個網站,過大解析。另外,我是Scrapy:D的全新品牌Spankin,但我的腰帶上有多年的Twisted。

+0

感謝您的回覆!我會嘗試一下,閱讀一些東西,然後回覆你。 – Ci3

相關問題