如何根据现有的JSON列表防止在Scrapy抓取中出现重复项

时间:2018-07-07 18:10:22

标签: python json filter scrapy duplicates

在这只蜘蛛

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"},
  ....
]

,但是我每天都会运行这只Spider来获得本周的最后一天,因此,例如,如果今天是一周的第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 文件中,如前所示,我要做的是告诉Spider检查 JSON file,如果是,则跳过它,如果不是,则将其添加到文件中,

使用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"}
]

过滤后

1 个答案:

答案 0 :(得分:1)

Scrapy实际上仅提供一种寻找“重复项”的方式(对于数据,不是重复的请求):使用项目管道中的项目并使用重复过滤器来收集数据。参见:

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

当检测到重复项时,它会丢弃项目。这种方法有两个问题:(1)您必须编写重复过滤器方法来根据您使用的数据定义重复的 ,以及(2)此方法仅对在蜘蛛的相同“运行”中检查重复项。

另一种在几天之间运行Spider的方法是在两次运行之间保留数据。参见:

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

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

但是,如果您必须在当天之前的所有天中比较数据,则这种方法将变得不灵活。这意味着您将使Spider在当前数据之前一周的所有天中都保留数据。因此,例如,spider.state字典(这只是每天的JSON结果)将变得非常大,因为它充满了第7天之前所有天的数据。

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