Как я могу использовать несколько запросов и передавать элементы между ними в scrapy python
У меня есть объект item
, и мне нужно передать это на многих страницах для хранения данных в одном элементе
LIke мой элемент
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
Ответы
Ответ 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
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
Ответ 2
Чтобы гарантировать заказ запросов/обратных вызовов и что в конечном итоге возвращается только один элемент, вам нужно связать свои запросы, используя форму, например:
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 элемента в разных состояниях полноты в конвейере, но если вы вернете следующий запрос, то вы можете гарантировать порядок запросов и что вы будет иметь ровно один элемент в конце выполнения.
Ответ 3
Принятый ответ возвращает всего три элемента [с desc (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)
Предупреждение
Это решение по-прежнему синхронно и будет по-прежнему терпеть неудачу, если у вас есть исключения в обратных вызовах.
Для получения дополнительной информации проверьте сообщение в блоге, которое я написал об этом решении.