54

私はitemオブジェクトを持っており、単一のアイテムにデータを保存するためにそれを多くのページに渡す必要があります

私のアイテムが好きです

class DmozItem(Item):
    title = Field()
    description1 = Field()
    description2 = Field()
    description3 = Field()

現在、これらの3つの説明は3つの別々のページにあります。私は何かのようなことをしたい

今、これはのためにうまくいきますparseDescription1

def page_parser(self, response):
    sites = hxs.select('//div[@class="row"]')
    items = []
    request =  Request("http://www.example.com/lin1.cpp",  callback =self.parseDescription1)
    request.meta['item'] = item
    return request 

def parseDescription1(self,response):
    item = response.meta['item']
    item['desc1'] = "test"
    return item

しかし、私はのようなものが欲しい

def page_parser(self, response):
    sites = hxs.select('//div[@class="row"]')
    items = []
    request =  Request("http://www.example.com/lin1.cpp",  callback =self.parseDescription1)
    request.meta['item'] = item

    request =  Request("http://www.example.com/lin1.cpp",  callback =self.parseDescription2)
    request.meta['item'] = item

    request =  Request("http://www.example.com/lin1.cpp",  callback =self.parseDescription2)
    request.meta['item'] = item

    return request 

def parseDescription1(self,response):
    item = response.meta['item']
    item['desc1'] = "test"
    return item

def parseDescription2(self,response):
    item = response.meta['item']
    item['desc2'] = "test2"
    return item

def parseDescription3(self,response):
    item = response.meta['item']
    item['desc3'] = "test3"
    return item
4

4 に答える 4

38

問題ない。コードの正しいバージョンは次のとおりです。

def page_parser(self, response):
      sites = hxs.select('//div[@class="row"]')
      items = []

      request = Request("http://www.example.com/lin1.cpp", callback=self.parseDescription1)
      request.meta['item'] = item
      yield request

      request = Request("http://www.example.com/lin1.cpp", callback=self.parseDescription2, meta={'item': item})
      yield request

      yield Request("http://www.example.com/lin1.cpp", callback=self.parseDescription3, meta={'item': item})

def parseDescription1(self,response):
            item = response.meta['item']
            item['desc1'] = "test"
            return item

def parseDescription2(self,response):
            item = response.meta['item']
            item['desc2'] = "test2"
            return item

def parseDescription3(self,response):
            item = response.meta['item']
            item['desc3'] = "test3"
            return item
于 2012-12-17T09:51:04.160 に答える
31

リクエスト/コールバックの順序を保証し、最終的に1つのアイテムのみが返されるようにするには、次のようなフォームを使用してリクエストをチェーンする必要があります。

  def page_parser(self, response):
        sites = hxs.select('//div[@class="row"]')
        items = []

        request = Request("http://www.example.com/lin1.cpp", callback=self.parseDescription1)
        request.meta['item'] = Item()
        return [request]


  def parseDescription1(self,response):
        item = response.meta['item']
        item['desc1'] = "test"
        return [Request("http://www.example.com/lin2.cpp", callback=self.parseDescription2, meta={'item': item})]


  def parseDescription2(self,response):
        item = response.meta['item']
        item['desc2'] = "test2"
        return [Request("http://www.example.com/lin3.cpp", callback=self.parseDescription3, meta={'item': item})]

  def parseDescription3(self,response):
        item = response.meta['item']
        item['desc3'] = "test3"
        return [item]

各コールバック関数は、反復可能なアイテムまたはリクエストを返し、リクエストはスケジュールされ、アイテムはアイテムパイプラインを介して実行されます。

各コールバックからアイテムを返すと、パイプラインでさまざまな完全性の状態にある4つのアイテムになりますが、次のリクエストを返すと、リクエストの順序を保証でき、正確に実行終了時に1つのアイテム。

于 2013-04-23T19:22:44.913 に答える
23

