如何防止根据现有 JSON 列表在 Scrapy 获取时出现重复



In this Spider

import scrapy
class RedditSpider(scrapy.Spider):
name = 'Reddit'
allowed_domains = ['reddit.com']
start_urls = ['https://old.reddit.com']
def parse(self, response):
for link in response.css('li.first a.comments::attr(href)').extract():
yield scrapy.Request(url=response.urljoin(link), callback=self.parse_topics)

def parse_topics(self, response):
topics = {}
topics["title"] = response.css('a.title::text').extract_first()
topics["author"] = response.css('p.tagline a.author::text').extract_first()
if response.css('div.score.likes::attr(title)').extract_first() is not None:
topics["score"] = response.css('div.score.likes::attr(title)').extract_first()
else:
topics["score"] = "0"
if int(topics["score"]) > 10000:
author_url = response.css('p.tagline a.author::attr(href)').extract_first()
yield scrapy.Request(url=response.urljoin(author_url), callback=self.parse_user, meta={'topics': topics})
else:
yield topics
def parse_user(self, response):
topics = response.meta.get('topics')
users = {}
users["name"] = topics["author"]
users["karma"] = response.css('span.karma::text').extract_first()
yield users
yield topics

我得到这些结果:

[
{"name": "Username", "karma": "00000"},
{"title": "ExampleTitle1", "author": "Username", "score": "11000"},
{"name": "Username2", "karma": "00000"},
{"title": "ExampleTitle2", "author": "Username2", "score": "12000"},
{"name": "Username3", "karma": "00000"},
{"title": "ExampleTitle3", "author": "Username3", "score": "13000"},
{"title": "ExampleTitle4", "author": "Username4", "score": "9000"},
....
]

,但是我每天都运行这个蜘蛛来获得本周的最后一天,所以如果例如今天是一周的第 7 天,我会在今天得到 6 天之前的副本,就像这样

day1: result_day1
day2: result_day2, result_day1
day3: result_day3, result_day2, result_day1
. . . . . . .
day7: result_day7, result_day6, result_day5, result_day4, result_day3, result_day2, result_day1

所有数据都存储在一个JSON文件中,如前所示,我想做的是告诉蜘蛛检查获取的结果是否已经存在于JSON文件中,如果是,那么它会跳过它, 如果不是,则将其添加到文件中,

使用Scrapy可以吗?

例如:

如果昨天 (06.json( 结果是

[
{"name": "Username", "karma": "00000"},
{"title": "ExampleTitle1", "author": "Username", "score": "11000"},
{"name": "Username2", "karma": "00000"},
{"title": "ExampleTitle2", "author": "Username2", "score": "12000"},
{"name": "Username3", "karma": "00000"},
{"title": "ExampleTitle3", "author": "Username3", "score": "13000"},
{"title": "ExampleTitle4", "author": "Username4", "score": "9000"},
]

而今天(07.json(的结果是

[
{"name": "Username", "karma": "00000"},
{"title": "ExampleTitle1", "author": "Username", "score": "11000"},
{"name": "Username2", "karma": "00000"},
{"title": "ExampleTitle2", "author": "Username2", "score": "12000"},
{"name": "Username3", "karma": "00000"},
{"title": "ExampleTitle3", "author": "Username3", "score": "13000"},
{"title": "ExampleTitle4", "author": "Username4", "score": "9000"},
{"title": "ExampleTitle5", "author": "Username5", "score": "16700"}
]

今天名单(07.json(的结果是

[
{"title": "ExampleTitle5", "author": "Username5", "score": "16700"}
]

过滤后

Scrapy 实际上只提供了一种查找"重复项"的方法(用于数据,而不是发出的重复请求(:通过使用项目管道中的项目和使用重复过滤器来收集数据。看:

https://doc.scrapy.org/en/latest/topics/item-pipeline.html#duplicates-filter

当检测到重复项时,它会丢弃项目。我对这种方法有两个问题:(1(您必须编写重复过滤器方法来根据您使用的数据定义重复项,以及(2(此方法实际上只有助于检查蜘蛛的同一"运行"中的重复项。

在几天之间运行爬虫的另一种方法是在运行之间保留数据。看:

https://doc.scrapy.org/en/latest/topics/jobs.html#keeping-persistent-state-between-batches

使用此方法,spider.state将是上次运行(前一天(的数据。然后,当您再次运行蜘蛛时,您知道从上次运行中获得了哪些数据。因此,您可以实现逻辑来提取仅对当天唯一的数据(为每天的数据添加时间戳,并使用最后一天作为比较(。您可以快速实现这一点。而且,这可能足以解决您的问题。

但是,如果您必须比较当前日期之前所有天的数据,这种方法将变得不守规矩。这意味着您将使您的蜘蛛在当前蜘蛛之前的一周内保留所有天的数据。因此,您的spider.state字典(只是每天的 JSON 结果(会变得非常大,因为它充满了第 7 天之前所有天的数据。

如果您需要使当天添加的数据与之前的所有日子相比都是唯一的,我会完全放弃Scrapy的内置机制。我只是将所有数据写入数据库,并带有数据被抓取时的时间戳。然后,您可以使用数据库查询来找出每天添加的唯一数据。

最新更新