web-dev-qa-db-ja.com

複数のリクエストを使用して、それらの間でアイテムをスクレイピーで渡すにはどうすればよいですかpython

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
42
user1858027

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

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
30
warvariuc

リクエスト/コールバックの順序を保証し、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つのアイテム。

27
Dave McLain

受け入れられた回答は、合計3つのアイテムを返します[desc(i)をi = 1,2,3に設定]。

単一のアイテムを返す場合、Dave McLainのアイテムは機能しますが、アイテムを返すには、エラーなしで正常に実行され、parseDescription1parseDescription2、およびparseDescription3が必要です。

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


Workaround

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

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)

警告

このソリューションは依然として同期的であり、コールバック内に例外があると失敗します。

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

19
oliverguenther