¿Cómo puedo usar múltiples solicitudes y pasar elementos entre ellos en scrapy python?

Tengo el objeto de item y necesito pasarlo a lo largo de muchas páginas para almacenar datos en un solo elemento.

Como mi artículo es

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

Ahora esas tres descripciones están en tres páginas separadas. quiero hacer algo como

Ahora esto funciona bien para 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 

Pero quiero algo como

 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 

No hay problema. En lugar de

 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 

Hacer

 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 

Para garantizar un pedido de las solicitudes / devoluciones de llamada y que al final solo se devuelve un artículo, debe encadenar sus solicitudes mediante un formulario como:

  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] 

Cada función de callback devuelve un iterable de elementos o solicitudes, las solicitudes se progtwign y los elementos se ejecutan a través de su canalización de elementos.

Si devuelve un artículo de cada una de las devoluciones de llamada, terminará con 4 artículos en varios estados de integridad en su canalización, pero si devuelve la próxima solicitud, entonces puede garantizar el orden de las solicitudes y que tendrá exactamente Un elemento al final de la ejecución.

La respuesta aceptada devuelve un total de tres elementos [con desc (i) establecido para i = 1,2,3].

Si desea devolver un solo elemento, el elemento de Dave McLain funciona, sin embargo, requiere parseDescription1 , parseDescription2 y parseDescription3 para tener éxito y ejecutarse sin errores para devolver el elemento.

Para mi caso de uso, algunas de las solicitudes secundarias PUEDEN devolver errores HTTP 403/404 al azar, por lo que perdí algunos de los elementos, aunque podría haberlos eliminado parcialmente.


Solución

Por lo tanto, actualmente empleé la siguiente solución: en lugar de solo pasar el elemento en el request.meta la request.meta , pase una stack de llamadas que sepa a qué solicitud llamar después. Llamará al siguiente elemento de la stack (siempre que no esté vacío), y devuelve el elemento si la stack está vacía.

El parámetro de solicitud de errback se usa para regresar al método del despachador en caso de errores y simplemente continuar con el siguiente elemento de la stack.

 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) 

Advertencia

Esta solución sigue siendo sincrónica y seguirá fallando si tiene alguna excepción dentro de las devoluciones de llamada.

Para obtener más información, consulte la publicación del blog que escribí sobre esa solución .