Как использовать несколько запросов и передавать данные между ними в Scrapy на Python?
Описание проблемы:
У меня есть объект item
, который я хочу передать через несколько страниц, чтобы сохранить данные в одном элементе.
Мой объект 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 = []
request1 = Request("http://www.example.com/lin1.cpp", callback=self.parseDescription1)
request1.meta['item'] = item
request2 = Request("http://www.example.com/lin2.cpp", callback=self.parseDescription2)
request2.meta['item'] = item
request3 = Request("http://www.example.com/lin3.cpp", callback=self.parseDescription3)
request3.meta['item'] = item
return request1, request2, request3
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
Проблема заключается в том, что, когда я пытаюсь отправить несколько запросов, объект item
не сохраняет данные должным образом, и в конце концов возвращается неполный/неправильный объект. Как мне правильно организовать код для передачи одного и того же объекта item
через несколько запросов, чтобы гарантировать, что все данные будут собраны корректно?
3 ответ(ов)
Нет проблем. Вот исправленная версия вашего кода:
def page_parser(self, response):
sites = hxs.select('//div[@class="row"]')
items = []
item = {} # Объявляем item до первого запроса
request = Request("http://www.example.com/lin1.cpp", callback=self.parseDescription1, 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
Обратите внимание на то, что я добавил объявление переменной item
в page_parser
перед первым запросом. В противном случае, когда вы используете 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()
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 элемента в различных состояниях завершенности. Однако, если вы вернете следующий запрос, вы сможете гарантировать порядок выполнения запросов и то, что в конце выполнения у вас будет ровно один элемент.
Все предложенные ответы имеют свои плюсы и минусы. Я просто добавлю еще один вариант, чтобы продемонстрировать, как это стало проще благодаря изменениям в кодовой базе (как в 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'] = "Больше данных из этого нового ответа"
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})
Как изменить порядок столбцов в DataFrame?
'pip' не распознан как командa внутреннего или внешнего формата
Почему statistics.mean() работает так медленно?
Преобразование строки даты JSON в datetime в Python
Есть ли разница между поднятием экземпляра класса Exception и самого класса Exception?