我有item
对象,我需要将其传递到多个页面以将数据存储在单个项目中
喜欢我的项目
class DmozItem(Item):
title = Field()
description1 = Field()
description2 = Field()
description3 = Field()
现在这三个描述分别在三个页面中。我想做像
这样的事情现在这适用于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
答案 0 :(得分:30)
没问题。以下是您的代码的正确版本:
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
答案 1 :(得分:25)
为了保证请求/回调的排序,并且最终只返回一个项目,您需要使用以下形式链接您的请求:
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个项目,但如果您返回下一个请求,那么您可以保证请求的顺序,并且您在执行结束时将只有一个项目。
答案 2 :(得分:17)
接受的答案总共返回三项[desc(i)设置为i = 1,2,3]。
如果你想返回一个项目,Dave McLain的项目确实有效,但需要parseDescription1
,parseDescription2
和parseDescription3
才能成功运行且没有错误才能返回项目
对于我的用例,一些子请求可能会随机返回HTTP 403/404错误,因此我丢失了一些项目,即使我可以部分删除它们。
因此,我目前采用以下解决方法:不只是在request.meta
dict中传递项目,而是传递调用堆栈,它知道下一个要调用的请求。它将调用堆栈上的下一个项目(只要它不为空),并在堆栈为空时返回该项目。
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)
此解决方案仍然是同步的,如果回调中有任何异常,它仍然会失败。
答案 3 :(得分:1)
提供的所有答案各有其优缺点。我只是添加了一个额外的示例,以演示由于代码库的更改而简化了此操作(Python和Scrapy)。我们不再需要使用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})