2016-09-29 2 views
2

私はScrapyでスクラップしたい相対URLの大きなファイルを持っています。このファイルを1行ずつ読み込み、スパイダーに解析要求を作成するコードを記述しました。以下にいくつかのサンプルコードを示します。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がコールバックを先に呼び出すようにする方法はありますか?最終的には、アイテムがパイプラインを流れるのを見る前に、100万回のリクエストを待つことは望ましくありません。

+0

私は間違っているかもしれませんが、あなたが '' parse_some_page() ''で '' result''を返すので、このシナリオが発生すると思います。利回りを取り除くとどうなりますか? –

+0

@ notorious.no私が 'yield'を省略すると、新しいリクエストをどうすればいいのか分かりません。 – Ci3

答えて

2

私は2つのソリューションを思いついた。 1)大規模なサイトが多すぎる場合は、別のプロセスでスパイダーを実行します。 2)Twisted で遅延とコールバックを使用してください(逃げないでください、恐ろしくないでしょう)。私は最初のものが単にグーグルであることができるので、第2の方法を使用する方法を議論するでしょう。

yield requestを実行するすべての関数は、結果が利用可能になるまで「ブロック」します。したがって、parse_some_page()関数は、応答レスポンスオブジェクトを生成し、応答が返されるまで次の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()を介して)チェーンされていることです。今scrapy.Requestためcallbackパラメータを見てみましょう:

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

このスニペットが何をするかは、scrapy.Responseはリクエストから利用可能になった直後にコールバックチェーンを開始しています。 Twistedでは、が値とともに実行された後にのみ、コールバックチェーンを実行し始めます。

応答が提供された後、parse_some_page()関数は、Responseを引数として実行されます。ここで必要なものを抽出し、次のコールバックに渡します(つまりwrite_to_disk()私の例)。必要に応じて、ループ内のDeferredにさらにコールバックを追加できます。

この回答と最初のやりとりの違いは、最初にすべての応答を待ち、次にコールバックを実行するためにyieldを使用したことです。私の方法では各リクエストのコールバックとしてDeferred.callback()を使用しているので、各応答は即座に処理されます。

これは(うまくいけば)役立ちます。

参照

PS

私が解析するには大きすぎるのサイトを見つけることができなかったので、これは実際にあなたのために働く場合、私は見当もつかない。また、私はScrapy:Dで新しいブランドスパンキンですが、私は自分のベルトの下に何年ものTwistedを持っています。

+0

このような素晴らしい返答をありがとう!私はそれを試し+いくつかのことを読んで、あなたに戻ってきます。 – Ci3

関連する問題