如何用Scrapy删除数据库中过期项

3 投票
2 回答
2302 浏览
提问于 2025-04-15 17:56

我正在抓取一个视频网站,这个网站经常会过期内容。我考虑使用 scrapy 来进行抓取,但不太确定如何删除过期的项目。

检测一个项目是否过期的策略有:

  1. 抓取网站的 "delete.rss"。
  2. 每隔几天,尝试重新加载内容页面,确保它仍然可以正常工作。
  3. 抓取网站内容索引的每一页,如果找不到视频就删除它。

请告诉我如何在 scrapy 中删除过期的项目。我会通过 django 将我的 scrapy 项目存储在 mysql 数据库中。

2010-01-18 更新

我找到了一种有效的解决方案,但可能还不是最优的。我在每个我同步的视频上维护一个 "found_in_last_scan" 的标记。当爬虫开始时,它将所有标记设置为 False。当它完成时,删除那些仍然标记为 False 的视频。我是通过连接 signals.spider_openedsignals.spider_closed 来实现的。请确认这是一种有效的策略,并且没有问题。

2 个回答

0

如果你有一个HTTP网址,怀疑它可能已经失效了(比如你在一个“已删除”的信息源里找到了它,或者你很久没检查过这个网址),最简单、最快的方法就是发送一个HTTP HEAD 请求来检查这个网址。在Python中,最好的做法是使用标准库里的httplib模块:首先用HTTPConnection创建一个连接对象c,连接到你感兴趣的主机(如果使用HTTP 1.1,这样可以重复使用连接来检查多个网址,性能更好,系统负担更轻),然后调用crequest方法,第一次参数是'HEAD',第二个参数是你要检查的网址(当然不包括主机部分;-)。

每次调用request后,你需要调用c.getresponse()来获取一个HTTPResponse对象,这个对象的status属性会告诉你这个网址是否仍然有效。

是的,这个方法有点底层,但正因为如此,它能让你更好地优化你的任务,只需要对HTTP有一点了解就可以了;-).

4

我还没有测试这个!
我得承认,我还没有尝试在Scrapy中使用Django模型,但我来试试解释一下:

我想最简单的方法是为deleted.rss文件创建一个新的爬虫,方法是扩展XMLFeedSpider(这是从Scrapy文档中复制过来的,然后进行了修改)。我建议你创建一个新的爬虫,因为接下来的逻辑和抓取网站的逻辑关系不大:

from scrapy import log
from scrapy.contrib.spiders import XMLFeedSpider
from myproject.items import DeletedUrlItem

class MySpider(XMLFeedSpider):
    domain_name = 'example.com'
    start_urls = ['http://www.example.com/deleted.rss']
    iterator = 'iternodes' # This is actually unnecesary, since it's the default value
    itertag = 'item'

    def parse_node(self, response, url):
        url['url'] = node.select('#path/to/url').extract()

        return url # return an Item 

SPIDER = MySpider()

不是一个可以直接使用的爬虫,但如果我没记错的话,RSS文件是纯XML格式。我不太确定deleted.rss的具体样子,但我相信你能搞清楚如何从XML中提取URL。现在,这个例子中导入了myproject.items.DeletedUrlItem,在这个例子里它只是一个字符串,但你需要用下面的代码创建DeletedUrlItem:

你需要创建DeletedUrlItem:

class DeletedUrlItem(Item):
    url = Field()

而不是保存,你可以通过使用Django的模型API删除这些项目,在Scrapy的ItemPipeline中进行操作——我假设你正在使用DjangoItem

# we raise a DropItem exception so Scrapy
# doesn't try to process the item any further
from scrapy.core.exceptions import DropItem

# import your model
import django.Model.yourModel

class DeleteUrlPipeline(item):

    def process_item(self, spider, item):
        if item['url']:
            delete_item = yourModel.objects.get(url=item['url'])
            delete_item.delete() # actually delete the item!
            raise DropItem("Deleted: %s" % item)

注意delete_item.delete()这一行。


我知道这个回答可能有错误,因为我是凭记忆写的 :-) 但如果你有评论或者搞不清楚,我一定会更新的。

撰写回答