Собирать элементы из нескольких запросов в массиве в Scrapy - PullRequest
1 голос
/ 07 июня 2019

Я написал небольшой пример паука, чтобы проиллюстрировать мою проблему:

class ListEntrySpider(scrapy.Spider):
    start_urls = ['https://example.com/lists']

    def parse(self, response):
        for i in json.dumps(response.text)['ids']:
           scrapy.Request(f'https://example.com/list/{i}', callback=self.parse_lists)

    def parse_lists(self, response):
        for entry in json.dumps(response.text)['list']:
            yield ListEntryItem(**entry)

Мне нужно иметь все элементы, которые являются результатом нескольких запросов (все ListEntryItem s в массиве внутри паука, так что диспетчеризациязапросы, которые зависят от всех элементов.

Моя первая идея состояла в том, чтобы объединить запросы в цепочку и передать оставшиеся идентификаторы и уже извлеченные элементы в метаатрибут запроса, пока не будет достигнут последний запрос.

class ListEntrySpider(scrapy.Spider):
    start_urls = ['https://example.com/lists']

    def parse(self, response):
        ids = json.dumps(response.text)['ids']
        yield self._create_request(ids, [])

    def parse_lists(self, response):
        self._create_request(response.meta['ids'], response.meta['items'].extend(list(self._extract_lists(response))))

    def finish(self, response):
        items = response.meta['items'].extend(list(self._extract_lists(response)))

    def _extract_lists(self, response):
        for entry in json.dumps(response.text)['list']:
            yield ListEntryItem(**entry)

    def _create_request(self, ids: list, items: List[ListEntryItem]):
        i = ids.pop(0)
        return scrapy.Request(
            f'https://example.com/list/{i}',
            meta={'ids': ids, 'items': items},
            callback=self.parse_lists if len(ids) > 1 else self.finish
        )

Как видите, мое решение выглядит очень сложным. Я ищу что-то более читаемое и менее сложное.

1 Ответ

1 голос
/ 07 июня 2019

для этого есть разные подходы.Один цепляет, как вы.Возникают проблемы, если один из запросов в середине цепочки сбрасывается по любой причине.Вы должны быть очень осторожны с этим и обрабатывать все возможные ошибки / игнорируемые запросы.

Другой подход заключается в использовании отдельного паука для всех «сгруппированных» запросов.Вы можете запустить этих пауков программно и передать ведро (например, dict) как атрибут паука.В вашем конвейере вы добавляете свои предметы из каждого запроса в это ведро.Снаружи вы слушаете сигнал spider_closed и получаете это ведро, которое затем содержит все ваши предметы.

Узнайте, как программно запустить паука с помощью бегуна на гусеничном ходу: https://docs.scrapy.org/en/latest/topics/practices.html#running-multiple-spiders-in-the-same-process

передать ведро вашему пауку при вызове crawl () вашего бегуна на гусеничном ходу crawler_runner_object.crawl(YourSpider, bucket=dict())

и поймать сигнал sider_closed

from scrapy.signalmanager import dispatcher

def on_spider_closed(spider):
    bucket = spider.bucket

dispatcher.connect(on_spider_closed, signal=signals.spider_closed)

этот подход может показаться даже более сложным, чем цепочка ваших запросов, ноэто на самом деле снимает большую сложность с проблемы, так как внутри вашего паука вы можете делать свои запросы, не обращая особого внимания на все остальные запросы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...