受け入れられた回答は、合計3つの項目を返します[desc(i)がi=1,2,3に設定されている]。

単一のアイテムを返品する場合、Dave McLainのアイテムは機能しますが、アイテムを返品するには、、、が成功し、エラーなしで実行される必要がparseDescription1ありparseDescription2ますparseDescription3

私のユースケースでは、一部のサブリクエストがランダムにHTTP 403/404エラーを返す可能性があるため、部分的にスクレイプできたとしても、一部のアイテムを失いました。


回避策

したがって、私は現在、次の回避策を採用しています。dictでアイテムを渡すだけでなく、次に呼び出す要求を認識しているコールスタックrequest.metaを渡します。スタック上の次のアイテムを呼び出し(空でない限り)、スタックが空の場合はアイテムを返します。

requestパラメータは、errbackエラーが発生したときにディスパッチャメソッドに戻り、次のスタックアイテムに進むために使用されます。

def callnext(self, response):
    ''' Call next target for the item loader, or yields it if completed. '''

    # Get the meta object from the request, as the response
    # does not contain it.
    meta = response.request.meta

    # Items remaining in the stack? Execute them
    if len(meta['callstack']) > 0:
        target = meta['callstack'].pop(0)
        yield Request(target['url'], meta=meta, callback=target['callback'], errback=self.callnext)
    else:
        yield meta['loader'].load_item()

def parseDescription1(self, response):

    # Recover item(loader)
    l = response.meta['loader']

    # Use just as before
    l.add_css(...)

    # Build the call stack
    callstack = [
        {'url': "http://www.example.com/lin2.cpp",
        'callback': self.parseDescription2 },
        {'url': "http://www.example.com/lin3.cpp",
        'callback': self.parseDescription3 }
    ]

    return self.callnext(response)

def parseDescription2(self, response):

    # Recover item(loader)
    l = response.meta['loader']

    # Use just as before
    l.add_css(...)

    return self.callnext(response)


def parseDescription3(self, response):

    # ...

    return self.callnext(response)

警告

このソリューションはまだ同期中であり、コールバック内に例外がある場合でも失敗します。

詳細については、そのソリューションについて書いたブログ投稿を確認してください

于 2014-08-29T15:16:55.527 に答える
3

提供されるすべての回答には、長所と短所があります。コードベース(PythonとScrapyの両方)の変更により、これがどのように単純化されたかを示すために、1つ追加しています。使用する必要がなくなり、meta代わりに使用できますcb_kwargs(つまり、コールバック関数に渡すキーワード引数)。

したがって、これを行う代わりに:

def page_parser(self, response):
    sites = hxs.select('//div[@class="row"]')
    items = []

    request = Request("http://www.example.com/lin1.cpp",
                      callback=self.parseDescription1)
    request.meta['item'] = Item()
    return [request]


def parseDescription1(self,response):
    item = response.meta['item']
    item['desc1'] = "test"
    return [Request("http://www.example.com/lin2.cpp",
                    callback=self.parseDescription2, meta={'item': item})]
...

できるよ:

def page_parser(self, response):
    sites = hxs.select('//div[@class="row"]')
    items = []

    yield response.follow("http://www.example.com/lin1.cpp",
                          callback=self.parseDescription1,
                          cb_kwargs={"item": item()})


def parseDescription1(self,response, item):
    item['desc1'] = "More data from this new response"
    yield response.follow("http://www.example.com/lin2.cpp",
                          callback=self.parseDescription2,
                          cb_kwargs={'item': item})
...

何らかの理由で同じ機能で処理したいリンクが複数ある場合は、交換できます

yield response.follow(a_single_url,
                      callback=some_function,
                      cb_kwargs={"data": to_pass_to_callback})

yield from response.follow_all([many, urls, to, parse],
                               callback=some_function,
                               cb_kwargs={"data": to_pass_to_callback})
于 2020-05-14T09:57:27.170 に答える