从Scrapy中的数组中的多个请求中收集项目

时间:2019-06-06 23:08:28

标签: scrapy

我写了一个小例子蜘蛛来说明我的问题:

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位于蜘蛛网中的数组中,因此,派发依赖于所有项目的请求。

我的第一个想法是链接请求,并在请求的meta属性中传递剩余的ID和已经提取的项目,直到到达最后一个请求为止。

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 个答案:

答案 0 :(得分:1)

有不同的方法。一种是像您一样进行链接。发生问题是由于任何原因导致链中间的请求之一被丢弃。您必须对此非常小心,并处理所有可能的错误/被忽略的请求。

另一种方法是对所有“分组”请求使用单独的蜘蛛。 您可以通过编程方式启动这些蜘蛛,并将存储桶(例如dict)作为蜘蛛属性传递。在管道中,您将每个请求中的项目添加到此存储桶。从“外部”开始,您会听到spider_closed信号,并获得包含所有项目的存储桶。

查看此处,了解如何通过爬虫程序以编程方式启动Spider: 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)

这种方法似乎比链接您的请求还要复杂,但是实际上它消除了问题的复杂性,因为您可以在蜘蛛程序中发出请求,而不必考虑其他所有请求。