我写了一个小例子蜘蛛来说明我的问题:
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
)
如您所见,我的解决方案看起来非常复杂。我正在寻找更易读,更简单的东西。
答案 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)
这种方法似乎比链接您的请求还要复杂,但是实际上它消除了问题的复杂性,因为您可以在蜘蛛程序中发出请求,而不必考虑其他所有请求。